Skip to content

feat(world-vercel): support Hook minimum retention - #3286

Merged
NathanColosimo merged 2 commits into
mainfrom
codex/hook-retention-vercel
Aug 7, 2026
Merged

feat(world-vercel): support Hook minimum retention#3286
NathanColosimo merged 2 commits into
mainfrom
codex/hook-retention-vercel

Conversation

@NathanColosimo

@NathanColosimo NathanColosimo commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

The backend PR must be merged and deployed first

Summary

  • advertise Hook minimum-retention support from the Vercel World
  • run the existing Hook-retention end-to-end case against Vercel deployments
  • document the Vercel World's fixed 30-day maximum
  • turn deterministic World validation failures into FatalError so workflows do not retry invalid Hook requests

The SDK remains unaware of the 30-day limit. workflow-server enforces it at the event-persistence boundary and returns HTTP 400. Core recognizes that deterministic response, including the existing Local World validation error, and stops the workflow instead of retrying it.

Limits

  • every Vercel project may request up to 30 days
  • the limit does not depend on plan or Observability Plus
  • requests beyond 30 days fail before event payload upload or entity materialization

The wire field already flows through world-vercel. The capability is enabled only after the backend implements and deploys the storage semantics.

Stack

Verification

  • @workflow/core build passed
  • focused suspension-handler tests: 19 passed
  • dependency build for the Local, Postgres, and Vercel World stack passed
  • world-vercel TypeScript check passed
  • all 39 world-vercel event tests passed
  • workflow-server production build and TypeScript check passed
  • focused V2 and V4 backend integration tests passed against the rebuilt server

Docs Preview

Page Preview
createHook() minimum retention v5
Vercel World limitation v5

@vercel

vercel Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

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

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

@changeset-bot

changeset-bot Bot commented Aug 2, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 4a912fa

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

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

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

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

@github-actions

github-actions Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

All tests passed

E2E Test Summary

Summary
Passed Failed Skipped Total
✅ ▲ Vercel Production 2954 0 478 3432
✅ 💻 Local Development 3290 0 454 3744
✅ 📦 Local Production 3290 0 454 3744
✅ 🐘 Local Postgres 3290 0 454 3744
✅ 🪟 Windows 312 0 0 312
✅ 📋 Other 2072 0 424 2496
✅ vercel-multi-region 27 0 0 27
Total 15235 0 2264 17499
Details by Category

✅ ▲ Vercel Production

App Passed Failed Skipped
✅ astro-node 128 0 28
✅ astro-quickjs 128 0 28
✅ example-node 128 0 28
✅ example-quickjs 128 0 28
✅ express-node 128 0 28
✅ express-quickjs 128 0 28
✅ fastify-node 128 0 28
✅ fastify-quickjs 128 0 28
✅ hono-node 128 0 28
✅ hono-quickjs 128 0 28
✅ nextjs-turbopack-node 153 0 3
✅ nextjs-turbopack-quickjs 153 0 3
✅ nextjs-webpack-node 153 0 3
✅ nextjs-webpack-quickjs 153 0 3
✅ nitro-node 128 0 28
✅ nitro-quickjs 128 0 28
✅ nuxt-node 128 0 28
✅ nuxt-quickjs 128 0 28
✅ sveltekit-node 147 0 9
✅ sveltekit-quickjs 147 0 9
✅ vite-node 128 0 28
✅ vite-quickjs 128 0 28

✅ 💻 Local Development

App Passed Failed Skipped
✅ astro-stable-node 130 0 26
✅ astro-stable-quickjs 130 0 26
✅ express-stable-node 130 0 26
✅ express-stable-quickjs 130 0 26
✅ fastify-stable-node 130 0 26
✅ fastify-stable-quickjs 130 0 26
✅ hono-stable-node 130 0 26
✅ hono-stable-quickjs 130 0 26
✅ nextjs-turbopack-canary-node 137 0 19
✅ nextjs-turbopack-canary-quickjs 137 0 19
✅ nextjs-turbopack-stable-node 156 0 0
✅ nextjs-turbopack-stable-quickjs 156 0 0
✅ nextjs-webpack-canary-node 137 0 19
✅ nextjs-webpack-canary-quickjs 137 0 19
✅ nextjs-webpack-stable-node 156 0 0
✅ nextjs-webpack-stable-quickjs 156 0 0
✅ nitro-stable-node 130 0 26
✅ nitro-stable-quickjs 130 0 26
✅ nuxt-stable-node 130 0 26
✅ nuxt-stable-quickjs 130 0 26
✅ sveltekit-stable-node 149 0 7
✅ sveltekit-stable-quickjs 149 0 7
✅ vite-stable-node 130 0 26
✅ vite-stable-quickjs 130 0 26

✅ 📦 Local Production

App Passed Failed Skipped
✅ astro-stable-node 130 0 26
✅ astro-stable-quickjs 130 0 26
✅ express-stable-node 130 0 26
✅ express-stable-quickjs 130 0 26
✅ fastify-stable-node 130 0 26
✅ fastify-stable-quickjs 130 0 26
✅ hono-stable-node 130 0 26
✅ hono-stable-quickjs 130 0 26
✅ nextjs-turbopack-canary-node 137 0 19
✅ nextjs-turbopack-canary-quickjs 137 0 19
✅ nextjs-turbopack-stable-node 156 0 0
✅ nextjs-turbopack-stable-quickjs 156 0 0
✅ nextjs-webpack-canary-node 137 0 19
✅ nextjs-webpack-canary-quickjs 137 0 19
✅ nextjs-webpack-stable-node 156 0 0
✅ nextjs-webpack-stable-quickjs 156 0 0
✅ nitro-stable-node 130 0 26
✅ nitro-stable-quickjs 130 0 26
✅ nuxt-stable-node 130 0 26
✅ nuxt-stable-quickjs 130 0 26
✅ sveltekit-stable-node 149 0 7
✅ sveltekit-stable-quickjs 149 0 7
✅ vite-stable-node 130 0 26
✅ vite-stable-quickjs 130 0 26

✅ 🐘 Local Postgres

App Passed Failed Skipped
✅ astro-stable-node 130 0 26
✅ astro-stable-quickjs 130 0 26
✅ express-stable-node 130 0 26
✅ express-stable-quickjs 130 0 26
✅ fastify-stable-node 130 0 26
✅ fastify-stable-quickjs 130 0 26
✅ hono-stable-node 130 0 26
✅ hono-stable-quickjs 130 0 26
✅ nextjs-turbopack-canary-node 137 0 19
✅ nextjs-turbopack-canary-quickjs 137 0 19
✅ nextjs-turbopack-stable-node 156 0 0
✅ nextjs-turbopack-stable-quickjs 156 0 0
✅ nextjs-webpack-canary-node 137 0 19
✅ nextjs-webpack-canary-quickjs 137 0 19
✅ nextjs-webpack-stable-node 156 0 0
✅ nextjs-webpack-stable-quickjs 156 0 0
✅ nitro-stable-node 130 0 26
✅ nitro-stable-quickjs 130 0 26
✅ nuxt-stable-node 130 0 26
✅ nuxt-stable-quickjs 130 0 26
✅ sveltekit-stable-node 149 0 7
✅ sveltekit-stable-quickjs 149 0 7
✅ vite-stable-node 130 0 26
✅ vite-stable-quickjs 130 0 26

✅ 🪟 Windows

App Passed Failed Skipped
✅ nextjs-turbopack-node 156 0 0
✅ nextjs-turbopack-quickjs 156 0 0

✅ 📋 Other

App Passed Failed Skipped
✅ e2e-local-dev-nest-stable-node 130 0 26
✅ e2e-local-dev-nest-stable-quickjs 130 0 26
✅ e2e-local-dev-tanstack-start-node 130 0 26
✅ e2e-local-dev-tanstack-start-quickjs 130 0 26
✅ e2e-local-postgres-nest-stable-node 130 0 26
✅ e2e-local-postgres-nest-stable-quickjs 130 0 26
✅ e2e-local-postgres-tanstack-start-node 130 0 26
✅ e2e-local-postgres-tanstack-start-quickjs 130 0 26
✅ e2e-local-prod-nest-stable-node 130 0 26
✅ e2e-local-prod-nest-stable-quickjs 130 0 26
✅ e2e-local-prod-tanstack-start-node 130 0 26
✅ e2e-local-prod-tanstack-start-quickjs 130 0 26
✅ e2e-vercel-prod-nest-node 128 0 28
✅ e2e-vercel-prod-nest-quickjs 128 0 28
✅ e2e-vercel-prod-tanstack-start-node 128 0 28
✅ e2e-vercel-prod-tanstack-start-quickjs 128 0 28

✅ vercel-multi-region

App Passed Failed Skipped
✅ nextjs-turbopack 27 0 0

📋 View full workflow run

@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

📊 Workflow Benchmarks

commit 0b1dc5b · Wed, 05 Aug 2026 22:37:22 GMT · run logs

Backend: vercel · app: nextjs-turbopack

Metric Scenario Best (ms) P75 (ms) P90 (ms) P99 (ms) Samples
TTFS step 1239 (+61%) 🔻 1321 🔴 (+35%) 🔻 1361 🔴 (+29%) 🔻 1556 🔴 (-7.2%) 30
TTFS stream 335 (-63%) 💚 1296 🔴 (+33%) 🔻 1330 🔴 (+34%) 🔻 1399 🔴 (+8.0%) 30
TTFS hook + stream 1489 (+71%) 🔻 1597 🔴 (+29%) 🔻 1640 🔴 (+30%) 🔻 1999 🔴 (+34%) 🔻 30
STSO 1020 steps (inline) 92 (+2.2%) 122 (-6.9%) 139 (-11%) 206 (-17%) 💚 1018
STSO 1020 steps (queue-hop) 3207 3207 3207 3207 1
WO 1020 steps 124846 (-3.8%) 124846 (-3.8%) 124846 (-3.8%) 124846 (-3.8%) 1
SL stream latency 94 (+16%) 🔻 160 🔴 (+29%) 🔻 206 🔴 (+37%) 🔻 629 🔴 (+53%) 🔻 30
SO stream overhead (text) 103 (+1.0%) 164 (+4.5%) 188 (+3.3%) 234 (+18%) 🔻 30
SO stream overhead (structured) 110 (+6.8%) 147 (-10%) 168 (-5.6%) 206 (-35%) 💚 30
📈 STSO distribution vs main (inline / queue-hop histograms)

1020 steps (inline)

Cumulative STSO time: main 128734ms → this run 120344ms (Δ -8390ms, -7%)

 50-100 ms  ┃                         main   4  this  34   +30
100-150 ms  ███████████████████████┃  main 896  this 921   +25
150-200 ms  ┃█                        main  93  this  51   -42
200-250 ms  ┃                         main  16  this   6   -10
250-300 ms  ┃                         main   3  this   4    +1
300-350 ms  ┃                         main   4  this   1    -3
350-400 ms  ┃                         main   1  this   1    +0
400-450 ms  ┃                         main   1  this   0    -1
500-550 ms  ┃                         main   1  this   0    -1

1020 steps (queue-hop)

Cumulative STSO time: 3207ms over 1 samples

No main baseline with raw samples yet — showing this run's distribution on its own; the diff appears once a run on main has recorded them.

3000-3500 ms  ████████████████████████  steps 1
📜 Previous results (2)

4a912fa

Wed, 05 Aug 2026 05:35:44 GMT · run logs

vercel / nextjs-turbopack

Metric Scenario Best (ms) P75 (ms) P90 (ms) P99 (ms) Samples
TTFS step 1240 (+40%) 🔻 1342 🔴 (+30%) 🔻 1359 🔴 (+2.4%) 1418 🔴 (-4.3%) 30
TTFS stream 210 (-79%) 💚 1364 🔴 (+33%) 🔻 1405 🔴 (+35%) 🔻 1478 🔴 (+27%) 🔻 30
TTFS hook + stream 1518 (+261%) 🔻 1642 🔴 (+26%) 🔻 1706 🔴 (+11%) 1755 🔴 (+2.7%) 30
STSO 1020 steps (inline) 81 (-4.7%) 129 (-7.9%) 145 (-13%) 227 (-47%) 💚 1018
STSO 1020 steps (queue-hop) 3292 (-30%) 💚 3292 (-30%) 💚 3292 (-30%) 💚 3292 (-30%) 💚 1
WO 1020 steps 132284 (-12%) 132284 (-12%) 132284 (-12%) 132284 (-12%) 1
SL stream latency 101 (+23%) 🔻 142 🔴 (+31%) 🔻 181 🔴 (+53%) 🔻 4026 🔴 (+2282%) 🔻 30
SO stream overhead (text) 114 (+9.6%) 166 (-1.8%) 187 (+4.5%) 276 (-23%) 💚 30
SO stream overhead (structured) 103 (-1.0%) 145 (-18%) 💚 157 (-33%) 💚 264 (-49%) 💚 30

6ec2f5d

Tue, 04 Aug 2026 00:01:24 GMT · run logs

vercel / nextjs-turbopack

Metric Scenario Best (ms) P75 (ms) P90 (ms) P99 (ms) Samples
TTFS step 1271 (+56%) 🔻 1329 🔴 (+20%) 🔻 1368 🔴 (+16%) 🔻 1662 🔴 (-4.5%) 30
TTFS stream 285 (+5.6%) 1293 🔴 (+21%) 🔻 1308 🔴 (+21%) 🔻 1359 🔴 (+20%) 🔻 30
TTFS hook + stream 593 (-51%) 💚 1602 🔴 (+20%) 🔻 1669 🔴 (+23%) 🔻 1765 🔴 (+23%) 🔻 30
STSO 1020 steps (inline) 173 (+90%) 🔻 464 (+197%) 🔻 522 (+175%) 🔻 698 (+115%) 🔻 1016
STSO 1020 steps (queue-hop) 1447 (-58%) 💚 3377 (-2.7%) 3377 (-2.7%) 3377 (-2.7%) 3
WO 1020 steps 396410 (+165%) 🔻 396410 (+165%) 🔻 396410 (+165%) 🔻 396410 (+165%) 🔻 1
SL stream latency 87 (-15%) 145 🔴 (-4.6%) 180 🔴 (-2.2%) 229 🔴 (-24%) 💚 30
SO stream overhead (text) 107 (-26%) 💚 164 (-32%) 💚 176 (-54%) 💚 423 (-3.2%) 30
SO stream overhead (structured) 98 (-16%) 💚 141 (-34%) 💚 162 (-31%) 💚 458 (+55%) 🔻 30
ℹ️ Metric definitions & methodology

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

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

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

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

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

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

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

@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

No backport to stable for 4bb86d3 (AI decision).

This is feature work: it advertises a new hookRetention capability from the Vercel World and enables the corresponding Hook minimum-retention e2e case, adding a capability that does not exist on stable (verified: origin/stable's packages/world-vercel/src/index.ts has no hookRetention capability and packages/core/src/runtime/suspension-handler.ts has no attribute/World validation handling at all). The one arguably fix-like part — turning deterministic World validation rejections into FatalError in createHookEvent so workflows stop retrying invalid Hook requests — is a generalization of main-only validation-failure code and would need to be split out and force-backported if wanted. Per policy, a commit mixing a fix with feature work counts as feature work.

To override, re-run the Backport to stable workflow manually via workflow_dispatch and paste this commit SHA into the ref input:

4bb86d305423a2da813cbef8b7d6fe4421288bba

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants