Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
28e9c8f
[world-vercel] Add /run-id sub-export with tagged ULID encode/decode
TooTallNate May 13, 2026
76e0024
[world-vercel] Add string-value assertions to run-id tests
TooTallNate May 13, 2026
cc1e0b8
[world-vercel] Remove internal-repo reference from regions doc comment
TooTallNate May 13, 2026
9147205
[world-vercel] Address PR review feedback on run-id sub-export
TooTallNate May 13, 2026
6efe0df
[world] [core] [world-vercel] Add World.createRunId() and region-awar…
TooTallNate May 13, 2026
d072229
[core] [world] [world-vercel] Pass full StartOptions to World.createR…
TooTallNate May 13, 2026
ef2993d
Merge remote-tracking branch 'origin/main' into world-create-run-id
VaguelySerious Jun 11, 2026
68feaa7
Address review feedback: doc fixes and deterministic same-ms tests
VaguelySerious Jun 11, 2026
162993f
Merge remote-tracking branch 'origin/main' into world-create-run-id
TooTallNate Jun 15, 2026
07d4718
test: keep workflow-server override rewrite-compatible
TooTallNate Jun 15, 2026
4f87069
fix(world): clear region tag bit before ULID timestamp validation
TooTallNate Jun 15, 2026
24282d8
fix(world-vercel): validate tagged runId timestamps via run-id decode
TooTallNate Jun 16, 2026
c8d65dc
fix(world-vercel): default run ID region to iad1 instead of unknown
TooTallNate Jun 16, 2026
bed8f7c
Merge remote-tracking branch 'origin/main' into world-create-run-id
TooTallNate Jun 17, 2026
f2b905d
test(e2e): use verbose reporter + per-test start heartbeat
TooTallNate Jun 17, 2026
8cc53c7
test(world-vercel): point WORKFLOW_SERVER_URL_OVERRIDE at combined 52…
TooTallNate Jun 17, 2026
05c98dc
Merge remote-tracking branch 'origin/main' into world-create-run-id
TooTallNate Jun 17, 2026
f29ee31
fix(core): region-tag the health-check correlationId
TooTallNate Jun 17, 2026
d84f83f
Merge remote-tracking branch 'origin/main' into world-create-run-id
VaguelySerious Jul 1, 2026
17197cd
Address review feedback: validate region overrides, reset server over…
VaguelySerious Jul 1, 2026
fc3e1f9
test(world-vercel): point WORKFLOW_SERVER_URL_OVERRIDE at wave-1 mult…
TooTallNate Jul 6, 2026
4480b37
Merge origin/main into world-create-run-id
TooTallNate Jul 6, 2026
ca0a65b
test(e2e): Vercel multi-region suite for start()'s region option
TooTallNate Jul 6, 2026
31e01af
test(e2e): start region probes in-function; fix getWorld await
TooTallNate Jul 6, 2026
b149741
Merge origin/main into world-create-run-id
TooTallNate Jul 6, 2026
450f4e9
test(e2e): cover explicit and implicit region starts in the multi-reg…
TooTallNate Jul 6, 2026
d933d96
Revert WORKFLOW_SERVER_URL_OVERRIDE to '' — wave-1 multi-region servi…
TooTallNate Jul 6, 2026
3d7c34b
test(e2e): cross-region stream visibility (iad1 writer, sfo1 reader)
TooTallNate Jul 7, 2026
3625089
test(e2e): extend multi-region suite to all 19 provisioned regions
TooTallNate Jul 8, 2026
5a936f1
test(e2e): tolerate geo-adjacent execution of queue callbacks
TooTallNate Jul 8, 2026
dff3f80
Revert WORKFLOW_SERVER_URL_OVERRIDE to '' — all-regions serving is in…
TooTallNate Jul 8, 2026
586c6e3
Merge origin/main into world-create-run-id
TooTallNate Jul 8, 2026
c42618f
docs: fix three stale comments flagged in review
TooTallNate Jul 8, 2026
fce2a8e
test(e2e): cover hook resolve/resume for runs owned by non-iad1 regions
TooTallNate Jul 9, 2026
4f77bc1
Merge origin/main into world-create-run-id
TooTallNate Jul 13, 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
6 changes: 6 additions & 0 deletions .changeset/core-run-id-input.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
---
"@workflow/core": minor
"workflow": minor
---

`start()` now delegates run ID generation to `world.createRunId(options)` when the world provides it, falling back to a monotonic ULID otherwise. The full options bag is passed through so worlds can read whichever fields they recognise. Adds a new `region` option that worlds may consume — when set, it is also forwarded onto the queue options so the initial workflow message is routed to the matching region.
5 changes: 5 additions & 0 deletions .changeset/world-create-run-id.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
---
"@workflow/world": minor
---

Add optional `createRunId(options?)` to the `World` interface and `region` to `QueueOptions`. Worlds can now mint custom run IDs (reading whichever start-option fields they recognise) and route messages to a specific region.
5 changes: 5 additions & 0 deletions .changeset/world-vercel-create-run-id.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
---
"@workflow/world-vercel": minor
---

Implement `World.createRunId` to mint region-tagged ULIDs, preferring an explicit `options.region` from `start()`, then the `VERCEL_REGION` environment variable, and finally defaulting to `iad1` so run IDs are always tagged with a concrete, routable region. The queue now routes each message to the region encoded in the payload's tagged run ID (or to an explicit `opts.region` override), instead of the previous hard-coded `iad1`.
97 changes: 92 additions & 5 deletions .github/workflows/tests.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -381,7 +381,7 @@ jobs:
run: echo "ms=$(($(date +%s) * 1000))" >> "$GITHUB_OUTPUT"

- name: Run E2E Tests
run: pnpm run test:e2e --reporter=default --reporter=json --reporter=./packages/core/e2e/github-reporter.ts "--outputFile=e2e-vercel-prod-$APP_NAME.json"
run: pnpm run test:e2e --reporter=verbose --reporter=json --reporter=./packages/core/e2e/github-reporter.ts "--outputFile=e2e-vercel-prod-$APP_NAME.json"
env:
NODE_OPTIONS: "--enable-source-maps"
DEPLOYMENT_URL: ${{ steps.waitForDeployment.outputs.deployment-url }}
Expand DownExpand Up@@ -436,6 +436,93 @@ jobs:
retention-days: 7
if-no-files-found: ignore

e2e-vercel-multi-region:
# Vercel-specific multi-region routing suite (start()'s `region`
# option). Deliberately separate from e2e-vercel-prod: that job runs
# the shared suite as a matrix across all worlds/frameworks, while
# this functionality only exists for @workflow/world-vercel and only
# the nextjs-turbopack workbench is deployed multi-region
# (workbench/nextjs-turbopack/vercel.json pins iad1+sfo1+fra1).
name: E2E Vercel Multi-Region Tests (nextjs-turbopack)
runs-on: ubuntu-latest
# 19 regions: the implicit-route cases pay one cold start each,
# sequentially.
timeout-minutes: 35
needs: ci-scope
if: ${{ needs.ci-scope.outputs.fast-path != 'true' }}
permissions:
id-token: write
contents: read
deployments: read
statuses: read
env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
WORKFLOW_PUBLIC_MANIFEST: '1'
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: Waiting for the Vercel deployment
id: waitForDeployment
uses: vercel/wait-for-deployment-action@0e2b0c5c5cce31f1648108aeec56467187aca037
with:
project-slug: "example-nextjs-workflow-turbopack"
timeout: 1000
check-interval: 15
environment: ${{ github.ref == 'refs/heads/main' && 'production' || 'preview' }}

- name: Run Multi-Region E2E Tests
run: pnpm vitest run packages/core/e2e/e2e-region.test.ts --reporter=verbose --reporter=json --reporter=./packages/core/e2e/github-reporter.ts "--outputFile=e2e-vercel-multi-region-$APP_NAME.json"
env:
NODE_OPTIONS: "--enable-source-maps"
DEPLOYMENT_URL: ${{ steps.waitForDeployment.outputs.deployment-url }}
VERCEL_DEPLOYMENT_ID: ${{ steps.waitForDeployment.outputs.deployment-id }}
APP_NAME: "nextjs-turbopack"
WORKFLOW_VERCEL_ENV: ${{ github.ref == 'refs/heads/main' && 'production' || 'preview' }}
WORKFLOW_VERCEL_AUTH_TOKEN: ${{ secrets.VERCEL_LABS_TOKEN }}
WORKFLOW_VERCEL_TEAM: "team_nO2mCG4W8IxPIeKoSsqwAxxB"
WORKFLOW_VERCEL_PROJECT: "prj_yjkM7UdHliv8bfxZ1sMJQf1pMpdi"
WORKFLOW_VERCEL_PROJECT_SLUG: "example-nextjs-workflow-turbopack"
# See the note on e2e-vercel-prod: PRs point at the protected
# workflow-server preview; unset on main for production.
VERCEL_WORKFLOW_SERVER_URL: ${{ github.ref != 'refs/heads/main' && secrets.VERCEL_WORKFLOW_SERVER_URL || '' }}

- name: Capture runtime logs on failure
if: failure()
env:
APP_NAME: "nextjs-turbopack"
WORKFLOW_VERCEL_AUTH_TOKEN: ${{ secrets.VERCEL_LABS_TOKEN }}
WORKFLOW_VERCEL_TEAM: "team_nO2mCG4W8IxPIeKoSsqwAxxB"
WORKFLOW_VERCEL_PROJECT: "prj_yjkM7UdHliv8bfxZ1sMJQf1pMpdi"
WORKFLOW_VERCEL_ENV: ${{ github.ref == 'refs/heads/main' && 'production' || 'preview' }}
VERCEL_DEPLOYMENT_ID: ${{ steps.waitForDeployment.outputs.deployment-id }}
run: node .github/scripts/fetch-e2e-runtime-logs.mjs

- name: Upload E2E results
if: always()
uses: actions/upload-artifact@v4
with:
name: e2e-results-vercel-multi-region-nextjs-turbopack
path: |
e2e-vercel-multi-region-nextjs-turbopack.json
e2e-metadata-nextjs-turbopack-vercel.json
e2e-failures-nextjs-turbopack-vercel.json
e2e-diagnostics-nextjs-turbopack-vercel.json
e2e-runtime-logs-nextjs-turbopack-vercel.json
retention-days: 7
if-no-files-found: ignore

getTestMatrix:
name: Get Test Matrix
runs-on: ubuntu-latest
Expand DownExpand Up@@ -525,7 +612,7 @@ jobs:
(cd "$WORKBENCH_APP_PATH" && pnpm dev 2>&1 | tee "$DEV_SERVER_LOG_PATH") &
echo "starting tests in 10 seconds" && sleep 10
pnpm vitest run packages/core/e2e/dev.test.ts; sleep 10
pnpm run test:e2e --reporter=default --reporter=json --reporter=./packages/core/e2e/github-reporter.ts --outputFile=e2e-local-dev-${{ matrix.app.name }}-${{ matrix.app.artifactSuffix }}.json
pnpm run test:e2e --reporter=verbose --reporter=json --reporter=./packages/core/e2e/github-reporter.ts --outputFile=e2e-local-dev-${{ matrix.app.name }}-${{ matrix.app.artifactSuffix }}.json
env:
NODE_OPTIONS: "--enable-source-maps"
APP_NAME: ${{ matrix.app.name }}
Expand DownExpand Up@@ -615,7 +702,7 @@ jobs:
export PROD_SERVER_LOG_PATH="$GITHUB_WORKSPACE/prod-server-${{ matrix.app.name }}-${{ matrix.app.artifactSuffix }}.log"
(cd "$WORKBENCH_APP_PATH" && pnpm start 2>&1 | tee "$PROD_SERVER_LOG_PATH") &
echo "starting tests in 10 seconds" && sleep 10
pnpm run test:e2e --reporter=default --reporter=json --reporter=./packages/core/e2e/github-reporter.ts --outputFile=e2e-local-prod-${{ matrix.app.name }}-${{ matrix.app.artifactSuffix }}.json
pnpm run test:e2e --reporter=verbose --reporter=json --reporter=./packages/core/e2e/github-reporter.ts --outputFile=e2e-local-prod-${{ matrix.app.name }}-${{ matrix.app.artifactSuffix }}.json
env:
NODE_OPTIONS: "--enable-source-maps"
APP_NAME: ${{ matrix.app.name }}
Expand DownExpand Up@@ -723,7 +810,7 @@ jobs:
export PROD_SERVER_LOG_PATH="$GITHUB_WORKSPACE/prod-server-postgres-${{ matrix.app.name }}-${{ matrix.app.artifactSuffix }}.log"
(cd "$WORKBENCH_APP_PATH" && pnpm start 2>&1 | tee "$PROD_SERVER_LOG_PATH") &
echo "starting tests in 10 seconds" && sleep 10
pnpm run test:e2e --reporter=default --reporter=json --reporter=./packages/core/e2e/github-reporter.ts --outputFile=e2e-local-postgres-${{ matrix.app.name }}-${{ matrix.app.artifactSuffix }}.json
pnpm run test:e2e --reporter=verbose --reporter=json --reporter=./packages/core/e2e/github-reporter.ts --outputFile=e2e-local-postgres-${{ matrix.app.name }}-${{ matrix.app.artifactSuffix }}.json
env:
NODE_OPTIONS: "--enable-source-maps"
APP_NAME: ${{ matrix.app.name }}
Expand DownExpand Up@@ -841,7 +928,7 @@ jobs:
exit 1
}

pnpm run test:e2e --reporter=default --reporter=json --reporter=./packages/core/e2e/github-reporter.ts --outputFile=e2e-windows-nextjs-turbopack.json
pnpm run test:e2e --reporter=verbose --reporter=json --reporter=./packages/core/e2e/github-reporter.ts --outputFile=e2e-windows-nextjs-turbopack.json
$e2eExit = $LASTEXITCODE
Stop-Job $job -ErrorAction SilentlyContinue
exit $e2eExit
Expand Down
8 changes: 8 additions & 0 deletions e2e-diagnostics-nextjs-turbopack-vercel.json
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
[
{
"testName": "WorkflowNotRegisteredError fails the run when workflow does not exist",
"runId": "wrun_41KV8YMWKC00RAQCRH320GSWR4",
"timestamp": "2026-06-16T19:31:07.845Z",
"dashboardUrl": "https://vercel.com/vercel-labs/example-nextjs-workflow-turbopack/observability/workflows/runs/wrun_41KV8YMWKC00RAQCRH320GSWR4?environment=preview"
}
]
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
28e9c8f
[world-vercel] Add /run-id sub-export with tagged ULID encode/decode
TooTallNate May 13, 2026
76e0024
[world-vercel] Add string-value assertions to run-id tests
TooTallNate May 13, 2026
cc1e0b8
[world-vercel] Remove internal-repo reference from regions doc comment
TooTallNate May 13, 2026
9147205
[world-vercel] Address PR review feedback on run-id sub-export
TooTallNate May 13, 2026
6efe0df
[world] [core] [world-vercel] Add World.createRunId() and region-awar…
TooTallNate May 13, 2026
d072229
[core] [world] [world-vercel] Pass full StartOptions to World.createR…
TooTallNate May 13, 2026
ef2993d
Merge remote-tracking branch 'origin/main' into world-create-run-id
VaguelySerious Jun 11, 2026
68feaa7
Address review feedback: doc fixes and deterministic same-ms tests
VaguelySerious Jun 11, 2026
162993f
Merge remote-tracking branch 'origin/main' into world-create-run-id
TooTallNate Jun 15, 2026
07d4718
test: keep workflow-server override rewrite-compatible
TooTallNate Jun 15, 2026
4f87069
fix(world): clear region tag bit before ULID timestamp validation
TooTallNate Jun 15, 2026
24282d8
fix(world-vercel): validate tagged runId timestamps via run-id decode
TooTallNate Jun 16, 2026
c8d65dc
fix(world-vercel): default run ID region to iad1 instead of unknown
TooTallNate Jun 16, 2026
bed8f7c
Merge remote-tracking branch 'origin/main' into world-create-run-id
TooTallNate Jun 17, 2026
f2b905d
test(e2e): use verbose reporter + per-test start heartbeat
TooTallNate Jun 17, 2026
8cc53c7
test(world-vercel): point WORKFLOW_SERVER_URL_OVERRIDE at combined 52…
TooTallNate Jun 17, 2026
05c98dc
Merge remote-tracking branch 'origin/main' into world-create-run-id
TooTallNate Jun 17, 2026
f29ee31
fix(core): region-tag the health-check correlationId
TooTallNate Jun 17, 2026
d84f83f
Merge remote-tracking branch 'origin/main' into world-create-run-id
VaguelySerious Jul 1, 2026
17197cd
Address review feedback: validate region overrides, reset server over…
VaguelySerious Jul 1, 2026
fc3e1f9
test(world-vercel): point WORKFLOW_SERVER_URL_OVERRIDE at wave-1 mult…
TooTallNate Jul 6, 2026
4480b37
Merge origin/main into world-create-run-id
TooTallNate Jul 6, 2026
ca0a65b
test(e2e): Vercel multi-region suite for start()'s region option
TooTallNate Jul 6, 2026
31e01af
test(e2e): start region probes in-function; fix getWorld await
TooTallNate Jul 6, 2026
b149741
Merge origin/main into world-create-run-id
TooTallNate Jul 6, 2026
450f4e9
test(e2e): cover explicit and implicit region starts in the multi-reg…
TooTallNate Jul 6, 2026
d933d96
Revert WORKFLOW_SERVER_URL_OVERRIDE to '' — wave-1 multi-region servi…
TooTallNate Jul 6, 2026
3d7c34b
test(e2e): cross-region stream visibility (iad1 writer, sfo1 reader)
TooTallNate Jul 7, 2026
3625089
test(e2e): extend multi-region suite to all 19 provisioned regions
TooTallNate Jul 8, 2026
5a936f1
test(e2e): tolerate geo-adjacent execution of queue callbacks
TooTallNate Jul 8, 2026
dff3f80
Revert WORKFLOW_SERVER_URL_OVERRIDE to '' — all-regions serving is in…
TooTallNate Jul 8, 2026
586c6e3
Merge origin/main into world-create-run-id
TooTallNate Jul 8, 2026
c42618f
docs: fix three stale comments flagged in review
TooTallNate Jul 8, 2026
fce2a8e
test(e2e): cover hook resolve/resume for runs owned by non-iad1 regions
TooTallNate Jul 9, 2026
4f77bc1
Merge origin/main into world-create-run-id
TooTallNate Jul 13, 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
6 changes: 6 additions & 0 deletions .changeset/core-run-id-input.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
---
"@workflow/core": minor
"workflow": minor
---

`start()` now delegates run ID generation to `world.createRunId(options)` when the world provides it, falling back to a monotonic ULID otherwise. The full options bag is passed through so worlds can read whichever fields they recognise. Adds a new `region` option that worlds may consume — when set, it is also forwarded onto the queue options so the initial workflow message is routed to the matching region.
5 changes: 5 additions & 0 deletions .changeset/world-create-run-id.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
---
"@workflow/world": minor
---

Add optional `createRunId(options?)` to the `World` interface and `region` to `QueueOptions`. Worlds can now mint custom run IDs (reading whichever start-option fields they recognise) and route messages to a specific region.
5 changes: 5 additions & 0 deletions .changeset/world-vercel-create-run-id.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
---
"@workflow/world-vercel": minor
---

Implement `World.createRunId` to mint region-tagged ULIDs, preferring an explicit `options.region` from `start()`, then the `VERCEL_REGION` environment variable, and finally defaulting to `iad1` so run IDs are always tagged with a concrete, routable region. The queue now routes each message to the region encoded in the payload's tagged run ID (or to an explicit `opts.region` override), instead of the previous hard-coded `iad1`.
97 changes: 92 additions & 5 deletions .github/workflows/tests.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -381,7 +381,7 @@ jobs:
run: echo "ms=$(($(date +%s) * 1000))" >> "$GITHUB_OUTPUT"

- name: Run E2E Tests
run: pnpm run test:e2e --reporter=default --reporter=json --reporter=./packages/core/e2e/github-reporter.ts "--outputFile=e2e-vercel-prod-$APP_NAME.json"
run: pnpm run test:e2e --reporter=verbose --reporter=json --reporter=./packages/core/e2e/github-reporter.ts "--outputFile=e2e-vercel-prod-$APP_NAME.json"
env:
NODE_OPTIONS: "--enable-source-maps"
DEPLOYMENT_URL: ${{ steps.waitForDeployment.outputs.deployment-url }}
Expand DownExpand Up@@ -436,6 +436,93 @@ jobs:
retention-days: 7
if-no-files-found: ignore

e2e-vercel-multi-region:
# Vercel-specific multi-region routing suite (start()'s `region`
# option). Deliberately separate from e2e-vercel-prod: that job runs
# the shared suite as a matrix across all worlds/frameworks, while
# this functionality only exists for @workflow/world-vercel and only
# the nextjs-turbopack workbench is deployed multi-region
# (workbench/nextjs-turbopack/vercel.json pins iad1+sfo1+fra1).
name: E2E Vercel Multi-Region Tests (nextjs-turbopack)
runs-on: ubuntu-latest
# 19 regions: the implicit-route cases pay one cold start each,
# sequentially.
timeout-minutes: 35
needs: ci-scope
if: ${{ needs.ci-scope.outputs.fast-path != 'true' }}
permissions:
id-token: write
contents: read
deployments: read
statuses: read
env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
WORKFLOW_PUBLIC_MANIFEST: '1'
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: Waiting for the Vercel deployment
id: waitForDeployment
uses: vercel/wait-for-deployment-action@0e2b0c5c5cce31f1648108aeec56467187aca037
with:
project-slug: "example-nextjs-workflow-turbopack"
timeout: 1000
check-interval: 15
environment: ${{ github.ref == 'refs/heads/main' && 'production' || 'preview' }}

- name: Run Multi-Region E2E Tests
run: pnpm vitest run packages/core/e2e/e2e-region.test.ts --reporter=verbose --reporter=json --reporter=./packages/core/e2e/github-reporter.ts "--outputFile=e2e-vercel-multi-region-$APP_NAME.json"
env:
NODE_OPTIONS: "--enable-source-maps"
DEPLOYMENT_URL: ${{ steps.waitForDeployment.outputs.deployment-url }}
VERCEL_DEPLOYMENT_ID: ${{ steps.waitForDeployment.outputs.deployment-id }}
APP_NAME: "nextjs-turbopack"
WORKFLOW_VERCEL_ENV: ${{ github.ref == 'refs/heads/main' && 'production' || 'preview' }}
WORKFLOW_VERCEL_AUTH_TOKEN: ${{ secrets.VERCEL_LABS_TOKEN }}
WORKFLOW_VERCEL_TEAM: "team_nO2mCG4W8IxPIeKoSsqwAxxB"
WORKFLOW_VERCEL_PROJECT: "prj_yjkM7UdHliv8bfxZ1sMJQf1pMpdi"
WORKFLOW_VERCEL_PROJECT_SLUG: "example-nextjs-workflow-turbopack"
# See the note on e2e-vercel-prod: PRs point at the protected
# workflow-server preview; unset on main for production.
VERCEL_WORKFLOW_SERVER_URL: ${{ github.ref != 'refs/heads/main' && secrets.VERCEL_WORKFLOW_SERVER_URL || '' }}

- name: Capture runtime logs on failure
if: failure()
env:
APP_NAME: "nextjs-turbopack"
WORKFLOW_VERCEL_AUTH_TOKEN: ${{ secrets.VERCEL_LABS_TOKEN }}
WORKFLOW_VERCEL_TEAM: "team_nO2mCG4W8IxPIeKoSsqwAxxB"
WORKFLOW_VERCEL_PROJECT: "prj_yjkM7UdHliv8bfxZ1sMJQf1pMpdi"
WORKFLOW_VERCEL_ENV: ${{ github.ref == 'refs/heads/main' && 'production' || 'preview' }}
VERCEL_DEPLOYMENT_ID: ${{ steps.waitForDeployment.outputs.deployment-id }}
run: node .github/scripts/fetch-e2e-runtime-logs.mjs

- name: Upload E2E results
if: always()
uses: actions/upload-artifact@v4
with:
name: e2e-results-vercel-multi-region-nextjs-turbopack
path: |
e2e-vercel-multi-region-nextjs-turbopack.json
e2e-metadata-nextjs-turbopack-vercel.json
e2e-failures-nextjs-turbopack-vercel.json
e2e-diagnostics-nextjs-turbopack-vercel.json
e2e-runtime-logs-nextjs-turbopack-vercel.json
retention-days: 7
if-no-files-found: ignore

getTestMatrix:
name: Get Test Matrix
runs-on: ubuntu-latest
Expand DownExpand Up@@ -525,7 +612,7 @@ jobs:
(cd "$WORKBENCH_APP_PATH" && pnpm dev 2>&1 | tee "$DEV_SERVER_LOG_PATH") &
echo "starting tests in 10 seconds" && sleep 10
pnpm vitest run packages/core/e2e/dev.test.ts; sleep 10
pnpm run test:e2e --reporter=default --reporter=json --reporter=./packages/core/e2e/github-reporter.ts --outputFile=e2e-local-dev-${{ matrix.app.name }}-${{ matrix.app.artifactSuffix }}.json
pnpm run test:e2e --reporter=verbose --reporter=json --reporter=./packages/core/e2e/github-reporter.ts --outputFile=e2e-local-dev-${{ matrix.app.name }}-${{ matrix.app.artifactSuffix }}.json
env:
NODE_OPTIONS: "--enable-source-maps"
APP_NAME: ${{ matrix.app.name }}
Expand DownExpand Up@@ -615,7 +702,7 @@ jobs:
export PROD_SERVER_LOG_PATH="$GITHUB_WORKSPACE/prod-server-${{ matrix.app.name }}-${{ matrix.app.artifactSuffix }}.log"
(cd "$WORKBENCH_APP_PATH" && pnpm start 2>&1 | tee "$PROD_SERVER_LOG_PATH") &
echo "starting tests in 10 seconds" && sleep 10
pnpm run test:e2e --reporter=default --reporter=json --reporter=./packages/core/e2e/github-reporter.ts --outputFile=e2e-local-prod-${{ matrix.app.name }}-${{ matrix.app.artifactSuffix }}.json
pnpm run test:e2e --reporter=verbose --reporter=json --reporter=./packages/core/e2e/github-reporter.ts --outputFile=e2e-local-prod-${{ matrix.app.name }}-${{ matrix.app.artifactSuffix }}.json
env:
NODE_OPTIONS: "--enable-source-maps"
APP_NAME: ${{ matrix.app.name }}
Expand DownExpand Up@@ -723,7 +810,7 @@ jobs:
export PROD_SERVER_LOG_PATH="$GITHUB_WORKSPACE/prod-server-postgres-${{ matrix.app.name }}-${{ matrix.app.artifactSuffix }}.log"
(cd "$WORKBENCH_APP_PATH" && pnpm start 2>&1 | tee "$PROD_SERVER_LOG_PATH") &
echo "starting tests in 10 seconds" && sleep 10
pnpm run test:e2e --reporter=default --reporter=json --reporter=./packages/core/e2e/github-reporter.ts --outputFile=e2e-local-postgres-${{ matrix.app.name }}-${{ matrix.app.artifactSuffix }}.json
pnpm run test:e2e --reporter=verbose --reporter=json --reporter=./packages/core/e2e/github-reporter.ts --outputFile=e2e-local-postgres-${{ matrix.app.name }}-${{ matrix.app.artifactSuffix }}.json
env:
NODE_OPTIONS: "--enable-source-maps"
APP_NAME: ${{ matrix.app.name }}
Expand DownExpand Up@@ -841,7 +928,7 @@ jobs:
exit 1
}

pnpm run test:e2e --reporter=default --reporter=json --reporter=./packages/core/e2e/github-reporter.ts --outputFile=e2e-windows-nextjs-turbopack.json
pnpm run test:e2e --reporter=verbose --reporter=json --reporter=./packages/core/e2e/github-reporter.ts --outputFile=e2e-windows-nextjs-turbopack.json
$e2eExit = $LASTEXITCODE
Stop-Job $job -ErrorAction SilentlyContinue
exit $e2eExit
Expand Down
8 changes: 8 additions & 0 deletions e2e-diagnostics-nextjs-turbopack-vercel.json
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
[
{
"testName": "WorkflowNotRegisteredError fails the run when workflow does not exist",
"runId": "wrun_41KV8YMWKC00RAQCRH320GSWR4",
"timestamp": "2026-06-16T19:31:07.845Z",
"dashboardUrl": "https://vercel.com/vercel-labs/example-nextjs-workflow-turbopack/observability/workflows/runs/wrun_41KV8YMWKC00RAQCRH320GSWR4?environment=preview"
}
]
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
28e9c8f
[world-vercel] Add /run-id sub-export with tagged ULID encode/decode
TooTallNate May 13, 2026
76e0024
[world-vercel] Add string-value assertions to run-id tests
TooTallNate May 13, 2026
cc1e0b8
[world-vercel] Remove internal-repo reference from regions doc comment
TooTallNate May 13, 2026
9147205
[world-vercel] Address PR review feedback on run-id sub-export
TooTallNate May 13, 2026
6efe0df
[world] [core] [world-vercel] Add World.createRunId() and region-awar…
TooTallNate May 13, 2026
d072229
[core] [world] [world-vercel] Pass full StartOptions to World.createR…
TooTallNate May 13, 2026
ef2993d
Merge remote-tracking branch 'origin/main' into world-create-run-id
VaguelySerious Jun 11, 2026
68feaa7
Address review feedback: doc fixes and deterministic same-ms tests
VaguelySerious Jun 11, 2026
162993f
Merge remote-tracking branch 'origin/main' into world-create-run-id
TooTallNate Jun 15, 2026
07d4718
test: keep workflow-server override rewrite-compatible
TooTallNate Jun 15, 2026
4f87069
fix(world): clear region tag bit before ULID timestamp validation
TooTallNate Jun 15, 2026
24282d8
fix(world-vercel): validate tagged runId timestamps via run-id decode
TooTallNate Jun 16, 2026
c8d65dc
fix(world-vercel): default run ID region to iad1 instead of unknown
TooTallNate Jun 16, 2026
bed8f7c
Merge remote-tracking branch 'origin/main' into world-create-run-id
TooTallNate Jun 17, 2026
f2b905d
test(e2e): use verbose reporter + per-test start heartbeat
TooTallNate Jun 17, 2026
8cc53c7
test(world-vercel): point WORKFLOW_SERVER_URL_OVERRIDE at combined 52…
TooTallNate Jun 17, 2026
05c98dc
Merge remote-tracking branch 'origin/main' into world-create-run-id
TooTallNate Jun 17, 2026
f29ee31
fix(core): region-tag the health-check correlationId
TooTallNate Jun 17, 2026
d84f83f
Merge remote-tracking branch 'origin/main' into world-create-run-id
VaguelySerious Jul 1, 2026
17197cd
Address review feedback: validate region overrides, reset server over…
VaguelySerious Jul 1, 2026
fc3e1f9
test(world-vercel): point WORKFLOW_SERVER_URL_OVERRIDE at wave-1 mult…
TooTallNate Jul 6, 2026
4480b37
Merge origin/main into world-create-run-id
TooTallNate Jul 6, 2026
ca0a65b
test(e2e): Vercel multi-region suite for start()'s region option
TooTallNate Jul 6, 2026
31e01af
test(e2e): start region probes in-function; fix getWorld await
TooTallNate Jul 6, 2026
b149741
Merge origin/main into world-create-run-id
TooTallNate Jul 6, 2026
450f4e9
test(e2e): cover explicit and implicit region starts in the multi-reg…
TooTallNate Jul 6, 2026
d933d96
Revert WORKFLOW_SERVER_URL_OVERRIDE to '' — wave-1 multi-region servi…
TooTallNate Jul 6, 2026
3d7c34b
test(e2e): cross-region stream visibility (iad1 writer, sfo1 reader)
TooTallNate Jul 7, 2026
3625089
test(e2e): extend multi-region suite to all 19 provisioned regions
TooTallNate Jul 8, 2026
5a936f1
test(e2e): tolerate geo-adjacent execution of queue callbacks
TooTallNate Jul 8, 2026
dff3f80
Revert WORKFLOW_SERVER_URL_OVERRIDE to '' — all-regions serving is in…
TooTallNate Jul 8, 2026
586c6e3
Merge origin/main into world-create-run-id
TooTallNate Jul 8, 2026
c42618f
docs: fix three stale comments flagged in review
TooTallNate Jul 8, 2026
fce2a8e
test(e2e): cover hook resolve/resume for runs owned by non-iad1 regions
TooTallNate Jul 9, 2026
4f77bc1
Merge origin/main into world-create-run-id
TooTallNate Jul 13, 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
6 changes: 6 additions & 0 deletions .changeset/core-run-id-input.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
---
"@workflow/core": minor
"workflow": minor
---

`start()` now delegates run ID generation to `world.createRunId(options)` when the world provides it, falling back to a monotonic ULID otherwise. The full options bag is passed through so worlds can read whichever fields they recognise. Adds a new `region` option that worlds may consume — when set, it is also forwarded onto the queue options so the initial workflow message is routed to the matching region.
5 changes: 5 additions & 0 deletions .changeset/world-create-run-id.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
---
"@workflow/world": minor
---

Add optional `createRunId(options?)` to the `World` interface and `region` to `QueueOptions`. Worlds can now mint custom run IDs (reading whichever start-option fields they recognise) and route messages to a specific region.
5 changes: 5 additions & 0 deletions .changeset/world-vercel-create-run-id.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
---
"@workflow/world-vercel": minor
---

Implement `World.createRunId` to mint region-tagged ULIDs, preferring an explicit `options.region` from `start()`, then the `VERCEL_REGION` environment variable, and finally defaulting to `iad1` so run IDs are always tagged with a concrete, routable region. The queue now routes each message to the region encoded in the payload's tagged run ID (or to an explicit `opts.region` override), instead of the previous hard-coded `iad1`.
97 changes: 92 additions & 5 deletions .github/workflows/tests.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -381,7 +381,7 @@ jobs:
run: echo "ms=$(($(date +%s) * 1000))" >> "$GITHUB_OUTPUT"

- name: Run E2E Tests
run: pnpm run test:e2e --reporter=default --reporter=json --reporter=./packages/core/e2e/github-reporter.ts "--outputFile=e2e-vercel-prod-$APP_NAME.json"
run: pnpm run test:e2e --reporter=verbose --reporter=json --reporter=./packages/core/e2e/github-reporter.ts "--outputFile=e2e-vercel-prod-$APP_NAME.json"
env:
NODE_OPTIONS: "--enable-source-maps"
DEPLOYMENT_URL: ${{ steps.waitForDeployment.outputs.deployment-url }}
Expand DownExpand Up@@ -436,6 +436,93 @@ jobs:
retention-days: 7
if-no-files-found: ignore

e2e-vercel-multi-region:
# Vercel-specific multi-region routing suite (start()'s `region`
# option). Deliberately separate from e2e-vercel-prod: that job runs
# the shared suite as a matrix across all worlds/frameworks, while
# this functionality only exists for @workflow/world-vercel and only
# the nextjs-turbopack workbench is deployed multi-region
# (workbench/nextjs-turbopack/vercel.json pins iad1+sfo1+fra1).
name: E2E Vercel Multi-Region Tests (nextjs-turbopack)
runs-on: ubuntu-latest
# 19 regions: the implicit-route cases pay one cold start each,
# sequentially.
timeout-minutes: 35
needs: ci-scope
if: ${{ needs.ci-scope.outputs.fast-path != 'true' }}
permissions:
id-token: write
contents: read
deployments: read
statuses: read
env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
WORKFLOW_PUBLIC_MANIFEST: '1'
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: Waiting for the Vercel deployment
id: waitForDeployment
uses: vercel/wait-for-deployment-action@0e2b0c5c5cce31f1648108aeec56467187aca037
with:
project-slug: "example-nextjs-workflow-turbopack"
timeout: 1000
check-interval: 15
environment: ${{ github.ref == 'refs/heads/main' && 'production' || 'preview' }}

- name: Run Multi-Region E2E Tests
run: pnpm vitest run packages/core/e2e/e2e-region.test.ts --reporter=verbose --reporter=json --reporter=./packages/core/e2e/github-reporter.ts "--outputFile=e2e-vercel-multi-region-$APP_NAME.json"
env:
NODE_OPTIONS: "--enable-source-maps"
DEPLOYMENT_URL: ${{ steps.waitForDeployment.outputs.deployment-url }}
VERCEL_DEPLOYMENT_ID: ${{ steps.waitForDeployment.outputs.deployment-id }}
APP_NAME: "nextjs-turbopack"
WORKFLOW_VERCEL_ENV: ${{ github.ref == 'refs/heads/main' && 'production' || 'preview' }}
WORKFLOW_VERCEL_AUTH_TOKEN: ${{ secrets.VERCEL_LABS_TOKEN }}
WORKFLOW_VERCEL_TEAM: "team_nO2mCG4W8IxPIeKoSsqwAxxB"
WORKFLOW_VERCEL_PROJECT: "prj_yjkM7UdHliv8bfxZ1sMJQf1pMpdi"
WORKFLOW_VERCEL_PROJECT_SLUG: "example-nextjs-workflow-turbopack"
# See the note on e2e-vercel-prod: PRs point at the protected
# workflow-server preview; unset on main for production.
VERCEL_WORKFLOW_SERVER_URL: ${{ github.ref != 'refs/heads/main' && secrets.VERCEL_WORKFLOW_SERVER_URL || '' }}

- name: Capture runtime logs on failure
if: failure()
env:
APP_NAME: "nextjs-turbopack"
WORKFLOW_VERCEL_AUTH_TOKEN: ${{ secrets.VERCEL_LABS_TOKEN }}
WORKFLOW_VERCEL_TEAM: "team_nO2mCG4W8IxPIeKoSsqwAxxB"
WORKFLOW_VERCEL_PROJECT: "prj_yjkM7UdHliv8bfxZ1sMJQf1pMpdi"
WORKFLOW_VERCEL_ENV: ${{ github.ref == 'refs/heads/main' && 'production' || 'preview' }}
VERCEL_DEPLOYMENT_ID: ${{ steps.waitForDeployment.outputs.deployment-id }}
run: node .github/scripts/fetch-e2e-runtime-logs.mjs

- name: Upload E2E results
if: always()
uses: actions/upload-artifact@v4
with:
name: e2e-results-vercel-multi-region-nextjs-turbopack
path: |
e2e-vercel-multi-region-nextjs-turbopack.json
e2e-metadata-nextjs-turbopack-vercel.json
e2e-failures-nextjs-turbopack-vercel.json
e2e-diagnostics-nextjs-turbopack-vercel.json
e2e-runtime-logs-nextjs-turbopack-vercel.json
retention-days: 7
if-no-files-found: ignore

getTestMatrix:
name: Get Test Matrix
runs-on: ubuntu-latest
Expand DownExpand Up@@ -525,7 +612,7 @@ jobs:
(cd "$WORKBENCH_APP_PATH" && pnpm dev 2>&1 | tee "$DEV_SERVER_LOG_PATH") &
echo "starting tests in 10 seconds" && sleep 10
pnpm vitest run packages/core/e2e/dev.test.ts; sleep 10
pnpm run test:e2e --reporter=default --reporter=json --reporter=./packages/core/e2e/github-reporter.ts --outputFile=e2e-local-dev-${{ matrix.app.name }}-${{ matrix.app.artifactSuffix }}.json
pnpm run test:e2e --reporter=verbose --reporter=json --reporter=./packages/core/e2e/github-reporter.ts --outputFile=e2e-local-dev-${{ matrix.app.name }}-${{ matrix.app.artifactSuffix }}.json
env:
NODE_OPTIONS: "--enable-source-maps"
APP_NAME: ${{ matrix.app.name }}
Expand DownExpand Up@@ -615,7 +702,7 @@ jobs:
export PROD_SERVER_LOG_PATH="$GITHUB_WORKSPACE/prod-server-${{ matrix.app.name }}-${{ matrix.app.artifactSuffix }}.log"
(cd "$WORKBENCH_APP_PATH" && pnpm start 2>&1 | tee "$PROD_SERVER_LOG_PATH") &
echo "starting tests in 10 seconds" && sleep 10
pnpm run test:e2e --reporter=default --reporter=json --reporter=./packages/core/e2e/github-reporter.ts --outputFile=e2e-local-prod-${{ matrix.app.name }}-${{ matrix.app.artifactSuffix }}.json
pnpm run test:e2e --reporter=verbose --reporter=json --reporter=./packages/core/e2e/github-reporter.ts --outputFile=e2e-local-prod-${{ matrix.app.name }}-${{ matrix.app.artifactSuffix }}.json
env:
NODE_OPTIONS: "--enable-source-maps"
APP_NAME: ${{ matrix.app.name }}
Expand DownExpand Up@@ -723,7 +810,7 @@ jobs:
export PROD_SERVER_LOG_PATH="$GITHUB_WORKSPACE/prod-server-postgres-${{ matrix.app.name }}-${{ matrix.app.artifactSuffix }}.log"
(cd "$WORKBENCH_APP_PATH" && pnpm start 2>&1 | tee "$PROD_SERVER_LOG_PATH") &
echo "starting tests in 10 seconds" && sleep 10
pnpm run test:e2e --reporter=default --reporter=json --reporter=./packages/core/e2e/github-reporter.ts --outputFile=e2e-local-postgres-${{ matrix.app.name }}-${{ matrix.app.artifactSuffix }}.json
pnpm run test:e2e --reporter=verbose --reporter=json --reporter=./packages/core/e2e/github-reporter.ts --outputFile=e2e-local-postgres-${{ matrix.app.name }}-${{ matrix.app.artifactSuffix }}.json
env:
NODE_OPTIONS: "--enable-source-maps"
APP_NAME: ${{ matrix.app.name }}
Expand DownExpand Up@@ -841,7 +928,7 @@ jobs:
exit 1
}

pnpm run test:e2e --reporter=default --reporter=json --reporter=./packages/core/e2e/github-reporter.ts --outputFile=e2e-windows-nextjs-turbopack.json
pnpm run test:e2e --reporter=verbose --reporter=json --reporter=./packages/core/e2e/github-reporter.ts --outputFile=e2e-windows-nextjs-turbopack.json
$e2eExit = $LASTEXITCODE
Stop-Job $job -ErrorAction SilentlyContinue
exit $e2eExit
Expand Down
8 changes: 8 additions & 0 deletions e2e-diagnostics-nextjs-turbopack-vercel.json
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
[
{
"testName": "WorkflowNotRegisteredError fails the run when workflow does not exist",
"runId": "wrun_41KV8YMWKC00RAQCRH320GSWR4",
"timestamp": "2026-06-16T19:31:07.845Z",
"dashboardUrl": "https://vercel.com/vercel-labs/example-nextjs-workflow-turbopack/observability/workflows/runs/wrun_41KV8YMWKC00RAQCRH320GSWR4?environment=preview"
}
]
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
28e9c8f
[world-vercel] Add /run-id sub-export with tagged ULID encode/decode
TooTallNate May 13, 2026
76e0024
[world-vercel] Add string-value assertions to run-id tests
TooTallNate May 13, 2026
cc1e0b8
[world-vercel] Remove internal-repo reference from regions doc comment
TooTallNate May 13, 2026
9147205
[world-vercel] Address PR review feedback on run-id sub-export
TooTallNate May 13, 2026
6efe0df
[world] [core] [world-vercel] Add World.createRunId() and region-awar…
TooTallNate May 13, 2026
d072229
[core] [world] [world-vercel] Pass full StartOptions to World.createR…
TooTallNate May 13, 2026
ef2993d
Merge remote-tracking branch 'origin/main' into world-create-run-id
VaguelySerious Jun 11, 2026
68feaa7
Address review feedback: doc fixes and deterministic same-ms tests
VaguelySerious Jun 11, 2026
162993f
Merge remote-tracking branch 'origin/main' into world-create-run-id
TooTallNate Jun 15, 2026
07d4718
test: keep workflow-server override rewrite-compatible
TooTallNate Jun 15, 2026
4f87069
fix(world): clear region tag bit before ULID timestamp validation
TooTallNate Jun 15, 2026
24282d8
fix(world-vercel): validate tagged runId timestamps via run-id decode
TooTallNate Jun 16, 2026
c8d65dc
fix(world-vercel): default run ID region to iad1 instead of unknown
TooTallNate Jun 16, 2026
bed8f7c
Merge remote-tracking branch 'origin/main' into world-create-run-id
TooTallNate Jun 17, 2026
f2b905d
test(e2e): use verbose reporter + per-test start heartbeat
TooTallNate Jun 17, 2026
8cc53c7
test(world-vercel): point WORKFLOW_SERVER_URL_OVERRIDE at combined 52…
TooTallNate Jun 17, 2026
05c98dc
Merge remote-tracking branch 'origin/main' into world-create-run-id
TooTallNate Jun 17, 2026
f29ee31
fix(core): region-tag the health-check correlationId
TooTallNate Jun 17, 2026
d84f83f
Merge remote-tracking branch 'origin/main' into world-create-run-id
VaguelySerious Jul 1, 2026
17197cd
Address review feedback: validate region overrides, reset server over…
VaguelySerious Jul 1, 2026
fc3e1f9
test(world-vercel): point WORKFLOW_SERVER_URL_OVERRIDE at wave-1 mult…
TooTallNate Jul 6, 2026
4480b37
Merge origin/main into world-create-run-id
TooTallNate Jul 6, 2026
ca0a65b
test(e2e): Vercel multi-region suite for start()'s region option
TooTallNate Jul 6, 2026
31e01af
test(e2e): start region probes in-function; fix getWorld await
TooTallNate Jul 6, 2026
b149741
Merge origin/main into world-create-run-id
TooTallNate Jul 6, 2026
450f4e9
test(e2e): cover explicit and implicit region starts in the multi-reg…
TooTallNate Jul 6, 2026
d933d96
Revert WORKFLOW_SERVER_URL_OVERRIDE to '' — wave-1 multi-region servi…
TooTallNate Jul 6, 2026
3d7c34b
test(e2e): cross-region stream visibility (iad1 writer, sfo1 reader)
TooTallNate Jul 7, 2026
3625089
test(e2e): extend multi-region suite to all 19 provisioned regions
TooTallNate Jul 8, 2026
5a936f1
test(e2e): tolerate geo-adjacent execution of queue callbacks
TooTallNate Jul 8, 2026
dff3f80
Revert WORKFLOW_SERVER_URL_OVERRIDE to '' — all-regions serving is in…
TooTallNate Jul 8, 2026
586c6e3
Merge origin/main into world-create-run-id
TooTallNate Jul 8, 2026
c42618f
docs: fix three stale comments flagged in review
TooTallNate Jul 8, 2026
fce2a8e
test(e2e): cover hook resolve/resume for runs owned by non-iad1 regions
TooTallNate Jul 9, 2026
4f77bc1
Merge origin/main into world-create-run-id
TooTallNate Jul 13, 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
6 changes: 6 additions & 0 deletions .changeset/core-run-id-input.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
---
"@workflow/core": minor
"workflow": minor
---

`start()` now delegates run ID generation to `world.createRunId(options)` when the world provides it, falling back to a monotonic ULID otherwise. The full options bag is passed through so worlds can read whichever fields they recognise. Adds a new `region` option that worlds may consume — when set, it is also forwarded onto the queue options so the initial workflow message is routed to the matching region.
5 changes: 5 additions & 0 deletions .changeset/world-create-run-id.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
---
"@workflow/world": minor
---

Add optional `createRunId(options?)` to the `World` interface and `region` to `QueueOptions`. Worlds can now mint custom run IDs (reading whichever start-option fields they recognise) and route messages to a specific region.
5 changes: 5 additions & 0 deletions .changeset/world-vercel-create-run-id.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
---
"@workflow/world-vercel": minor
---

Implement `World.createRunId` to mint region-tagged ULIDs, preferring an explicit `options.region` from `start()`, then the `VERCEL_REGION` environment variable, and finally defaulting to `iad1` so run IDs are always tagged with a concrete, routable region. The queue now routes each message to the region encoded in the payload's tagged run ID (or to an explicit `opts.region` override), instead of the previous hard-coded `iad1`.
97 changes: 92 additions & 5 deletions .github/workflows/tests.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -381,7 +381,7 @@ jobs:
run: echo "ms=$(($(date +%s) * 1000))" >> "$GITHUB_OUTPUT"

- name: Run E2E Tests
run: pnpm run test:e2e --reporter=default --reporter=json --reporter=./packages/core/e2e/github-reporter.ts "--outputFile=e2e-vercel-prod-$APP_NAME.json"
run: pnpm run test:e2e --reporter=verbose --reporter=json --reporter=./packages/core/e2e/github-reporter.ts "--outputFile=e2e-vercel-prod-$APP_NAME.json"
env:
NODE_OPTIONS: "--enable-source-maps"
DEPLOYMENT_URL: ${{ steps.waitForDeployment.outputs.deployment-url }}
Expand DownExpand Up@@ -436,6 +436,93 @@ jobs:
retention-days: 7
if-no-files-found: ignore

e2e-vercel-multi-region:
# Vercel-specific multi-region routing suite (start()'s `region`
# option). Deliberately separate from e2e-vercel-prod: that job runs
# the shared suite as a matrix across all worlds/frameworks, while
# this functionality only exists for @workflow/world-vercel and only
# the nextjs-turbopack workbench is deployed multi-region
# (workbench/nextjs-turbopack/vercel.json pins iad1+sfo1+fra1).
name: E2E Vercel Multi-Region Tests (nextjs-turbopack)
runs-on: ubuntu-latest
# 19 regions: the implicit-route cases pay one cold start each,
# sequentially.
timeout-minutes: 35
needs: ci-scope
if: ${{ needs.ci-scope.outputs.fast-path != 'true' }}
permissions:
id-token: write
contents: read
deployments: read
statuses: read
env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
WORKFLOW_PUBLIC_MANIFEST: '1'
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: Waiting for the Vercel deployment
id: waitForDeployment
uses: vercel/wait-for-deployment-action@0e2b0c5c5cce31f1648108aeec56467187aca037
with:
project-slug: "example-nextjs-workflow-turbopack"
timeout: 1000
check-interval: 15
environment: ${{ github.ref == 'refs/heads/main' && 'production' || 'preview' }}

- name: Run Multi-Region E2E Tests
run: pnpm vitest run packages/core/e2e/e2e-region.test.ts --reporter=verbose --reporter=json --reporter=./packages/core/e2e/github-reporter.ts "--outputFile=e2e-vercel-multi-region-$APP_NAME.json"
env:
NODE_OPTIONS: "--enable-source-maps"
DEPLOYMENT_URL: ${{ steps.waitForDeployment.outputs.deployment-url }}
VERCEL_DEPLOYMENT_ID: ${{ steps.waitForDeployment.outputs.deployment-id }}
APP_NAME: "nextjs-turbopack"
WORKFLOW_VERCEL_ENV: ${{ github.ref == 'refs/heads/main' && 'production' || 'preview' }}
WORKFLOW_VERCEL_AUTH_TOKEN: ${{ secrets.VERCEL_LABS_TOKEN }}
WORKFLOW_VERCEL_TEAM: "team_nO2mCG4W8IxPIeKoSsqwAxxB"
WORKFLOW_VERCEL_PROJECT: "prj_yjkM7UdHliv8bfxZ1sMJQf1pMpdi"
WORKFLOW_VERCEL_PROJECT_SLUG: "example-nextjs-workflow-turbopack"
# See the note on e2e-vercel-prod: PRs point at the protected
# workflow-server preview; unset on main for production.
VERCEL_WORKFLOW_SERVER_URL: ${{ github.ref != 'refs/heads/main' && secrets.VERCEL_WORKFLOW_SERVER_URL || '' }}

- name: Capture runtime logs on failure
if: failure()
env:
APP_NAME: "nextjs-turbopack"
WORKFLOW_VERCEL_AUTH_TOKEN: ${{ secrets.VERCEL_LABS_TOKEN }}
WORKFLOW_VERCEL_TEAM: "team_nO2mCG4W8IxPIeKoSsqwAxxB"
WORKFLOW_VERCEL_PROJECT: "prj_yjkM7UdHliv8bfxZ1sMJQf1pMpdi"
WORKFLOW_VERCEL_ENV: ${{ github.ref == 'refs/heads/main' && 'production' || 'preview' }}
VERCEL_DEPLOYMENT_ID: ${{ steps.waitForDeployment.outputs.deployment-id }}
run: node .github/scripts/fetch-e2e-runtime-logs.mjs

- name: Upload E2E results
if: always()
uses: actions/upload-artifact@v4
with:
name: e2e-results-vercel-multi-region-nextjs-turbopack
path: |
e2e-vercel-multi-region-nextjs-turbopack.json
e2e-metadata-nextjs-turbopack-vercel.json
e2e-failures-nextjs-turbopack-vercel.json
e2e-diagnostics-nextjs-turbopack-vercel.json
e2e-runtime-logs-nextjs-turbopack-vercel.json
retention-days: 7
if-no-files-found: ignore

getTestMatrix:
name: Get Test Matrix
runs-on: ubuntu-latest
Expand DownExpand Up@@ -525,7 +612,7 @@ jobs:
(cd "$WORKBENCH_APP_PATH" && pnpm dev 2>&1 | tee "$DEV_SERVER_LOG_PATH") &
echo "starting tests in 10 seconds" && sleep 10
pnpm vitest run packages/core/e2e/dev.test.ts; sleep 10
pnpm run test:e2e --reporter=default --reporter=json --reporter=./packages/core/e2e/github-reporter.ts --outputFile=e2e-local-dev-${{ matrix.app.name }}-${{ matrix.app.artifactSuffix }}.json
pnpm run test:e2e --reporter=verbose --reporter=json --reporter=./packages/core/e2e/github-reporter.ts --outputFile=e2e-local-dev-${{ matrix.app.name }}-${{ matrix.app.artifactSuffix }}.json
env:
NODE_OPTIONS: "--enable-source-maps"
APP_NAME: ${{ matrix.app.name }}
Expand DownExpand Up@@ -615,7 +702,7 @@ jobs:
export PROD_SERVER_LOG_PATH="$GITHUB_WORKSPACE/prod-server-${{ matrix.app.name }}-${{ matrix.app.artifactSuffix }}.log"
(cd "$WORKBENCH_APP_PATH" && pnpm start 2>&1 | tee "$PROD_SERVER_LOG_PATH") &
echo "starting tests in 10 seconds" && sleep 10
pnpm run test:e2e --reporter=default --reporter=json --reporter=./packages/core/e2e/github-reporter.ts --outputFile=e2e-local-prod-${{ matrix.app.name }}-${{ matrix.app.artifactSuffix }}.json
pnpm run test:e2e --reporter=verbose --reporter=json --reporter=./packages/core/e2e/github-reporter.ts --outputFile=e2e-local-prod-${{ matrix.app.name }}-${{ matrix.app.artifactSuffix }}.json
env:
NODE_OPTIONS: "--enable-source-maps"
APP_NAME: ${{ matrix.app.name }}
Expand DownExpand Up@@ -723,7 +810,7 @@ jobs:
export PROD_SERVER_LOG_PATH="$GITHUB_WORKSPACE/prod-server-postgres-${{ matrix.app.name }}-${{ matrix.app.artifactSuffix }}.log"
(cd "$WORKBENCH_APP_PATH" && pnpm start 2>&1 | tee "$PROD_SERVER_LOG_PATH") &
echo "starting tests in 10 seconds" && sleep 10
pnpm run test:e2e --reporter=default --reporter=json --reporter=./packages/core/e2e/github-reporter.ts --outputFile=e2e-local-postgres-${{ matrix.app.name }}-${{ matrix.app.artifactSuffix }}.json
pnpm run test:e2e --reporter=verbose --reporter=json --reporter=./packages/core/e2e/github-reporter.ts --outputFile=e2e-local-postgres-${{ matrix.app.name }}-${{ matrix.app.artifactSuffix }}.json
env:
NODE_OPTIONS: "--enable-source-maps"
APP_NAME: ${{ matrix.app.name }}
Expand DownExpand Up@@ -841,7 +928,7 @@ jobs:
exit 1
}

pnpm run test:e2e --reporter=default --reporter=json --reporter=./packages/core/e2e/github-reporter.ts --outputFile=e2e-windows-nextjs-turbopack.json
pnpm run test:e2e --reporter=verbose --reporter=json --reporter=./packages/core/e2e/github-reporter.ts --outputFile=e2e-windows-nextjs-turbopack.json
$e2eExit = $LASTEXITCODE
Stop-Job $job -ErrorAction SilentlyContinue
exit $e2eExit
Expand Down
8 changes: 8 additions & 0 deletions e2e-diagnostics-nextjs-turbopack-vercel.json
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
[
{
"testName": "WorkflowNotRegisteredError fails the run when workflow does not exist",
"runId": "wrun_41KV8YMWKC00RAQCRH320GSWR4",
"timestamp": "2026-06-16T19:31:07.845Z",
"dashboardUrl": "https://vercel.com/vercel-labs/example-nextjs-workflow-turbopack/observability/workflows/runs/wrun_41KV8YMWKC00RAQCRH320GSWR4?environment=preview"
}
]
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
28e9c8f
[world-vercel] Add /run-id sub-export with tagged ULID encode/decode
TooTallNate May 13, 2026
76e0024
[world-vercel] Add string-value assertions to run-id tests
TooTallNate May 13, 2026
cc1e0b8
[world-vercel] Remove internal-repo reference from regions doc comment
TooTallNate May 13, 2026
9147205
[world-vercel] Address PR review feedback on run-id sub-export
TooTallNate May 13, 2026
6efe0df
[world] [core] [world-vercel] Add World.createRunId() and region-awar…
TooTallNate May 13, 2026
d072229
[core] [world] [world-vercel] Pass full StartOptions to World.createR…
TooTallNate May 13, 2026
ef2993d
Merge remote-tracking branch 'origin/main' into world-create-run-id
VaguelySerious Jun 11, 2026
68feaa7
Address review feedback: doc fixes and deterministic same-ms tests
VaguelySerious Jun 11, 2026
162993f
Merge remote-tracking branch 'origin/main' into world-create-run-id
TooTallNate Jun 15, 2026
07d4718
test: keep workflow-server override rewrite-compatible
TooTallNate Jun 15, 2026
4f87069
fix(world): clear region tag bit before ULID timestamp validation
TooTallNate Jun 15, 2026
24282d8
fix(world-vercel): validate tagged runId timestamps via run-id decode
TooTallNate Jun 16, 2026
c8d65dc
fix(world-vercel): default run ID region to iad1 instead of unknown
TooTallNate Jun 16, 2026
bed8f7c
Merge remote-tracking branch 'origin/main' into world-create-run-id
TooTallNate Jun 17, 2026
f2b905d
test(e2e): use verbose reporter + per-test start heartbeat
TooTallNate Jun 17, 2026
8cc53c7
test(world-vercel): point WORKFLOW_SERVER_URL_OVERRIDE at combined 52…
TooTallNate Jun 17, 2026
05c98dc
Merge remote-tracking branch 'origin/main' into world-create-run-id
TooTallNate Jun 17, 2026
f29ee31
fix(core): region-tag the health-check correlationId
TooTallNate Jun 17, 2026
d84f83f
Merge remote-tracking branch 'origin/main' into world-create-run-id
VaguelySerious Jul 1, 2026
17197cd
Address review feedback: validate region overrides, reset server over…
VaguelySerious Jul 1, 2026
fc3e1f9
test(world-vercel): point WORKFLOW_SERVER_URL_OVERRIDE at wave-1 mult…
TooTallNate Jul 6, 2026
4480b37
Merge origin/main into world-create-run-id
TooTallNate Jul 6, 2026
ca0a65b
test(e2e): Vercel multi-region suite for start()'s region option
TooTallNate Jul 6, 2026
31e01af
test(e2e): start region probes in-function; fix getWorld await
TooTallNate Jul 6, 2026
b149741
Merge origin/main into world-create-run-id
TooTallNate Jul 6, 2026
450f4e9
test(e2e): cover explicit and implicit region starts in the multi-reg…
TooTallNate Jul 6, 2026
d933d96
Revert WORKFLOW_SERVER_URL_OVERRIDE to '' — wave-1 multi-region servi…
TooTallNate Jul 6, 2026
3d7c34b
test(e2e): cross-region stream visibility (iad1 writer, sfo1 reader)
TooTallNate Jul 7, 2026
3625089
test(e2e): extend multi-region suite to all 19 provisioned regions
TooTallNate Jul 8, 2026
5a936f1
test(e2e): tolerate geo-adjacent execution of queue callbacks
TooTallNate Jul 8, 2026
dff3f80
Revert WORKFLOW_SERVER_URL_OVERRIDE to '' — all-regions serving is in…
TooTallNate Jul 8, 2026
586c6e3
Merge origin/main into world-create-run-id
TooTallNate Jul 8, 2026
c42618f
docs: fix three stale comments flagged in review
TooTallNate Jul 8, 2026
fce2a8e
test(e2e): cover hook resolve/resume for runs owned by non-iad1 regions
TooTallNate Jul 9, 2026
4f77bc1
Merge origin/main into world-create-run-id
TooTallNate Jul 13, 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
6 changes: 6 additions & 0 deletions .changeset/core-run-id-input.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
---
"@workflow/core": minor
"workflow": minor
---

`start()` now delegates run ID generation to `world.createRunId(options)` when the world provides it, falling back to a monotonic ULID otherwise. The full options bag is passed through so worlds can read whichever fields they recognise. Adds a new `region` option that worlds may consume — when set, it is also forwarded onto the queue options so the initial workflow message is routed to the matching region.
5 changes: 5 additions & 0 deletions .changeset/world-create-run-id.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
---
"@workflow/world": minor
---

Add optional `createRunId(options?)` to the `World` interface and `region` to `QueueOptions`. Worlds can now mint custom run IDs (reading whichever start-option fields they recognise) and route messages to a specific region.
5 changes: 5 additions & 0 deletions .changeset/world-vercel-create-run-id.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
---
"@workflow/world-vercel": minor
---

Implement `World.createRunId` to mint region-tagged ULIDs, preferring an explicit `options.region` from `start()`, then the `VERCEL_REGION` environment variable, and finally defaulting to `iad1` so run IDs are always tagged with a concrete, routable region. The queue now routes each message to the region encoded in the payload's tagged run ID (or to an explicit `opts.region` override), instead of the previous hard-coded `iad1`.
97 changes: 92 additions & 5 deletions .github/workflows/tests.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -381,7 +381,7 @@ jobs:
run: echo "ms=$(($(date +%s) * 1000))" >> "$GITHUB_OUTPUT"

- name: Run E2E Tests
run: pnpm run test:e2e --reporter=default --reporter=json --reporter=./packages/core/e2e/github-reporter.ts "--outputFile=e2e-vercel-prod-$APP_NAME.json"
run: pnpm run test:e2e --reporter=verbose --reporter=json --reporter=./packages/core/e2e/github-reporter.ts "--outputFile=e2e-vercel-prod-$APP_NAME.json"
env:
NODE_OPTIONS: "--enable-source-maps"
DEPLOYMENT_URL: ${{ steps.waitForDeployment.outputs.deployment-url }}
Expand DownExpand Up@@ -436,6 +436,93 @@ jobs:
retention-days: 7
if-no-files-found: ignore

e2e-vercel-multi-region:
# Vercel-specific multi-region routing suite (start()'s `region`
# option). Deliberately separate from e2e-vercel-prod: that job runs
# the shared suite as a matrix across all worlds/frameworks, while
# this functionality only exists for @workflow/world-vercel and only
# the nextjs-turbopack workbench is deployed multi-region
# (workbench/nextjs-turbopack/vercel.json pins iad1+sfo1+fra1).
name: E2E Vercel Multi-Region Tests (nextjs-turbopack)
runs-on: ubuntu-latest
# 19 regions: the implicit-route cases pay one cold start each,
# sequentially.
timeout-minutes: 35
needs: ci-scope
if: ${{ needs.ci-scope.outputs.fast-path != 'true' }}
permissions:
id-token: write
contents: read
deployments: read
statuses: read
env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
WORKFLOW_PUBLIC_MANIFEST: '1'
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: Waiting for the Vercel deployment
id: waitForDeployment
uses: vercel/wait-for-deployment-action@0e2b0c5c5cce31f1648108aeec56467187aca037
with:
project-slug: "example-nextjs-workflow-turbopack"
timeout: 1000
check-interval: 15
environment: ${{ github.ref == 'refs/heads/main' && 'production' || 'preview' }}

- name: Run Multi-Region E2E Tests
run: pnpm vitest run packages/core/e2e/e2e-region.test.ts --reporter=verbose --reporter=json --reporter=./packages/core/e2e/github-reporter.ts "--outputFile=e2e-vercel-multi-region-$APP_NAME.json"
env:
NODE_OPTIONS: "--enable-source-maps"
DEPLOYMENT_URL: ${{ steps.waitForDeployment.outputs.deployment-url }}
VERCEL_DEPLOYMENT_ID: ${{ steps.waitForDeployment.outputs.deployment-id }}
APP_NAME: "nextjs-turbopack"
WORKFLOW_VERCEL_ENV: ${{ github.ref == 'refs/heads/main' && 'production' || 'preview' }}
WORKFLOW_VERCEL_AUTH_TOKEN: ${{ secrets.VERCEL_LABS_TOKEN }}
WORKFLOW_VERCEL_TEAM: "team_nO2mCG4W8IxPIeKoSsqwAxxB"
WORKFLOW_VERCEL_PROJECT: "prj_yjkM7UdHliv8bfxZ1sMJQf1pMpdi"
WORKFLOW_VERCEL_PROJECT_SLUG: "example-nextjs-workflow-turbopack"
# See the note on e2e-vercel-prod: PRs point at the protected
# workflow-server preview; unset on main for production.
VERCEL_WORKFLOW_SERVER_URL: ${{ github.ref != 'refs/heads/main' && secrets.VERCEL_WORKFLOW_SERVER_URL || '' }}

- name: Capture runtime logs on failure
if: failure()
env:
APP_NAME: "nextjs-turbopack"
WORKFLOW_VERCEL_AUTH_TOKEN: ${{ secrets.VERCEL_LABS_TOKEN }}
WORKFLOW_VERCEL_TEAM: "team_nO2mCG4W8IxPIeKoSsqwAxxB"
WORKFLOW_VERCEL_PROJECT: "prj_yjkM7UdHliv8bfxZ1sMJQf1pMpdi"
WORKFLOW_VERCEL_ENV: ${{ github.ref == 'refs/heads/main' && 'production' || 'preview' }}
VERCEL_DEPLOYMENT_ID: ${{ steps.waitForDeployment.outputs.deployment-id }}
run: node .github/scripts/fetch-e2e-runtime-logs.mjs

- name: Upload E2E results
if: always()
uses: actions/upload-artifact@v4
with:
name: e2e-results-vercel-multi-region-nextjs-turbopack
path: |
e2e-vercel-multi-region-nextjs-turbopack.json
e2e-metadata-nextjs-turbopack-vercel.json
e2e-failures-nextjs-turbopack-vercel.json
e2e-diagnostics-nextjs-turbopack-vercel.json
e2e-runtime-logs-nextjs-turbopack-vercel.json
retention-days: 7
if-no-files-found: ignore

getTestMatrix:
name: Get Test Matrix
runs-on: ubuntu-latest
Expand DownExpand Up@@ -525,7 +612,7 @@ jobs:
(cd "$WORKBENCH_APP_PATH" && pnpm dev 2>&1 | tee "$DEV_SERVER_LOG_PATH") &
echo "starting tests in 10 seconds" && sleep 10
pnpm vitest run packages/core/e2e/dev.test.ts; sleep 10
pnpm run test:e2e --reporter=default --reporter=json --reporter=./packages/core/e2e/github-reporter.ts --outputFile=e2e-local-dev-${{ matrix.app.name }}-${{ matrix.app.artifactSuffix }}.json
pnpm run test:e2e --reporter=verbose --reporter=json --reporter=./packages/core/e2e/github-reporter.ts --outputFile=e2e-local-dev-${{ matrix.app.name }}-${{ matrix.app.artifactSuffix }}.json
env:
NODE_OPTIONS: "--enable-source-maps"
APP_NAME: ${{ matrix.app.name }}
Expand DownExpand Up@@ -615,7 +702,7 @@ jobs:
export PROD_SERVER_LOG_PATH="$GITHUB_WORKSPACE/prod-server-${{ matrix.app.name }}-${{ matrix.app.artifactSuffix }}.log"
(cd "$WORKBENCH_APP_PATH" && pnpm start 2>&1 | tee "$PROD_SERVER_LOG_PATH") &
echo "starting tests in 10 seconds" && sleep 10
pnpm run test:e2e --reporter=default --reporter=json --reporter=./packages/core/e2e/github-reporter.ts --outputFile=e2e-local-prod-${{ matrix.app.name }}-${{ matrix.app.artifactSuffix }}.json
pnpm run test:e2e --reporter=verbose --reporter=json --reporter=./packages/core/e2e/github-reporter.ts --outputFile=e2e-local-prod-${{ matrix.app.name }}-${{ matrix.app.artifactSuffix }}.json
env:
NODE_OPTIONS: "--enable-source-maps"
APP_NAME: ${{ matrix.app.name }}
Expand DownExpand Up@@ -723,7 +810,7 @@ jobs:
export PROD_SERVER_LOG_PATH="$GITHUB_WORKSPACE/prod-server-postgres-${{ matrix.app.name }}-${{ matrix.app.artifactSuffix }}.log"
(cd "$WORKBENCH_APP_PATH" && pnpm start 2>&1 | tee "$PROD_SERVER_LOG_PATH") &
echo "starting tests in 10 seconds" && sleep 10
pnpm run test:e2e --reporter=default --reporter=json --reporter=./packages/core/e2e/github-reporter.ts --outputFile=e2e-local-postgres-${{ matrix.app.name }}-${{ matrix.app.artifactSuffix }}.json
pnpm run test:e2e --reporter=verbose --reporter=json --reporter=./packages/core/e2e/github-reporter.ts --outputFile=e2e-local-postgres-${{ matrix.app.name }}-${{ matrix.app.artifactSuffix }}.json
env:
NODE_OPTIONS: "--enable-source-maps"
APP_NAME: ${{ matrix.app.name }}
Expand DownExpand Up@@ -841,7 +928,7 @@ jobs:
exit 1
}

pnpm run test:e2e --reporter=default --reporter=json --reporter=./packages/core/e2e/github-reporter.ts --outputFile=e2e-windows-nextjs-turbopack.json
pnpm run test:e2e --reporter=verbose --reporter=json --reporter=./packages/core/e2e/github-reporter.ts --outputFile=e2e-windows-nextjs-turbopack.json
$e2eExit = $LASTEXITCODE
Stop-Job $job -ErrorAction SilentlyContinue
exit $e2eExit
Expand Down
8 changes: 8 additions & 0 deletions e2e-diagnostics-nextjs-turbopack-vercel.json
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
[
{
"testName": "WorkflowNotRegisteredError fails the run when workflow does not exist",
"runId": "wrun_41KV8YMWKC00RAQCRH320GSWR4",
"timestamp": "2026-06-16T19:31:07.845Z",
"dashboardUrl": "https://vercel.com/vercel-labs/example-nextjs-workflow-turbopack/observability/workflows/runs/wrun_41KV8YMWKC00RAQCRH320GSWR4?environment=preview"
}
]
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
28e9c8f
[world-vercel] Add /run-id sub-export with tagged ULID encode/decode
TooTallNate May 13, 2026
76e0024
[world-vercel] Add string-value assertions to run-id tests
TooTallNate May 13, 2026
cc1e0b8
[world-vercel] Remove internal-repo reference from regions doc comment
TooTallNate May 13, 2026
9147205
[world-vercel] Address PR review feedback on run-id sub-export
TooTallNate May 13, 2026
6efe0df
[world] [core] [world-vercel] Add World.createRunId() and region-awar…
TooTallNate May 13, 2026
d072229
[core] [world] [world-vercel] Pass full StartOptions to World.createR…
TooTallNate May 13, 2026
ef2993d
Merge remote-tracking branch 'origin/main' into world-create-run-id
VaguelySerious Jun 11, 2026
68feaa7
Address review feedback: doc fixes and deterministic same-ms tests
VaguelySerious Jun 11, 2026
162993f
Merge remote-tracking branch 'origin/main' into world-create-run-id
TooTallNate Jun 15, 2026
07d4718
test: keep workflow-server override rewrite-compatible
TooTallNate Jun 15, 2026
4f87069
fix(world): clear region tag bit before ULID timestamp validation
TooTallNate Jun 15, 2026
24282d8
fix(world-vercel): validate tagged runId timestamps via run-id decode
TooTallNate Jun 16, 2026
c8d65dc
fix(world-vercel): default run ID region to iad1 instead of unknown
TooTallNate Jun 16, 2026
bed8f7c
Merge remote-tracking branch 'origin/main' into world-create-run-id
TooTallNate Jun 17, 2026
f2b905d
test(e2e): use verbose reporter + per-test start heartbeat
TooTallNate Jun 17, 2026
8cc53c7
test(world-vercel): point WORKFLOW_SERVER_URL_OVERRIDE at combined 52…
TooTallNate Jun 17, 2026
05c98dc
Merge remote-tracking branch 'origin/main' into world-create-run-id
TooTallNate Jun 17, 2026
f29ee31
fix(core): region-tag the health-check correlationId
TooTallNate Jun 17, 2026
d84f83f
Merge remote-tracking branch 'origin/main' into world-create-run-id
VaguelySerious Jul 1, 2026
17197cd
Address review feedback: validate region overrides, reset server over…
VaguelySerious Jul 1, 2026
fc3e1f9
test(world-vercel): point WORKFLOW_SERVER_URL_OVERRIDE at wave-1 mult…
TooTallNate Jul 6, 2026
4480b37
Merge origin/main into world-create-run-id
TooTallNate Jul 6, 2026
ca0a65b
test(e2e): Vercel multi-region suite for start()'s region option
TooTallNate Jul 6, 2026
31e01af
test(e2e): start region probes in-function; fix getWorld await
TooTallNate Jul 6, 2026
b149741
Merge origin/main into world-create-run-id
TooTallNate Jul 6, 2026
450f4e9
test(e2e): cover explicit and implicit region starts in the multi-reg…
TooTallNate Jul 6, 2026
d933d96
Revert WORKFLOW_SERVER_URL_OVERRIDE to '' — wave-1 multi-region servi…
TooTallNate Jul 6, 2026
3d7c34b
test(e2e): cross-region stream visibility (iad1 writer, sfo1 reader)
TooTallNate Jul 7, 2026
3625089
test(e2e): extend multi-region suite to all 19 provisioned regions
TooTallNate Jul 8, 2026
5a936f1
test(e2e): tolerate geo-adjacent execution of queue callbacks
TooTallNate Jul 8, 2026
dff3f80
Revert WORKFLOW_SERVER_URL_OVERRIDE to '' — all-regions serving is in…
TooTallNate Jul 8, 2026
586c6e3
Merge origin/main into world-create-run-id
TooTallNate Jul 8, 2026
c42618f
docs: fix three stale comments flagged in review
TooTallNate Jul 8, 2026
fce2a8e
test(e2e): cover hook resolve/resume for runs owned by non-iad1 regions
TooTallNate Jul 9, 2026
4f77bc1
Merge origin/main into world-create-run-id
TooTallNate Jul 13, 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
6 changes: 6 additions & 0 deletions .changeset/core-run-id-input.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
---
"@workflow/core": minor
"workflow": minor
---

`start()` now delegates run ID generation to `world.createRunId(options)` when the world provides it, falling back to a monotonic ULID otherwise. The full options bag is passed through so worlds can read whichever fields they recognise. Adds a new `region` option that worlds may consume — when set, it is also forwarded onto the queue options so the initial workflow message is routed to the matching region.
5 changes: 5 additions & 0 deletions .changeset/world-create-run-id.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
---
"@workflow/world": minor
---

Add optional `createRunId(options?)` to the `World` interface and `region` to `QueueOptions`. Worlds can now mint custom run IDs (reading whichever start-option fields they recognise) and route messages to a specific region.
5 changes: 5 additions & 0 deletions .changeset/world-vercel-create-run-id.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
---
"@workflow/world-vercel": minor
---

Implement `World.createRunId` to mint region-tagged ULIDs, preferring an explicit `options.region` from `start()`, then the `VERCEL_REGION` environment variable, and finally defaulting to `iad1` so run IDs are always tagged with a concrete, routable region. The queue now routes each message to the region encoded in the payload's tagged run ID (or to an explicit `opts.region` override), instead of the previous hard-coded `iad1`.
97 changes: 92 additions & 5 deletions .github/workflows/tests.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -381,7 +381,7 @@ jobs:
run: echo "ms=$(($(date +%s) * 1000))" >> "$GITHUB_OUTPUT"

- name: Run E2E Tests
run: pnpm run test:e2e --reporter=default --reporter=json --reporter=./packages/core/e2e/github-reporter.ts "--outputFile=e2e-vercel-prod-$APP_NAME.json"
run: pnpm run test:e2e --reporter=verbose --reporter=json --reporter=./packages/core/e2e/github-reporter.ts "--outputFile=e2e-vercel-prod-$APP_NAME.json"
env:
NODE_OPTIONS: "--enable-source-maps"
DEPLOYMENT_URL: ${{ steps.waitForDeployment.outputs.deployment-url }}
Expand DownExpand Up@@ -436,6 +436,93 @@ jobs:
retention-days: 7
if-no-files-found: ignore

e2e-vercel-multi-region:
# Vercel-specific multi-region routing suite (start()'s `region`
# option). Deliberately separate from e2e-vercel-prod: that job runs
# the shared suite as a matrix across all worlds/frameworks, while
# this functionality only exists for @workflow/world-vercel and only
# the nextjs-turbopack workbench is deployed multi-region
# (workbench/nextjs-turbopack/vercel.json pins iad1+sfo1+fra1).
name: E2E Vercel Multi-Region Tests (nextjs-turbopack)
runs-on: ubuntu-latest
# 19 regions: the implicit-route cases pay one cold start each,
# sequentially.
timeout-minutes: 35
needs: ci-scope
if: ${{ needs.ci-scope.outputs.fast-path != 'true' }}
permissions:
id-token: write
contents: read
deployments: read
statuses: read
env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
WORKFLOW_PUBLIC_MANIFEST: '1'
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: Waiting for the Vercel deployment
id: waitForDeployment
uses: vercel/wait-for-deployment-action@0e2b0c5c5cce31f1648108aeec56467187aca037
with:
project-slug: "example-nextjs-workflow-turbopack"
timeout: 1000
check-interval: 15
environment: ${{ github.ref == 'refs/heads/main' && 'production' || 'preview' }}

- name: Run Multi-Region E2E Tests
run: pnpm vitest run packages/core/e2e/e2e-region.test.ts --reporter=verbose --reporter=json --reporter=./packages/core/e2e/github-reporter.ts "--outputFile=e2e-vercel-multi-region-$APP_NAME.json"
env:
NODE_OPTIONS: "--enable-source-maps"
DEPLOYMENT_URL: ${{ steps.waitForDeployment.outputs.deployment-url }}
VERCEL_DEPLOYMENT_ID: ${{ steps.waitForDeployment.outputs.deployment-id }}
APP_NAME: "nextjs-turbopack"
WORKFLOW_VERCEL_ENV: ${{ github.ref == 'refs/heads/main' && 'production' || 'preview' }}
WORKFLOW_VERCEL_AUTH_TOKEN: ${{ secrets.VERCEL_LABS_TOKEN }}
WORKFLOW_VERCEL_TEAM: "team_nO2mCG4W8IxPIeKoSsqwAxxB"
WORKFLOW_VERCEL_PROJECT: "prj_yjkM7UdHliv8bfxZ1sMJQf1pMpdi"
WORKFLOW_VERCEL_PROJECT_SLUG: "example-nextjs-workflow-turbopack"
# See the note on e2e-vercel-prod: PRs point at the protected
# workflow-server preview; unset on main for production.
VERCEL_WORKFLOW_SERVER_URL: ${{ github.ref != 'refs/heads/main' && secrets.VERCEL_WORKFLOW_SERVER_URL || '' }}

- name: Capture runtime logs on failure
if: failure()
env:
APP_NAME: "nextjs-turbopack"
WORKFLOW_VERCEL_AUTH_TOKEN: ${{ secrets.VERCEL_LABS_TOKEN }}
WORKFLOW_VERCEL_TEAM: "team_nO2mCG4W8IxPIeKoSsqwAxxB"
WORKFLOW_VERCEL_PROJECT: "prj_yjkM7UdHliv8bfxZ1sMJQf1pMpdi"
WORKFLOW_VERCEL_ENV: ${{ github.ref == 'refs/heads/main' && 'production' || 'preview' }}
VERCEL_DEPLOYMENT_ID: ${{ steps.waitForDeployment.outputs.deployment-id }}
run: node .github/scripts/fetch-e2e-runtime-logs.mjs

- name: Upload E2E results
if: always()
uses: actions/upload-artifact@v4
with:
name: e2e-results-vercel-multi-region-nextjs-turbopack
path: |
e2e-vercel-multi-region-nextjs-turbopack.json
e2e-metadata-nextjs-turbopack-vercel.json
e2e-failures-nextjs-turbopack-vercel.json
e2e-diagnostics-nextjs-turbopack-vercel.json
e2e-runtime-logs-nextjs-turbopack-vercel.json
retention-days: 7
if-no-files-found: ignore

getTestMatrix:
name: Get Test Matrix
runs-on: ubuntu-latest
Expand DownExpand Up@@ -525,7 +612,7 @@ jobs:
(cd "$WORKBENCH_APP_PATH" && pnpm dev 2>&1 | tee "$DEV_SERVER_LOG_PATH") &
echo "starting tests in 10 seconds" && sleep 10
pnpm vitest run packages/core/e2e/dev.test.ts; sleep 10
pnpm run test:e2e --reporter=default --reporter=json --reporter=./packages/core/e2e/github-reporter.ts --outputFile=e2e-local-dev-${{ matrix.app.name }}-${{ matrix.app.artifactSuffix }}.json
pnpm run test:e2e --reporter=verbose --reporter=json --reporter=./packages/core/e2e/github-reporter.ts --outputFile=e2e-local-dev-${{ matrix.app.name }}-${{ matrix.app.artifactSuffix }}.json
env:
NODE_OPTIONS: "--enable-source-maps"
APP_NAME: ${{ matrix.app.name }}
Expand DownExpand Up@@ -615,7 +702,7 @@ jobs:
export PROD_SERVER_LOG_PATH="$GITHUB_WORKSPACE/prod-server-${{ matrix.app.name }}-${{ matrix.app.artifactSuffix }}.log"
(cd "$WORKBENCH_APP_PATH" && pnpm start 2>&1 | tee "$PROD_SERVER_LOG_PATH") &
echo "starting tests in 10 seconds" && sleep 10
pnpm run test:e2e --reporter=default --reporter=json --reporter=./packages/core/e2e/github-reporter.ts --outputFile=e2e-local-prod-${{ matrix.app.name }}-${{ matrix.app.artifactSuffix }}.json
pnpm run test:e2e --reporter=verbose --reporter=json --reporter=./packages/core/e2e/github-reporter.ts --outputFile=e2e-local-prod-${{ matrix.app.name }}-${{ matrix.app.artifactSuffix }}.json
env:
NODE_OPTIONS: "--enable-source-maps"
APP_NAME: ${{ matrix.app.name }}
Expand DownExpand Up@@ -723,7 +810,7 @@ jobs:
export PROD_SERVER_LOG_PATH="$GITHUB_WORKSPACE/prod-server-postgres-${{ matrix.app.name }}-${{ matrix.app.artifactSuffix }}.log"
(cd "$WORKBENCH_APP_PATH" && pnpm start 2>&1 | tee "$PROD_SERVER_LOG_PATH") &
echo "starting tests in 10 seconds" && sleep 10
pnpm run test:e2e --reporter=default --reporter=json --reporter=./packages/core/e2e/github-reporter.ts --outputFile=e2e-local-postgres-${{ matrix.app.name }}-${{ matrix.app.artifactSuffix }}.json
pnpm run test:e2e --reporter=verbose --reporter=json --reporter=./packages/core/e2e/github-reporter.ts --outputFile=e2e-local-postgres-${{ matrix.app.name }}-${{ matrix.app.artifactSuffix }}.json
env:
NODE_OPTIONS: "--enable-source-maps"
APP_NAME: ${{ matrix.app.name }}
Expand DownExpand Up@@ -841,7 +928,7 @@ jobs:
exit 1
}

pnpm run test:e2e --reporter=default --reporter=json --reporter=./packages/core/e2e/github-reporter.ts --outputFile=e2e-windows-nextjs-turbopack.json
pnpm run test:e2e --reporter=verbose --reporter=json --reporter=./packages/core/e2e/github-reporter.ts --outputFile=e2e-windows-nextjs-turbopack.json
$e2eExit = $LASTEXITCODE
Stop-Job $job -ErrorAction SilentlyContinue
exit $e2eExit
Expand Down
8 changes: 8 additions & 0 deletions e2e-diagnostics-nextjs-turbopack-vercel.json
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
[
{
"testName": "WorkflowNotRegisteredError fails the run when workflow does not exist",
"runId": "wrun_41KV8YMWKC00RAQCRH320GSWR4",
"timestamp": "2026-06-16T19:31:07.845Z",
"dashboardUrl": "https://vercel.com/vercel-labs/example-nextjs-workflow-turbopack/observability/workflows/runs/wrun_41KV8YMWKC00RAQCRH320GSWR4?environment=preview"
}
]
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
28e9c8f
[world-vercel] Add /run-id sub-export with tagged ULID encode/decode
TooTallNate May 13, 2026
76e0024
[world-vercel] Add string-value assertions to run-id tests
TooTallNate May 13, 2026
cc1e0b8
[world-vercel] Remove internal-repo reference from regions doc comment
TooTallNate May 13, 2026
9147205
[world-vercel] Address PR review feedback on run-id sub-export
TooTallNate May 13, 2026
6efe0df
[world] [core] [world-vercel] Add World.createRunId() and region-awar…
TooTallNate May 13, 2026
d072229
[core] [world] [world-vercel] Pass full StartOptions to World.createR…
TooTallNate May 13, 2026
ef2993d
Merge remote-tracking branch 'origin/main' into world-create-run-id
VaguelySerious Jun 11, 2026
68feaa7
Address review feedback: doc fixes and deterministic same-ms tests
VaguelySerious Jun 11, 2026
162993f
Merge remote-tracking branch 'origin/main' into world-create-run-id
TooTallNate Jun 15, 2026
07d4718
test: keep workflow-server override rewrite-compatible
TooTallNate Jun 15, 2026
4f87069
fix(world): clear region tag bit before ULID timestamp validation
TooTallNate Jun 15, 2026
24282d8
fix(world-vercel): validate tagged runId timestamps via run-id decode
TooTallNate Jun 16, 2026
c8d65dc
fix(world-vercel): default run ID region to iad1 instead of unknown
TooTallNate Jun 16, 2026
bed8f7c
Merge remote-tracking branch 'origin/main' into world-create-run-id
TooTallNate Jun 17, 2026
f2b905d
test(e2e): use verbose reporter + per-test start heartbeat
TooTallNate Jun 17, 2026
8cc53c7
test(world-vercel): point WORKFLOW_SERVER_URL_OVERRIDE at combined 52…
TooTallNate Jun 17, 2026
05c98dc
Merge remote-tracking branch 'origin/main' into world-create-run-id
TooTallNate Jun 17, 2026
f29ee31
fix(core): region-tag the health-check correlationId
TooTallNate Jun 17, 2026
d84f83f
Merge remote-tracking branch 'origin/main' into world-create-run-id
VaguelySerious Jul 1, 2026
17197cd
Address review feedback: validate region overrides, reset server over…
VaguelySerious Jul 1, 2026
fc3e1f9
test(world-vercel): point WORKFLOW_SERVER_URL_OVERRIDE at wave-1 mult…
TooTallNate Jul 6, 2026
4480b37
Merge origin/main into world-create-run-id
TooTallNate Jul 6, 2026
ca0a65b
test(e2e): Vercel multi-region suite for start()'s region option
TooTallNate Jul 6, 2026
31e01af
test(e2e): start region probes in-function; fix getWorld await
TooTallNate Jul 6, 2026
b149741
Merge origin/main into world-create-run-id
TooTallNate Jul 6, 2026
450f4e9
test(e2e): cover explicit and implicit region starts in the multi-reg…
TooTallNate Jul 6, 2026
d933d96
Revert WORKFLOW_SERVER_URL_OVERRIDE to '' — wave-1 multi-region servi…
TooTallNate Jul 6, 2026
3d7c34b
test(e2e): cross-region stream visibility (iad1 writer, sfo1 reader)
TooTallNate Jul 7, 2026
3625089
test(e2e): extend multi-region suite to all 19 provisioned regions
TooTallNate Jul 8, 2026
5a936f1
test(e2e): tolerate geo-adjacent execution of queue callbacks
TooTallNate Jul 8, 2026
dff3f80
Revert WORKFLOW_SERVER_URL_OVERRIDE to '' — all-regions serving is in…
TooTallNate Jul 8, 2026
586c6e3
Merge origin/main into world-create-run-id
TooTallNate Jul 8, 2026
c42618f
docs: fix three stale comments flagged in review
TooTallNate Jul 8, 2026
fce2a8e
test(e2e): cover hook resolve/resume for runs owned by non-iad1 regions
TooTallNate Jul 9, 2026
4f77bc1
Merge origin/main into world-create-run-id
TooTallNate Jul 13, 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
6 changes: 6 additions & 0 deletions .changeset/core-run-id-input.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
---
"@workflow/core": minor
"workflow": minor
---

`start()` now delegates run ID generation to `world.createRunId(options)` when the world provides it, falling back to a monotonic ULID otherwise. The full options bag is passed through so worlds can read whichever fields they recognise. Adds a new `region` option that worlds may consume — when set, it is also forwarded onto the queue options so the initial workflow message is routed to the matching region.
5 changes: 5 additions & 0 deletions .changeset/world-create-run-id.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
---
"@workflow/world": minor
---

Add optional `createRunId(options?)` to the `World` interface and `region` to `QueueOptions`. Worlds can now mint custom run IDs (reading whichever start-option fields they recognise) and route messages to a specific region.
5 changes: 5 additions & 0 deletions .changeset/world-vercel-create-run-id.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
---
"@workflow/world-vercel": minor
---

Implement `World.createRunId` to mint region-tagged ULIDs, preferring an explicit `options.region` from `start()`, then the `VERCEL_REGION` environment variable, and finally defaulting to `iad1` so run IDs are always tagged with a concrete, routable region. The queue now routes each message to the region encoded in the payload's tagged run ID (or to an explicit `opts.region` override), instead of the previous hard-coded `iad1`.
97 changes: 92 additions & 5 deletions .github/workflows/tests.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -381,7 +381,7 @@ jobs:
run: echo "ms=$(($(date +%s) * 1000))" >> "$GITHUB_OUTPUT"

- name: Run E2E Tests
run: pnpm run test:e2e --reporter=default --reporter=json --reporter=./packages/core/e2e/github-reporter.ts "--outputFile=e2e-vercel-prod-$APP_NAME.json"
run: pnpm run test:e2e --reporter=verbose --reporter=json --reporter=./packages/core/e2e/github-reporter.ts "--outputFile=e2e-vercel-prod-$APP_NAME.json"
env:
NODE_OPTIONS: "--enable-source-maps"
DEPLOYMENT_URL: ${{ steps.waitForDeployment.outputs.deployment-url }}
Expand DownExpand Up@@ -436,6 +436,93 @@ jobs:
retention-days: 7
if-no-files-found: ignore

e2e-vercel-multi-region:
# Vercel-specific multi-region routing suite (start()'s `region`
# option). Deliberately separate from e2e-vercel-prod: that job runs
# the shared suite as a matrix across all worlds/frameworks, while
# this functionality only exists for @workflow/world-vercel and only
# the nextjs-turbopack workbench is deployed multi-region
# (workbench/nextjs-turbopack/vercel.json pins iad1+sfo1+fra1).
name: E2E Vercel Multi-Region Tests (nextjs-turbopack)
runs-on: ubuntu-latest
# 19 regions: the implicit-route cases pay one cold start each,
# sequentially.
timeout-minutes: 35
needs: ci-scope
if: ${{ needs.ci-scope.outputs.fast-path != 'true' }}
permissions:
id-token: write
contents: read
deployments: read
statuses: read
env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
WORKFLOW_PUBLIC_MANIFEST: '1'
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: Waiting for the Vercel deployment
id: waitForDeployment
uses: vercel/wait-for-deployment-action@0e2b0c5c5cce31f1648108aeec56467187aca037
with:
project-slug: "example-nextjs-workflow-turbopack"
timeout: 1000
check-interval: 15
environment: ${{ github.ref == 'refs/heads/main' && 'production' || 'preview' }}

- name: Run Multi-Region E2E Tests
run: pnpm vitest run packages/core/e2e/e2e-region.test.ts --reporter=verbose --reporter=json --reporter=./packages/core/e2e/github-reporter.ts "--outputFile=e2e-vercel-multi-region-$APP_NAME.json"
env:
NODE_OPTIONS: "--enable-source-maps"
DEPLOYMENT_URL: ${{ steps.waitForDeployment.outputs.deployment-url }}
VERCEL_DEPLOYMENT_ID: ${{ steps.waitForDeployment.outputs.deployment-id }}
APP_NAME: "nextjs-turbopack"
WORKFLOW_VERCEL_ENV: ${{ github.ref == 'refs/heads/main' && 'production' || 'preview' }}
WORKFLOW_VERCEL_AUTH_TOKEN: ${{ secrets.VERCEL_LABS_TOKEN }}
WORKFLOW_VERCEL_TEAM: "team_nO2mCG4W8IxPIeKoSsqwAxxB"
WORKFLOW_VERCEL_PROJECT: "prj_yjkM7UdHliv8bfxZ1sMJQf1pMpdi"
WORKFLOW_VERCEL_PROJECT_SLUG: "example-nextjs-workflow-turbopack"
# See the note on e2e-vercel-prod: PRs point at the protected
# workflow-server preview; unset on main for production.
VERCEL_WORKFLOW_SERVER_URL: ${{ github.ref != 'refs/heads/main' && secrets.VERCEL_WORKFLOW_SERVER_URL || '' }}

- name: Capture runtime logs on failure
if: failure()
env:
APP_NAME: "nextjs-turbopack"
WORKFLOW_VERCEL_AUTH_TOKEN: ${{ secrets.VERCEL_LABS_TOKEN }}
WORKFLOW_VERCEL_TEAM: "team_nO2mCG4W8IxPIeKoSsqwAxxB"
WORKFLOW_VERCEL_PROJECT: "prj_yjkM7UdHliv8bfxZ1sMJQf1pMpdi"
WORKFLOW_VERCEL_ENV: ${{ github.ref == 'refs/heads/main' && 'production' || 'preview' }}
VERCEL_DEPLOYMENT_ID: ${{ steps.waitForDeployment.outputs.deployment-id }}
run: node .github/scripts/fetch-e2e-runtime-logs.mjs

- name: Upload E2E results
if: always()
uses: actions/upload-artifact@v4
with:
name: e2e-results-vercel-multi-region-nextjs-turbopack
path: |
e2e-vercel-multi-region-nextjs-turbopack.json
e2e-metadata-nextjs-turbopack-vercel.json
e2e-failures-nextjs-turbopack-vercel.json
e2e-diagnostics-nextjs-turbopack-vercel.json
e2e-runtime-logs-nextjs-turbopack-vercel.json
retention-days: 7
if-no-files-found: ignore

getTestMatrix:
name: Get Test Matrix
runs-on: ubuntu-latest
Expand DownExpand Up@@ -525,7 +612,7 @@ jobs:
(cd "$WORKBENCH_APP_PATH" && pnpm dev 2>&1 | tee "$DEV_SERVER_LOG_PATH") &
echo "starting tests in 10 seconds" && sleep 10
pnpm vitest run packages/core/e2e/dev.test.ts; sleep 10
pnpm run test:e2e --reporter=default --reporter=json --reporter=./packages/core/e2e/github-reporter.ts --outputFile=e2e-local-dev-${{ matrix.app.name }}-${{ matrix.app.artifactSuffix }}.json
pnpm run test:e2e --reporter=verbose --reporter=json --reporter=./packages/core/e2e/github-reporter.ts --outputFile=e2e-local-dev-${{ matrix.app.name }}-${{ matrix.app.artifactSuffix }}.json
env:
NODE_OPTIONS: "--enable-source-maps"
APP_NAME: ${{ matrix.app.name }}
Expand DownExpand Up@@ -615,7 +702,7 @@ jobs:
export PROD_SERVER_LOG_PATH="$GITHUB_WORKSPACE/prod-server-${{ matrix.app.name }}-${{ matrix.app.artifactSuffix }}.log"
(cd "$WORKBENCH_APP_PATH" && pnpm start 2>&1 | tee "$PROD_SERVER_LOG_PATH") &
echo "starting tests in 10 seconds" && sleep 10
pnpm run test:e2e --reporter=default --reporter=json --reporter=./packages/core/e2e/github-reporter.ts --outputFile=e2e-local-prod-${{ matrix.app.name }}-${{ matrix.app.artifactSuffix }}.json
pnpm run test:e2e --reporter=verbose --reporter=json --reporter=./packages/core/e2e/github-reporter.ts --outputFile=e2e-local-prod-${{ matrix.app.name }}-${{ matrix.app.artifactSuffix }}.json
env:
NODE_OPTIONS: "--enable-source-maps"
APP_NAME: ${{ matrix.app.name }}
Expand DownExpand Up@@ -723,7 +810,7 @@ jobs:
export PROD_SERVER_LOG_PATH="$GITHUB_WORKSPACE/prod-server-postgres-${{ matrix.app.name }}-${{ matrix.app.artifactSuffix }}.log"
(cd "$WORKBENCH_APP_PATH" && pnpm start 2>&1 | tee "$PROD_SERVER_LOG_PATH") &
echo "starting tests in 10 seconds" && sleep 10
pnpm run test:e2e --reporter=default --reporter=json --reporter=./packages/core/e2e/github-reporter.ts --outputFile=e2e-local-postgres-${{ matrix.app.name }}-${{ matrix.app.artifactSuffix }}.json
pnpm run test:e2e --reporter=verbose --reporter=json --reporter=./packages/core/e2e/github-reporter.ts --outputFile=e2e-local-postgres-${{ matrix.app.name }}-${{ matrix.app.artifactSuffix }}.json
env:
NODE_OPTIONS: "--enable-source-maps"
APP_NAME: ${{ matrix.app.name }}
Expand DownExpand Up@@ -841,7 +928,7 @@ jobs:
exit 1
}

pnpm run test:e2e --reporter=default --reporter=json --reporter=./packages/core/e2e/github-reporter.ts --outputFile=e2e-windows-nextjs-turbopack.json
pnpm run test:e2e --reporter=verbose --reporter=json --reporter=./packages/core/e2e/github-reporter.ts --outputFile=e2e-windows-nextjs-turbopack.json
$e2eExit = $LASTEXITCODE
Stop-Job $job -ErrorAction SilentlyContinue
exit $e2eExit
Expand Down
8 changes: 8 additions & 0 deletions e2e-diagnostics-nextjs-turbopack-vercel.json
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
[
{
"testName": "WorkflowNotRegisteredError fails the run when workflow does not exist",
"runId": "wrun_41KV8YMWKC00RAQCRH320GSWR4",
"timestamp": "2026-06-16T19:31:07.845Z",
"dashboardUrl": "https://vercel.com/vercel-labs/example-nextjs-workflow-turbopack/observability/workflows/runs/wrun_41KV8YMWKC00RAQCRH320GSWR4?environment=preview"
}
]
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
28e9c8f
[world-vercel] Add /run-id sub-export with tagged ULID encode/decode
TooTallNate May 13, 2026
76e0024
[world-vercel] Add string-value assertions to run-id tests
TooTallNate May 13, 2026
cc1e0b8
[world-vercel] Remove internal-repo reference from regions doc comment
TooTallNate May 13, 2026
9147205
[world-vercel] Address PR review feedback on run-id sub-export
TooTallNate May 13, 2026
6efe0df
[world] [core] [world-vercel] Add World.createRunId() and region-awar…
TooTallNate May 13, 2026
d072229
[core] [world] [world-vercel] Pass full StartOptions to World.createR…
TooTallNate May 13, 2026
ef2993d
Merge remote-tracking branch 'origin/main' into world-create-run-id
VaguelySerious Jun 11, 2026
68feaa7
Address review feedback: doc fixes and deterministic same-ms tests
VaguelySerious Jun 11, 2026
162993f
Merge remote-tracking branch 'origin/main' into world-create-run-id
TooTallNate Jun 15, 2026
07d4718
test: keep workflow-server override rewrite-compatible
TooTallNate Jun 15, 2026
4f87069
fix(world): clear region tag bit before ULID timestamp validation
TooTallNate Jun 15, 2026
24282d8
fix(world-vercel): validate tagged runId timestamps via run-id decode
TooTallNate Jun 16, 2026
c8d65dc
fix(world-vercel): default run ID region to iad1 instead of unknown
TooTallNate Jun 16, 2026
bed8f7c
Merge remote-tracking branch 'origin/main' into world-create-run-id
TooTallNate Jun 17, 2026
f2b905d
test(e2e): use verbose reporter + per-test start heartbeat
TooTallNate Jun 17, 2026
8cc53c7
test(world-vercel): point WORKFLOW_SERVER_URL_OVERRIDE at combined 52…
TooTallNate Jun 17, 2026
05c98dc
Merge remote-tracking branch 'origin/main' into world-create-run-id
TooTallNate Jun 17, 2026
f29ee31
fix(core): region-tag the health-check correlationId
TooTallNate Jun 17, 2026
d84f83f
Merge remote-tracking branch 'origin/main' into world-create-run-id
VaguelySerious Jul 1, 2026
17197cd
Address review feedback: validate region overrides, reset server over…
VaguelySerious Jul 1, 2026
fc3e1f9
test(world-vercel): point WORKFLOW_SERVER_URL_OVERRIDE at wave-1 mult…
TooTallNate Jul 6, 2026
4480b37
Merge origin/main into world-create-run-id
TooTallNate Jul 6, 2026
ca0a65b
test(e2e): Vercel multi-region suite for start()'s region option
TooTallNate Jul 6, 2026
31e01af
test(e2e): start region probes in-function; fix getWorld await
TooTallNate Jul 6, 2026
b149741
Merge origin/main into world-create-run-id
TooTallNate Jul 6, 2026
450f4e9
test(e2e): cover explicit and implicit region starts in the multi-reg…
TooTallNate Jul 6, 2026
d933d96
Revert WORKFLOW_SERVER_URL_OVERRIDE to '' — wave-1 multi-region servi…
TooTallNate Jul 6, 2026
3d7c34b
test(e2e): cross-region stream visibility (iad1 writer, sfo1 reader)
TooTallNate Jul 7, 2026
3625089
test(e2e): extend multi-region suite to all 19 provisioned regions
TooTallNate Jul 8, 2026
5a936f1
test(e2e): tolerate geo-adjacent execution of queue callbacks
TooTallNate Jul 8, 2026
dff3f80
Revert WORKFLOW_SERVER_URL_OVERRIDE to '' — all-regions serving is in…
TooTallNate Jul 8, 2026
586c6e3
Merge origin/main into world-create-run-id
TooTallNate Jul 8, 2026
c42618f
docs: fix three stale comments flagged in review
TooTallNate Jul 8, 2026
fce2a8e
test(e2e): cover hook resolve/resume for runs owned by non-iad1 regions
TooTallNate Jul 9, 2026
4f77bc1
Merge origin/main into world-create-run-id
TooTallNate Jul 13, 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
6 changes: 6 additions & 0 deletions .changeset/core-run-id-input.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
---
"@workflow/core": minor
"workflow": minor
---

`start()` now delegates run ID generation to `world.createRunId(options)` when the world provides it, falling back to a monotonic ULID otherwise. The full options bag is passed through so worlds can read whichever fields they recognise. Adds a new `region` option that worlds may consume — when set, it is also forwarded onto the queue options so the initial workflow message is routed to the matching region.
5 changes: 5 additions & 0 deletions .changeset/world-create-run-id.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
---
"@workflow/world": minor
---

Add optional `createRunId(options?)` to the `World` interface and `region` to `QueueOptions`. Worlds can now mint custom run IDs (reading whichever start-option fields they recognise) and route messages to a specific region.
5 changes: 5 additions & 0 deletions .changeset/world-vercel-create-run-id.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
---
"@workflow/world-vercel": minor
---

Implement `World.createRunId` to mint region-tagged ULIDs, preferring an explicit `options.region` from `start()`, then the `VERCEL_REGION` environment variable, and finally defaulting to `iad1` so run IDs are always tagged with a concrete, routable region. The queue now routes each message to the region encoded in the payload's tagged run ID (or to an explicit `opts.region` override), instead of the previous hard-coded `iad1`.
97 changes: 92 additions & 5 deletions .github/workflows/tests.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -381,7 +381,7 @@ jobs:
run: echo "ms=$(($(date +%s) * 1000))" >> "$GITHUB_OUTPUT"

- name: Run E2E Tests
run: pnpm run test:e2e --reporter=default --reporter=json --reporter=./packages/core/e2e/github-reporter.ts "--outputFile=e2e-vercel-prod-$APP_NAME.json"
run: pnpm run test:e2e --reporter=verbose --reporter=json --reporter=./packages/core/e2e/github-reporter.ts "--outputFile=e2e-vercel-prod-$APP_NAME.json"
env:
NODE_OPTIONS: "--enable-source-maps"
DEPLOYMENT_URL: ${{ steps.waitForDeployment.outputs.deployment-url }}
Expand DownExpand Up@@ -436,6 +436,93 @@ jobs:
retention-days: 7
if-no-files-found: ignore

e2e-vercel-multi-region:
# Vercel-specific multi-region routing suite (start()'s `region`
# option). Deliberately separate from e2e-vercel-prod: that job runs
# the shared suite as a matrix across all worlds/frameworks, while
# this functionality only exists for @workflow/world-vercel and only
# the nextjs-turbopack workbench is deployed multi-region
# (workbench/nextjs-turbopack/vercel.json pins iad1+sfo1+fra1).
name: E2E Vercel Multi-Region Tests (nextjs-turbopack)
runs-on: ubuntu-latest
# 19 regions: the implicit-route cases pay one cold start each,
# sequentially.
timeout-minutes: 35
needs: ci-scope
if: ${{ needs.ci-scope.outputs.fast-path != 'true' }}
permissions:
id-token: write
contents: read
deployments: read
statuses: read
env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
WORKFLOW_PUBLIC_MANIFEST: '1'
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: Waiting for the Vercel deployment
id: waitForDeployment
uses: vercel/wait-for-deployment-action@0e2b0c5c5cce31f1648108aeec56467187aca037
with:
project-slug: "example-nextjs-workflow-turbopack"
timeout: 1000
check-interval: 15
environment: ${{ github.ref == 'refs/heads/main' && 'production' || 'preview' }}

- name: Run Multi-Region E2E Tests
run: pnpm vitest run packages/core/e2e/e2e-region.test.ts --reporter=verbose --reporter=json --reporter=./packages/core/e2e/github-reporter.ts "--outputFile=e2e-vercel-multi-region-$APP_NAME.json"
env:
NODE_OPTIONS: "--enable-source-maps"
DEPLOYMENT_URL: ${{ steps.waitForDeployment.outputs.deployment-url }}
VERCEL_DEPLOYMENT_ID: ${{ steps.waitForDeployment.outputs.deployment-id }}
APP_NAME: "nextjs-turbopack"
WORKFLOW_VERCEL_ENV: ${{ github.ref == 'refs/heads/main' && 'production' || 'preview' }}
WORKFLOW_VERCEL_AUTH_TOKEN: ${{ secrets.VERCEL_LABS_TOKEN }}
WORKFLOW_VERCEL_TEAM: "team_nO2mCG4W8IxPIeKoSsqwAxxB"
WORKFLOW_VERCEL_PROJECT: "prj_yjkM7UdHliv8bfxZ1sMJQf1pMpdi"
WORKFLOW_VERCEL_PROJECT_SLUG: "example-nextjs-workflow-turbopack"
# See the note on e2e-vercel-prod: PRs point at the protected
# workflow-server preview; unset on main for production.
VERCEL_WORKFLOW_SERVER_URL: ${{ github.ref != 'refs/heads/main' && secrets.VERCEL_WORKFLOW_SERVER_URL || '' }}

- name: Capture runtime logs on failure
if: failure()
env:
APP_NAME: "nextjs-turbopack"
WORKFLOW_VERCEL_AUTH_TOKEN: ${{ secrets.VERCEL_LABS_TOKEN }}
WORKFLOW_VERCEL_TEAM: "team_nO2mCG4W8IxPIeKoSsqwAxxB"
WORKFLOW_VERCEL_PROJECT: "prj_yjkM7UdHliv8bfxZ1sMJQf1pMpdi"
WORKFLOW_VERCEL_ENV: ${{ github.ref == 'refs/heads/main' && 'production' || 'preview' }}
VERCEL_DEPLOYMENT_ID: ${{ steps.waitForDeployment.outputs.deployment-id }}
run: node .github/scripts/fetch-e2e-runtime-logs.mjs

- name: Upload E2E results
if: always()
uses: actions/upload-artifact@v4
with:
name: e2e-results-vercel-multi-region-nextjs-turbopack
path: |
e2e-vercel-multi-region-nextjs-turbopack.json
e2e-metadata-nextjs-turbopack-vercel.json
e2e-failures-nextjs-turbopack-vercel.json
e2e-diagnostics-nextjs-turbopack-vercel.json
e2e-runtime-logs-nextjs-turbopack-vercel.json
retention-days: 7
if-no-files-found: ignore

getTestMatrix:
name: Get Test Matrix
runs-on: ubuntu-latest
Expand DownExpand Up@@ -525,7 +612,7 @@ jobs:
(cd "$WORKBENCH_APP_PATH" && pnpm dev 2>&1 | tee "$DEV_SERVER_LOG_PATH") &
echo "starting tests in 10 seconds" && sleep 10
pnpm vitest run packages/core/e2e/dev.test.ts; sleep 10
pnpm run test:e2e --reporter=default --reporter=json --reporter=./packages/core/e2e/github-reporter.ts --outputFile=e2e-local-dev-${{ matrix.app.name }}-${{ matrix.app.artifactSuffix }}.json
pnpm run test:e2e --reporter=verbose --reporter=json --reporter=./packages/core/e2e/github-reporter.ts --outputFile=e2e-local-dev-${{ matrix.app.name }}-${{ matrix.app.artifactSuffix }}.json
env:
NODE_OPTIONS: "--enable-source-maps"
APP_NAME: ${{ matrix.app.name }}
Expand DownExpand Up@@ -615,7 +702,7 @@ jobs:
export PROD_SERVER_LOG_PATH="$GITHUB_WORKSPACE/prod-server-${{ matrix.app.name }}-${{ matrix.app.artifactSuffix }}.log"
(cd "$WORKBENCH_APP_PATH" && pnpm start 2>&1 | tee "$PROD_SERVER_LOG_PATH") &
echo "starting tests in 10 seconds" && sleep 10
pnpm run test:e2e --reporter=default --reporter=json --reporter=./packages/core/e2e/github-reporter.ts --outputFile=e2e-local-prod-${{ matrix.app.name }}-${{ matrix.app.artifactSuffix }}.json
pnpm run test:e2e --reporter=verbose --reporter=json --reporter=./packages/core/e2e/github-reporter.ts --outputFile=e2e-local-prod-${{ matrix.app.name }}-${{ matrix.app.artifactSuffix }}.json
env:
NODE_OPTIONS: "--enable-source-maps"
APP_NAME: ${{ matrix.app.name }}
Expand DownExpand Up@@ -723,7 +810,7 @@ jobs:
export PROD_SERVER_LOG_PATH="$GITHUB_WORKSPACE/prod-server-postgres-${{ matrix.app.name }}-${{ matrix.app.artifactSuffix }}.log"
(cd "$WORKBENCH_APP_PATH" && pnpm start 2>&1 | tee "$PROD_SERVER_LOG_PATH") &
echo "starting tests in 10 seconds" && sleep 10
pnpm run test:e2e --reporter=default --reporter=json --reporter=./packages/core/e2e/github-reporter.ts --outputFile=e2e-local-postgres-${{ matrix.app.name }}-${{ matrix.app.artifactSuffix }}.json
pnpm run test:e2e --reporter=verbose --reporter=json --reporter=./packages/core/e2e/github-reporter.ts --outputFile=e2e-local-postgres-${{ matrix.app.name }}-${{ matrix.app.artifactSuffix }}.json
env:
NODE_OPTIONS: "--enable-source-maps"
APP_NAME: ${{ matrix.app.name }}
Expand DownExpand Up@@ -841,7 +928,7 @@ jobs:
exit 1
}

pnpm run test:e2e --reporter=default --reporter=json --reporter=./packages/core/e2e/github-reporter.ts --outputFile=e2e-windows-nextjs-turbopack.json
pnpm run test:e2e --reporter=verbose --reporter=json --reporter=./packages/core/e2e/github-reporter.ts --outputFile=e2e-windows-nextjs-turbopack.json
$e2eExit = $LASTEXITCODE
Stop-Job $job -ErrorAction SilentlyContinue
exit $e2eExit
Expand Down
8 changes: 8 additions & 0 deletions e2e-diagnostics-nextjs-turbopack-vercel.json
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
[
{
"testName": "WorkflowNotRegisteredError fails the run when workflow does not exist",
"runId": "wrun_41KV8YMWKC00RAQCRH320GSWR4",
"timestamp": "2026-06-16T19:31:07.845Z",
"dashboardUrl": "https://vercel.com/vercel-labs/example-nextjs-workflow-turbopack/observability/workflows/runs/wrun_41KV8YMWKC00RAQCRH320GSWR4?environment=preview"
}
]
Loading
Loading