From 2bb0858bbf34c10313637ffba198fc826cc0df7f Mon Sep 17 00:00:00 2001 From: Pranay Prakash Date: Thu, 30 Jul 2026 16:05:33 -0700 Subject: [PATCH 1/2] [repro] main + WORKFLOW_H2_MULTIPLEX=0 Isolation experiment for the hook-storm regression that arrives with main somewhere in e8934ade9..b12f248b6. #3190 turned the events-path H2 agent from one in-flight request per connection into 100; this branch is current main (32ac8e73f) with nothing changed but that feature's documented kill switch, so a storm here measures multiplexing and nothing else. The probe route exists because a flag that never reached the deployed function's runtime environment would look exactly like a flag that made no difference. Co-Authored-By: Claude Fable 5 Signed-off-by: Pranay Prakash --- .../app/api/e2e-h2-flag/route.ts | 18 ++++++++++++++++++ workbench/nextjs-turbopack/vercel.json | 3 ++- 2 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 workbench/nextjs-turbopack/app/api/e2e-h2-flag/route.ts diff --git a/workbench/nextjs-turbopack/app/api/e2e-h2-flag/route.ts b/workbench/nextjs-turbopack/app/api/e2e-h2-flag/route.ts new file mode 100644 index 0000000000..88442d73be --- /dev/null +++ b/workbench/nextjs-turbopack/app/api/e2e-h2-flag/route.ts @@ -0,0 +1,18 @@ +/** + * Repro-branch probe. Proves that `WORKFLOW_H2_MULTIPLEX`, set in this app's + * `vercel.json` `env` block, actually reaches the deployed function's *runtime* + * environment — which is where `@workflow/world-vercel` reads it, lazily, in + * `createEventsDispatcher()` (packages/world-vercel/src/http-client.ts). + * + * Without this probe a flag-off storm that measures no change is ambiguous + * between "multiplexing is not the cause" and "the flag never arrived". + */ +export const dynamic = 'force-dynamic'; + +export function GET() { + return Response.json({ + WORKFLOW_H2_MULTIPLEX: process.env.WORKFLOW_H2_MULTIPLEX ?? null, + // Mirrors `h2MultiplexEnabled()` in world-vercel's http-client. + multiplexEnabled: process.env.WORKFLOW_H2_MULTIPLEX !== '0', + }); +} diff --git a/workbench/nextjs-turbopack/vercel.json b/workbench/nextjs-turbopack/vercel.json index aa4f892410..cc7e0a3f9f 100644 --- a/workbench/nextjs-turbopack/vercel.json +++ b/workbench/nextjs-turbopack/vercel.json @@ -1,6 +1,7 @@ { "env": { - "WORKFLOW_PUBLIC_MANIFEST": "1" + "WORKFLOW_PUBLIC_MANIFEST": "1", + "WORKFLOW_H2_MULTIPLEX": "0" }, "regions": [ "iad1", From 5cc453ae77b101c16a41ecf797ad8363c29e1c26 Mon Sep 17 00:00:00 2001 From: Pranay Prakash Date: Thu, 30 Jul 2026 16:09:00 -0700 Subject: [PATCH 2/2] [repro] guarantee the kill switch reaches the deployed runtime MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `vercel.json`'s `env` block is documented as passing variables to the functions, but Vercel marks the property deprecated, and a flag that silently fails to arrive would make a null result read as "multiplexing is not the cause" — the one outcome where being wrong is expensive. instrumentation.ts now supplies the value if the deployment did not, and records which source won so the probe route can report it. That also tells us where mitigation advice should point if #3190 owns the regression. Co-Authored-By: Claude Fable 5 Signed-off-by: Pranay Prakash --- .../app/api/e2e-h2-flag/route.ts | 3 +++ workbench/nextjs-turbopack/instrumentation.ts | 16 ++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/workbench/nextjs-turbopack/app/api/e2e-h2-flag/route.ts b/workbench/nextjs-turbopack/app/api/e2e-h2-flag/route.ts index 88442d73be..56eb345278 100644 --- a/workbench/nextjs-turbopack/app/api/e2e-h2-flag/route.ts +++ b/workbench/nextjs-turbopack/app/api/e2e-h2-flag/route.ts @@ -14,5 +14,8 @@ export function GET() { WORKFLOW_H2_MULTIPLEX: process.env.WORKFLOW_H2_MULTIPLEX ?? null, // Mirrors `h2MultiplexEnabled()` in world-vercel's http-client. multiplexEnabled: process.env.WORKFLOW_H2_MULTIPLEX !== '0', + // 'vercel.json' if the deployment supplied it, 'instrumentation' if the + // fallback in instrumentation.ts had to. + source: process.env.WORKFLOW_H2_MULTIPLEX_SOURCE ?? null, }); } diff --git a/workbench/nextjs-turbopack/instrumentation.ts b/workbench/nextjs-turbopack/instrumentation.ts index bd66985a97..ed7d636e0f 100644 --- a/workbench/nextjs-turbopack/instrumentation.ts +++ b/workbench/nextjs-turbopack/instrumentation.ts @@ -1,5 +1,21 @@ import { registerOTel } from '@vercel/otel'; +// Repro branch only. `vercel.json`'s `env` block is the documented way to give +// the deployed functions a runtime variable, but Vercel marks it deprecated, so +// this is the belt to that suspenders: if the block were ignored, a storm here +// would measure stock main and read as "multiplexing is not the cause". +// `@workflow/world-vercel` reads the variable lazily in +// `createEventsDispatcher()`, which always runs after instrumentation, so +// setting it here reaches the same code path. +// Recorded so the probe route can also tell us whether the deprecated +// `vercel.json` block still works — that decides where the real mitigation +// advice should point if #3190 turns out to own the regression. +process.env.WORKFLOW_H2_MULTIPLEX_SOURCE = + process.env.WORKFLOW_H2_MULTIPLEX === undefined + ? 'instrumentation' + : 'vercel.json'; +process.env.WORKFLOW_H2_MULTIPLEX ??= '0'; + export function register() { registerOTel({ serviceName: 'nextjs-turbopack',