') + ')', '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('^' + ".*" + ', '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" + ', '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('^' + ".*" + ', '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); } })(); })(); [core] Forward-port stream reconnect to getReadable level by VaguelySerious · Pull Request #2318 · vercel/workflow · GitHub
Skip to content

[core] Forward-port stream reconnect to getReadable level - #2318

Merged
VaguelySerious merged 1 commit into
mainfrom
peter/forward-port-1847-reconnect
Jun 10, 2026
Merged

[core] Forward-port stream reconnect to getReadable level#2318
VaguelySerious merged 1 commit into
mainfrom
peter/forward-port-1847-reconnect

Conversation

@VaguelySerious

Copy link
Copy Markdown
Member

Forward-port of #1847 (which targets stable) onto main.

Same mechanism: object/serialized streams now transparently auto-reconnect at the getReadable level. When the upstream server stream connection errors mid-run (e.g. a per-session duration timeout), the wrapper reopens the stream at startIndex + consumedFrames, counting 4-byte length-prefixed wire frames to compute the resume position. Partial in-flight frame bytes are discarded and resent in full from the new index. A clean EOF (no error) is treated as stream completion and does not reconnect. Negative startIndex (last-N) skips reconnect and behaves as a single-shot read.

Adapted to main's world API: reads go through world.streams.get(runId, name, startIndex), so runId is threaded into createReconnectingFramedStream.

Bounded by a consecutive-failure cap (resets on forward progress) and an absolute total-reconnect backstop so a misbehaving backend can never loop forever.

Object streams only. Byte-stream reconnect is a separate follow-up since it depends on byte-stream framing (#1853).

🤖 Generated with Claude Code

Adds transparent auto-reconnect for object/serialized streams at the
getReadable level. On upstream error mid-run, the wrapper reopens the
server stream at startIndex + consumedFrames by counting 4-byte
length-prefixed wire frames. Clean EOF is treated as completion. Byte
streams keep their existing raw, non-reconnecting path.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@vercel

vercelBot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

@changeset-bot

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 484ff35

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

This PR includes changesets to release 16 packages
NameType
@workflow/corePatch
@workflow/buildersPatch
@workflow/cliPatch
@workflow/nextPatch
@workflow/nitroPatch
@workflow/vitestPatch
@workflow/web-sharedPatch
@workflow/webPatch
workflowPatch
@workflow/world-testingPatch
@workflow/astroPatch
@workflow/nestPatch
@workflow/rollupPatch
@workflow/sveltekitPatch
@workflow/vitePatch
@workflow/nuxtPatch

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-actionsBot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

📊 Benchmark Results

📈 Comparing against baseline from main branch. Green 🟢 = faster, Red 🔺 = slower.

workflow with no steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro0.043s (+0.9%)1.006s (~)0.963s101.00x
💻 LocalExpress0.044s (+1.9%)1.006s (~)0.962s101.02x
🐘 PostgresExpress0.062s (-6.9% 🟢)1.013s (~)0.951s101.43x
💻 LocalNext.js (Turbopack)0.062s (+12.2% 🔺)1.006s (~)0.944s101.43x
🐘 PostgresNitro0.064s (+1.1%)1.012s (~)0.948s101.48x
🐘 PostgresNext.js (Turbopack)0.070s (~)1.012s (~)0.942s101.63x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express0.279s (+2.6%)2.425s (-10.6% 🟢)2.146s101.00x
▲ VercelNext.js (Turbopack)0.337s (+2.4%)2.215s (-5.4% 🟢)1.878s101.21x
▲ VercelNitro0.414s (+58.6% 🔺)2.618s (+7.5% 🔺)2.204s101.48x

🔍 Observability: Express | Next.js (Turbopack) | Nitro

workflow with 1 step

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro1.090s (-0.6%)2.006s (~)0.916s101.00x
💻 LocalExpress1.093s (-0.7%)2.006s (~)0.913s101.00x
🐘 PostgresExpress1.106s (+1.3%)2.009s (~)0.903s101.02x
🐘 PostgresNitro1.111s (~)2.009s (~)0.897s101.02x
🐘 PostgresNext.js (Turbopack)1.135s (-0.6%)2.010s (~)0.875s101.04x
💻 LocalNext.js (Turbopack)1.137s (+2.5%)2.006s (~)0.870s101.04x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express1.599s (+1.0%)3.456s (-3.2%)1.857s101.00x
▲ VercelNitro1.813s (+15.8% 🔺)3.880s (+9.9% 🔺)2.067s101.13x
▲ VercelNext.js (Turbopack)1.829s (+9.8% 🔺)3.930s (-3.6%)2.101s101.14x

🔍 Observability: Express | Nitro | Next.js (Turbopack)

workflow with 10 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro10.512s (~)11.022s (~)0.510s31.00x
💻 LocalExpress10.530s (~)11.022s (~)0.492s31.00x
🐘 PostgresNitro10.555s (~)11.017s (~)0.462s31.00x
🐘 PostgresExpress10.555s (-1.5%)11.017s (-2.9%)0.462s31.00x
💻 LocalNext.js (Turbopack)10.763s (+1.5%)11.022s (~)0.258s31.02x
🐘 PostgresNext.js (Turbopack)10.903s (+0.8%)11.352s (+3.0%)0.449s31.04x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro14.656s (+10.0% 🔺)16.597s (+6.4% 🔺)1.941s21.00x
▲ VercelExpress14.691s (+11.7% 🔺)16.716s (+8.2% 🔺)2.024s21.00x
▲ VercelNext.js (Turbopack)15.058s (+11.6% 🔺)17.760s (+10.3% 🔺)2.702s21.03x

🔍 Observability: Nitro | Express | Next.js (Turbopack)

workflow with 25 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express13.729s (-0.8%)14.018s (-1.5%)0.289s51.00x
💻 LocalNitro13.743s (~)14.027s (~)0.284s51.00x
💻 LocalExpress13.766s (-1.0%)14.027s (-1.4%)0.261s51.00x
🐘 PostgresNitro13.778s (-0.5%)14.020s (~)0.242s51.00x
💻 LocalNext.js (Turbopack)14.312s (+2.0%)15.029s (+2.7%)0.716s41.04x
🐘 PostgresNext.js (Turbopack)14.696s (+0.7%)15.022s (~)0.326s41.07x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express22.488s (-11.7% 🟢)24.084s (-11.6% 🟢)1.596s31.00x
▲ VercelNitro22.998s (-9.9% 🟢)24.988s (-9.5% 🟢)1.990s31.02x
▲ VercelNext.js (Turbopack)23.989s (-6.0% 🟢)26.209s (-3.7%)2.220s31.07x

🔍 Observability: Express | Nitro | Next.js (Turbopack)

workflow with 50 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express12.356s (-3.0%)13.017s (-2.2%)0.661s71.00x
🐘 PostgresNitro12.473s (-2.1%)13.019s (~)0.546s71.01x
💻 LocalExpress12.482s (-0.9%)13.025s (~)0.543s71.01x
💻 LocalNitro12.498s (+0.7%)13.025s (~)0.527s71.01x
💻 LocalNext.js (Turbopack)13.620s (+5.2% 🔺)14.026s (+5.4% 🔺)0.406s71.10x
🐘 PostgresNext.js (Turbopack)13.979s (+1.6%)14.303s (+2.0%)0.325s71.13x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)31.936s (+7.8% 🔺)34.221s (+7.4% 🔺)2.286s31.00x
▲ VercelNitro35.529s (+22.3% 🔺)37.805s (+20.4% 🔺)2.276s31.11x
▲ VercelExpress36.781s (+14.7% 🔺)38.997s (+13.9% 🔺)2.217s31.15x

🔍 Observability: Next.js (Turbopack) | Nitro | Express

Promise.all with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.180s (-1.6%)2.007s (~)0.827s151.00x
🐘 PostgresExpress1.191s (-0.7%)2.007s (-0.7%)0.816s151.01x
💻 LocalExpress1.191s (-5.7% 🟢)2.006s (~)0.815s151.01x
💻 LocalNitro1.205s (-0.7%)2.006s (~)0.801s151.02x
🐘 PostgresNext.js (Turbopack)1.231s (-1.6%)2.007s (~)0.776s151.04x
💻 LocalNext.js (Turbopack)1.326s (+8.4% 🔺)2.007s (~)0.681s151.12x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)2.618s (-2.6%)4.700s (+11.4% 🔺)2.082s71.00x
▲ VercelExpress3.062s (+10.6% 🔺)4.407s (-6.7% 🟢)1.345s71.17x
▲ VercelNitro3.474s (+25.4% 🔺)5.666s (+30.5% 🔺)2.192s61.33x

🔍 Observability: Next.js (Turbopack) | Express | Nitro

Promise.all with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.253s (-5.6% 🟢)2.006s (~)0.754s151.00x
🐘 PostgresNitro1.267s (+0.7%)2.007s (~)0.739s151.01x
🐘 PostgresNext.js (Turbopack)1.387s (-1.6%)2.008s (~)0.620s151.11x
💻 LocalExpress1.642s (-12.8% 🟢)2.006s (-9.7% 🟢)0.363s151.31x
💻 LocalNitro1.725s (+1.2%)2.072s (~)0.347s151.38x
💻 LocalNext.js (Turbopack)1.852s (+7.3% 🔺)2.141s (+6.7% 🔺)0.289s151.48x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express3.432s (-16.0% 🟢)5.025s (-18.5% 🟢)1.593s61.00x
▲ VercelNext.js (Turbopack)3.458s (-15.8% 🟢)5.350s (-12.8% 🟢)1.893s61.01x
▲ VercelNitro3.777s (-7.2% 🟢)5.718s (-7.9% 🟢)1.941s61.10x

🔍 Observability: Express | Next.js (Turbopack) | Nitro

Promise.all with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.368s (-1.8%)2.006s (~)0.638s151.00x
🐘 PostgresNitro1.392s (-1.0%)2.008s (~)0.615s151.02x
🐘 PostgresNext.js (Turbopack)1.912s (+8.6% 🔺)2.510s (+9.4% 🔺)0.599s121.40x
💻 LocalExpress4.508s (-14.9% 🟢)5.012s (-16.7% 🟢)0.505s63.29x
💻 LocalNitro4.837s (+2.5%)5.180s (+3.4%)0.343s63.54x
💻 LocalNext.js (Turbopack)5.726s (+30.3% 🔺)6.415s (+28.0% 🔺)0.689s54.19x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express5.589s (-5.3% 🟢)7.131s (-9.6% 🟢)1.542s51.00x
▲ VercelNitro5.642s (-16.2% 🟢)7.804s (-10.4% 🟢)2.162s41.01x
▲ VercelNext.js (Turbopack)5.715s (-2.7%)7.795s (~)2.080s41.02x

🔍 Observability: Express | Nitro | Next.js (Turbopack)

Promise.race with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.182s (-1.5%)2.008s (~)0.826s151.00x
🐘 PostgresExpress1.201s (~)2.009s (~)0.807s151.02x
🐘 PostgresNext.js (Turbopack)1.246s (-1.6%)2.008s (~)0.762s151.05x
💻 LocalNext.js (Turbopack)1.360s (+4.2%)2.007s (~)0.647s151.15x
💻 LocalExpress1.560s (-5.8% 🟢)2.006s (-3.3%)0.446s151.32x
💻 LocalNitro1.582s (+2.5%)2.074s (+3.4%)0.492s151.34x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)2.629s (-39.9% 🟢)4.196s (-33.3% 🟢)1.567s81.00x
▲ VercelExpress2.740s (+6.7% 🔺)4.306s (+2.6%)1.566s71.04x
▲ VercelNitro2.863s (+7.1% 🔺)4.388s (+1.3%)1.525s71.09x

🔍 Observability: Next.js (Turbopack) | Express | Nitro

Promise.race with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.272s (+2.3%)2.008s (~)0.736s151.00x
🐘 PostgresNitro1.278s (~)2.008s (~)0.730s151.00x
🐘 PostgresNext.js (Turbopack)1.379s (-1.3%)2.007s (~)0.629s151.08x
💻 LocalExpress1.860s (-12.5% 🟢)2.149s (-17.0% 🟢)0.290s141.46x
💻 LocalNext.js (Turbopack)2.045s (+1.2%)2.592s (+3.4%)0.546s121.61x
💻 LocalNitro2.123s (+20.8% 🔺)2.508s (+21.0% 🔺)0.385s121.67x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express3.622s (-1.5%)5.181s (-7.7% 🟢)1.559s61.00x
▲ VercelNext.js (Turbopack)4.009s (-11.3% 🟢)5.796s (-6.0% 🟢)1.786s61.11x
▲ VercelNitro4.197s (+2.5%)5.731s (-4.5%)1.534s61.16x

🔍 Observability: Express | Next.js (Turbopack) | Nitro

Promise.race with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.383s (-7.8% 🟢)2.008s (-16.3% 🟢)0.624s151.00x
🐘 PostgresNitro1.386s (-5.1% 🟢)2.008s (~)0.622s151.00x
🐘 PostgresNext.js (Turbopack)1.689s (+2.0%)2.222s (+7.2% 🔺)0.533s141.22x
💻 LocalExpress5.191s (-11.5% 🟢)5.680s (-8.6% 🟢)0.489s63.75x
💻 LocalNitro5.498s (+15.9% 🔺)6.216s (+16.3% 🔺)0.719s53.97x
💻 LocalNext.js (Turbopack)6.198s (+20.1% 🔺)6.817s (+13.3% 🔺)0.619s54.48x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)5.186s (-10.4% 🟢)6.860s (-9.5% 🟢)1.674s51.00x
▲ VercelNitro5.436s (+10.3% 🔺)7.514s (+11.5% 🔺)2.078s41.05x
▲ VercelExpress6.194s (+22.1% 🔺)7.770s (+17.2% 🔺)1.576s41.19x

🔍 Observability: Next.js (Turbopack) | Nitro | Express

workflow with 10 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.559s (-16.2% 🟢)1.006s (-4.0%)0.447s601.00x
💻 LocalExpress0.579s (-11.8% 🟢)1.005s (-3.3%)0.426s601.03x
🐘 PostgresNitro0.585s (-3.8%)1.007s (~)0.421s601.05x
💻 LocalNitro0.609s (-24.2% 🟢)1.005s (-16.6% 🟢)0.395s601.09x
🐘 PostgresNext.js (Turbopack)0.825s (+1.6%)1.024s (+1.8%)0.199s591.48x
💻 LocalNext.js (Turbopack)0.867s (+24.6% 🔺)1.005s (~)0.138s601.55x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)5.267s (+4.3%)7.077s (+3.3%)1.810s91.00x
▲ VercelNitro5.534s (-25.8% 🟢)7.103s (-25.4% 🟢)1.569s91.05x
▲ VercelExpress5.564s (+1.1%)7.198s (+1.2%)1.634s91.06x

🔍 Observability: Next.js (Turbopack) | Nitro | Express

workflow with 25 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.360s (-11.5% 🟢)2.030s (-5.7% 🟢)0.670s451.00x
🐘 PostgresNitro1.376s (-2.9%)2.030s (+1.1%)0.654s451.01x
💻 LocalExpress1.495s (-6.1% 🟢)2.028s (~)0.533s451.10x
💻 LocalNitro1.535s (+4.8%)2.006s (~)0.472s451.13x
🐘 PostgresNext.js (Turbopack)1.941s (~)2.125s (-2.3%)0.184s431.43x
💻 LocalNext.js (Turbopack)2.115s (+22.3% 🔺)3.008s (+48.3% 🔺)0.893s301.56x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)13.276s (-10.1% 🟢)15.320s (-9.5% 🟢)2.044s61.00x
▲ VercelNitro14.234s (+7.7% 🔺)16.245s (+4.9%)2.011s61.07x
▲ VercelExpress14.646s (-10.6% 🟢)16.714s (-8.8% 🟢)2.067s61.10x

🔍 Observability: Next.js (Turbopack) | Nitro | Express

workflow with 50 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express2.710s (-6.9% 🟢)3.058s (-13.6% 🟢)0.348s401.00x
🐘 PostgresNitro2.733s (-6.2% 🟢)3.167s (-3.5%)0.434s381.01x
💻 LocalExpress3.127s (-8.5% 🟢)3.853s (-3.9%)0.726s321.15x
💻 LocalNitro3.281s (+2.3%)4.010s (~)0.728s301.21x
🐘 PostgresNext.js (Turbopack)3.944s (+2.2%)4.218s (+3.5%)0.273s291.46x
💻 LocalNext.js (Turbopack)4.362s (+17.0% 🔺)5.010s (+21.9% 🔺)0.648s241.61x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express24.451s (-15.2% 🟢)26.736s (-13.5% 🟢)2.285s51.00x
▲ VercelNitro25.400s (-14.7% 🟢)27.405s (-14.3% 🟢)2.005s51.04x
▲ VercelNext.js (Turbopack)27.665s (+2.5%)30.074s (+2.8%)2.410s41.13x

🔍 Observability: Express | Nitro | Next.js (Turbopack)

workflow with 10 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.211s (-16.3% 🟢)1.006s (~)0.795s601.00x
🐘 PostgresNitro0.215s (-8.8% 🟢)1.006s (~)0.792s601.02x
🐘 PostgresNext.js (Turbopack)0.272s (-3.5%)1.006s (~)0.734s601.29x
💻 LocalExpress0.406s (-12.7% 🟢)1.004s (~)0.599s601.92x
💻 LocalNitro0.458s (+7.8% 🔺)1.004s (~)0.547s602.17x
💻 LocalNext.js (Turbopack)0.580s (-5.0% 🟢)1.005s (-3.3%)0.425s602.75x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro2.335s (~)3.934s (-9.2% 🟢)1.599s161.00x
▲ VercelExpress2.390s (+8.0% 🔺)3.799s (-7.3% 🟢)1.410s161.02x
▲ VercelNext.js (Turbopack)2.730s (+24.1% 🔺)4.232s (~)1.502s151.17x

🔍 Observability: Nitro | Express | Next.js (Turbopack)

workflow with 25 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro0.349s (-6.0% 🟢)1.006s (~)0.657s901.00x
🐘 PostgresExpress0.350s (+3.3%)1.006s (~)0.656s901.00x
🐘 PostgresNext.js (Turbopack)0.492s (+1.9%)1.006s (~)0.514s901.41x
💻 LocalExpress2.067s (-5.7% 🟢)2.535s (-10.2% 🟢)0.468s365.93x
💻 LocalNitro2.180s (+5.6% 🔺)2.638s (~)0.457s356.25x
💻 LocalNext.js (Turbopack)2.469s (+0.8%)3.077s (-1.0%)0.608s307.08x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro5.213s (-88.1% 🟢)6.899s (-85.0% 🟢)1.686s141.00x
▲ VercelExpress5.824s (-86.6% 🟢)7.548s (-83.3% 🟢)1.724s121.12x
▲ VercelNext.js (Turbopack)5.829s (-7.0% 🟢)7.723s (-9.1% 🟢)1.894s121.12x

🔍 Observability: Nitro | Express | Next.js (Turbopack)

workflow with 50 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.667s (+6.6% 🔺)1.006s (-4.6%)0.340s1201.00x
🐘 PostgresNitro0.680s (-3.8%)1.006s (~)0.325s1201.02x
🐘 PostgresNext.js (Turbopack)0.954s (-2.6%)1.419s (-11.8% 🟢)0.465s851.43x
💻 LocalExpress8.723s (-15.5% 🟢)9.333s (-14.1% 🟢)0.610s1313.08x
💻 LocalNitro9.744s (+11.9% 🔺)10.279s (+10.2% 🔺)0.535s1214.61x
💻 LocalNext.js (Turbopack)10.645s (+3.2%)11.483s (+1.6%)0.838s1115.96x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro15.998s (-94.9% 🟢)17.838s (-94.3% 🟢)1.840s71.00x
▲ VercelNext.js (Turbopack)19.225s (-88.0% 🟢)21.719s (-86.6% 🟢)2.494s61.20x
▲ VercelExpress20.183s (+32.3% 🔺)22.131s (+26.5% 🔺)1.949s61.26x

🔍 Observability: Nitro | Next.js (Turbopack) | Express

Stream Benchmarks(includes TTFB metrics)
workflow with stream

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Express1.151s (-1.5%)2.005s (~)0.010s (-18.8% 🟢)2.017s (~)0.866s101.00x
🐘 PostgresExpress1.156s (-3.2%)2.001s (~)0.001s (-96.8% 🟢)2.010s (-1.6%)0.854s101.00x
🐘 PostgresNitro1.159s (-1.8%)1.999s (~)0.002s (+7.1% 🔺)2.011s (~)0.852s101.01x
💻 LocalNitro1.162s (+1.0%)2.005s (~)0.011s (+5.0%)2.018s (~)0.856s101.01x
💻 LocalNext.js (Turbopack)1.203s (+2.9%)2.003s (~)0.012s (+19.8% 🔺)2.019s (~)0.816s101.05x
🐘 PostgresNext.js (Turbopack)1.243s (+0.7%)2.002s (~)0.001s (-40.0% 🟢)2.011s (~)0.768s101.08x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.239s (+4.4%)3.261s (-4.3%)1.025s (+95.0% 🔺)4.768s (+7.2% 🔺)2.528s101.00x
▲ VercelNitro2.334s (+7.8% 🔺)3.814s (+11.6% 🔺)0.749s (+45.3% 🔺)4.999s (+11.9% 🔺)2.665s101.04x
▲ VercelNext.js (Turbopack)2.349s (+4.8%)3.389s (-4.5%)1.119s (-91.0% 🟢)4.980s (-69.8% 🟢)2.631s101.05x

🔍 Observability: Express | Nitro | Next.js (Turbopack)

stream pipeline with 5 transform steps (1MB)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Express1.556s (-4.9%)2.010s (~)0.013s (+17.8% 🔺)2.025s (~)0.470s301.00x
🐘 PostgresNitro1.578s (-5.1% 🟢)2.007s (~)0.005s (+28.2% 🔺)2.027s (~)0.449s301.01x
💻 LocalNitro1.583s (-9.1% 🟢)2.010s (~)0.013s (+39.1% 🔺)2.025s (-7.9% 🟢)0.442s301.02x
🐘 PostgresExpress1.590s (-8.5% 🟢)2.004s (-6.5% 🟢)0.005s (+67.6% 🔺)2.026s (-6.2% 🟢)0.436s301.02x
💻 LocalNext.js (Turbopack)1.733s (+5.7% 🔺)2.008s (~)0.013s (+29.7% 🔺)2.025s (~)0.291s301.11x
🐘 PostgresNext.js (Turbopack)1.756s (~)2.010s (~)0.005s (+34.7% 🔺)2.025s (~)0.270s301.13x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)5.852s (-8.2% 🟢)7.182s (-9.5% 🟢)0.264s (-27.1% 🟢)7.967s (-10.1% 🟢)2.115s81.00x
▲ VercelNitro5.865s (+2.1%)7.113s (-6.4% 🟢)0.240s (-28.9% 🟢)7.909s (-6.8% 🟢)2.044s81.00x
▲ VercelExpress6.143s (+6.7% 🔺)7.659s (+3.1%)0.496s (+40.3% 🔺)8.630s (+2.7%)2.486s71.05x

🔍 Observability: Next.js (Turbopack) | Nitro | Express

10 parallel streams (1MB each)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro0.723s (~)1.067s (+3.5%)0.000s (-65.5% 🟢)1.079s (+3.0%)0.356s561.00x
🐘 PostgresExpress0.734s (-24.9% 🟢)1.069s (-25.2% 🟢)0.000s (~)1.079s (-25.5% 🟢)0.345s561.02x
🐘 PostgresNext.js (Turbopack)0.858s (+3.1%)1.090s (+1.8%)0.000s (-49.1% 🟢)1.099s (+1.8%)0.240s551.19x
💻 LocalExpress1.351s (-8.9% 🟢)2.013s (~)0.000s (-40.0% 🟢)2.015s (~)0.664s301.87x
💻 LocalNitro1.425s (+5.3% 🔺)2.013s (~)0.000s (-60.0% 🟢)2.016s (~)0.591s301.97x
💻 LocalNext.js (Turbopack)1.479s (+2.2%)2.013s (~)0.000s (-12.5% 🟢)2.016s (~)0.537s302.05x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro3.105s (-13.8% 🟢)4.547s (-10.4% 🟢)0.000s (+Infinity% 🔺)5.076s (-9.8% 🟢)1.971s121.00x
▲ VercelNext.js (Turbopack)3.211s (-3.0%)4.641s (-5.2% 🟢)0.000s (+Infinity% 🔺)5.109s (-6.4% 🟢)1.899s121.03x
▲ VercelExpress3.328s (-18.0% 🟢)4.465s (-16.4% 🟢)0.000s (-90.2% 🟢)5.099s (-17.3% 🟢)1.771s121.07x

🔍 Observability: Nitro | Next.js (Turbopack) | Express

fan-out fan-in 10 streams (1MB each)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.454s (+3.1%)2.102s (+5.3% 🔺)0.000s (NaN%)2.152s (+7.0% 🔺)0.698s281.00x
🐘 PostgresExpress1.501s (-7.0% 🟢)2.222s (-1.4%)0.000s (+Infinity% 🔺)2.234s (-2.0%)0.733s271.03x
🐘 PostgresNext.js (Turbopack)1.763s (+2.0%)2.225s (-1.6%)0.000s (+Infinity% 🔺)2.251s (-0.8%)0.488s271.21x
💻 LocalNext.js (Turbopack)3.014s (-6.2% 🟢)3.553s (-8.8% 🟢)0.001s (+88.2% 🔺)3.558s (-8.8% 🟢)0.544s172.07x
💻 LocalExpress3.028s (-9.2% 🟢)3.730s (-2.8%)0.001s (+119.6% 🔺)3.733s (-2.9%)0.705s172.08x
💻 LocalNitro3.218s (+3.3%)4.027s (+3.4%)0.001s (+166.7% 🔺)4.030s (+3.3%)0.811s152.21x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro4.869s (-30.9% 🟢)6.226s (-27.4% 🟢)0.000s (NaN%)6.743s (-25.8% 🟢)1.874s91.00x
▲ VercelExpress4.982s (-21.2% 🟢)6.399s (-16.1% 🟢)0.000s (-70.4% 🟢)6.822s (-16.7% 🟢)1.840s91.02x
▲ VercelNext.js (Turbopack)4.991s (-3.4%)6.387s (-4.4%)0.000s (-100.0% 🟢)6.909s (-4.1%)1.918s91.02x

🔍 Observability: Nitro | Express | Next.js (Turbopack)

Summary

Fastest Framework by World

Winner determined by most benchmark wins

World🥇 Fastest FrameworkWins
💻 LocalExpress15/21
🐘 PostgresExpress14/21
▲ VercelExpress8/21
Fastest World by Framework

Winner determined by most benchmark wins

Framework🥇 Fastest WorldWins
Express🐘 Postgres16/21
Next.js (Turbopack)🐘 Postgres15/21
Nitro🐘 Postgres17/21
Column Definitions
  • Workflow Time: Runtime reported by workflow (completedAt - createdAt) - primary metric
  • TTFB: Time to First Byte - time from workflow start until first stream byte received (stream benchmarks only)
  • Slurp: Time from first byte to complete stream consumption (stream benchmarks only)
  • Wall Time: Total testbench time (trigger workflow + poll for result)
  • Overhead: Testbench overhead (Wall Time - Workflow Time)
  • Samples: Number of benchmark iterations run
  • vs Fastest: How much slower compared to the fastest configuration for this benchmark

Worlds:

  • 💻 Local: In-memory filesystem world (local development)
  • 🐘 Postgres: PostgreSQL database world (local development)
  • ▲ Vercel: Vercel production/preview deployment
  • 🌐 Turso: Community world (local development)
  • 🌐 MongoDB: Community world (local development)
  • 🌐 Redis: Community world (local development)
  • 🌐 Jazz: Community world (local development)
  • 🌐 Redis: Community world (local development)
  • 🌐 Redis + BullMQ: Community world (local development)
  • 🌐 Cloudflare: Community world (local development)
  • 🌐 MySQL: Community world (local development)
  • 🌐 Azure: Community world (local development)
  • 🌐 NATS JetStream: Community world (local development)
  • 🌐 Upstash: Community world (local development)

📋 View full workflow run

@github-actions

github-actionsBot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

All tests passed

Summary

PassedFailedSkippedTotal
✅ ▲ Vercel Production126602191485
✅ 💻 Local Development153602191755
✅ 📦 Local Production153602191755
✅ 🐘 Local Postgres153602191755
✅ 🪟 Windows13500135
✅ 📋 Other7690176945
Total6778010527830

Details by Category

✅ ▲ Vercel Production
AppPassedFailedSkipped
✅ astro109026
✅ example109026
✅ express109026
✅ fastify109026
✅ hono109026
✅ nextjs-turbopack13302
✅ nextjs-webpack13302
✅ nitro109026
✅ nuxt109026
✅ sveltekit12807
✅ vite109026
✅ 💻 Local Development
AppPassedFailedSkipped
✅ astro-stable110025
✅ express-stable110025
✅ fastify-stable110025
✅ hono-stable110025
✅ nextjs-turbopack-canary116019
✅ nextjs-turbopack-stable-lazy-discovery-disabled13500
✅ nextjs-turbopack-stable-lazy-discovery-enabled13500
✅ nextjs-webpack-canary116019
✅ nextjs-webpack-stable-lazy-discovery-enabled13500
✅ nitro-stable110025
✅ nuxt-stable110025
✅ sveltekit-stable12906
✅ vite-stable110025
✅ 📦 Local Production
AppPassedFailedSkipped
✅ astro-stable110025
✅ express-stable110025
✅ fastify-stable110025
✅ hono-stable110025
✅ nextjs-turbopack-canary116019
✅ nextjs-turbopack-stable-lazy-discovery-disabled13500
✅ nextjs-turbopack-stable-lazy-discovery-enabled13500
✅ nextjs-webpack-canary116019
✅ nextjs-webpack-stable-lazy-discovery-enabled13500
✅ nitro-stable110025
✅ nuxt-stable110025
✅ sveltekit-stable12906
✅ vite-stable110025
✅ 🐘 Local Postgres
AppPassedFailedSkipped
✅ astro-stable110025
✅ express-stable110025
✅ fastify-stable110025
✅ hono-stable110025
✅ nextjs-turbopack-canary116019
✅ nextjs-turbopack-stable-lazy-discovery-disabled13500
✅ nextjs-turbopack-stable-lazy-discovery-enabled13500
✅ nextjs-webpack-canary116019
✅ nextjs-webpack-stable-lazy-discovery-enabled13500
✅ nitro-stable110025
✅ nuxt-stable110025
✅ sveltekit-stable12906
✅ vite-stable110025
✅ 🪟 Windows
AppPassedFailedSkipped
✅ nextjs-turbopack13500
✅ 📋 Other
AppPassedFailedSkipped
✅ e2e-local-dev-nest-stable110025
✅ e2e-local-dev-tanstack-start-110025
✅ e2e-local-postgres-nest-stable110025
✅ e2e-local-postgres-tanstack-start-110025
✅ e2e-local-prod-nest-stable110025
✅ e2e-local-prod-tanstack-start-110025
✅ e2e-vercel-prod-tanstack-start109026

📋 View full workflow run


Some E2E test jobs failed:

  • Vercel Prod: success
  • Local Dev: cancelled
  • Local Prod: failure
  • Local Postgres: failure
  • Windows: success

Check the workflow run for details.

@VaguelySerious
VaguelySerious marked this pull request as ready for review June 10, 2026 09:46
@VaguelySerious
VaguelySerious requested a review from a team as a code ownerJune 10, 2026 09:46
@VaguelySerious

Copy link
Copy Markdown
MemberAuthor

(AI) Cross-PR context & merge order

Together these make run.getReadable() transparently reconnect when a stream's underlying connection ends mid-stream (e.g. the periodic server-side max-duration cutoff), instead of surfacing a truncated stream to the consumer.

How the pieces fit:

Behavioural note: the reconnecting reader only reopens on a connection error — a clean close means "complete". So the client change is inert on its own and only takes effect once paired with the coordinated server-side change that ends a timed-out connection with an error rather than a silent close (handled separately). Shipping the client first is therefore safe and must precede that server change.

Suggested order:

  1. [core] Move stream reconnect logic to getReadable level #1847stable (client reconnect; a no-op until the paired server change ships).
  2. [core] Forward-port stream reconnect to getReadable level #2318main (same, on main). Release both so deployed apps gain reconnect.
  3. The coordinated server-side timeout change — only after the reconnect-capable client is released.

#1853 is independent and can land on its own schedule; it unblocks byte-stream reconnect as a future follow-up.

@TooTallNateTooTallNate left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Approve — faithful forward-port, correctly adapted to main's world API

I reviewed this against the stable PR (#1847) side by side, built @workflow/core from this branch, and ran the test suites locally: all 11 reconnecting-framed-stream tests plus the 199 serialization tests pass (210 total).

Port fidelity

The reconnect mechanism is identical to #1847 — frame counting, partial-frame discard with resume at startIndex + consumedFrames, clean-EOF-equals-completion, negative-startIndex opt-out, consecutive cap with reset-on-progress, absolute backstop. The adaptations are exactly the ones main's API requires and nothing more:

  • world.readFromStream(name, idx)world.streams.get(runId, name, idx), with runId threaded through createReconnectingFramedStream
  • getWorld()await getWorldLazy()
  • Reviver hookup matches: byte streams keep WorkflowServerReadableStream (no reconnect — no framing to count, explicitly deferred to the byte-framing follow-up), object streams get the reconnecting wrapper

The test file gained one main-only case (11 vs 10) and the mock world is adapted to the streams.get shape. Changeset is correctly scoped (@workflow/core: patch).

Notes

  1. Same non-blocking design note as the stable PR: a transient failure of the reopen itself (reconnect()connect() throwing) is fatal rather than counted against the budget — only reader.read() errors are retried. Worth folding into the budgeted loop in a follow-up, here and on stable, so the wrapper is robust against the exact server blip it exists to survive.

  2. Heads-up on a pending textual conflict: #1853 (byte-stream framing) touches the same reviver region of serialization.ts — specifically the value.type === 'bytes' branch this PR also edits (it adds framing dispatch where this PR adds the no-reconnect comment + scoped readable construction). Whichever lands second gets a small but real merge conflict; the resolution is straightforward (byte branch gains framing dispatch, object branch keeps the reconnecting wrapper) but worth coordinating so the second rebase is deliberate rather than auto-resolved.

  3. CI: the three red nextjs-webpack jobs (Local Postgres / Local Prod / Local Dev, stable lazyDiscovery disabled) are the known pre-existing failure on main (Dynamic require of "stream" during next build page-data collection) — I verified the identical failure signature on main's latest Tests run. Not attributable to this PR. Everything else is green and the branch has no conflicts with current main.

Good to land. Same sequencing logic as the stable PR applies: this is safe to release ahead of the coordinated server-side behavior change, since the reconnect path simply never triggers until the server starts erroring timed-out connections instead of closing them.

@VaguelySerious
VaguelySerious merged commit eb976db into mainJun 10, 2026
187 of 196 checks passed
@VaguelySerious
VaguelySerious deleted the peter/forward-port-1847-reconnect branch June 10, 2026 18:41
@github-actionsgithub-actionsBot mentioned this pull request Jun 10, 2026
pranaygp added a commit that referenced this pull request Jun 11, 2026
…-attributes-client
* origin/main: (32 commits)
feat(core): add optional namespace for queue topic prefix (#2305)
[web-shared] Show precise durations in the new trace viewer (#2335)
Validate unique workflow step IDs at build time (#2018)
Move run attributes into their own detail card (#2327)
[core] Forward-port stream reconnect to getReadable level (#2318)
[docs] Add "Step executed multiple times" error page (#2310)
Fix flickering on the detail panel when navigating the trace viewer (#2325)
Version Packages (beta) (#2254)
Patch vulnerable package dependencies (#2301)
Deprecate DurableAgent and update it with WorkflowAgent in v5 docs (#2285)
[codex] Fix partial world-local exclusive writes (#2296)
[world-vercel] Validate ref resolve responses before use (#2035)
fix: bump devalue to 5.8.1 (#2292)
Handle lazy stream key request failures (#2257)
fix(docs): declare Nitro auto-import globals for code samples (#2290)
[core] Preserve event-log order in hook-vs-sleep replay races (#2171) (#2185)
docs: add nitro changelog (#2232)
fix(next): always apply turbopack content condition regardless of builder mode (#2253)
Update @vercel/queue from 0.2.1 to 0.3.0 (#2255)
[nest] Use AST-based CommonJS import rewriting (#2080)
...
# Conflicts:
#	packages/world-local/src/storage/events-storage.ts
VaguelySerious added a commit that referenced this pull request Jun 11, 2026
Addresses review feedback on #2318: a transient failure of the reopen
itself (`connect()` throwing inside `reconnect()`) was fatal — only
`reader.read()` errors were retried. A brief server unavailability during
the reconnect window is exactly the blip this wrapper exists to survive,
so count it against the reconnect budget and retry rather than erroring
the stream. Budget exhaustion (a server that stays down) still terminates.
Adds a test: a reopen that throws once then succeeds recovers transparently.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
pranaygp added a commit that referenced this pull request Jun 11, 2026
…hook-ready
* origin-https/main: (119 commits)
fix: settle aborted parallel steps before completing abortParallelWorkflow (#2244)
Add native v4 workflow attribute events (#2226)
Version Packages (beta) (#2326)
[ci] Fix flaky windows unit tests (#2359)
Capture Vercel runtime logs when e2e Vercel Prod lanes fail (#2356)
Fix e2e failure reporting under vitest 4 and preserve fetch error causes (#2355)
[core] Fix process crash from rejected waitUntil promises (#2336)
[core] Remove duplicate `waitUntil` for suspension handler async operations (#2345)
Prevent local tests from hanging (#2338)
feat(core): add optional namespace for queue topic prefix (#2305)
[web-shared] Show precise durations in the new trace viewer (#2335)
Validate unique workflow step IDs at build time (#2018)
Move run attributes into their own detail card (#2327)
[core] Forward-port stream reconnect to getReadable level (#2318)
[docs] Add "Step executed multiple times" error page (#2310)
Fix flickering on the detail panel when navigating the trace viewer (#2325)
Version Packages (beta) (#2254)
Patch vulnerable package dependencies (#2301)
Deprecate DurableAgent and update it with WorkflowAgent in v5 docs (#2285)
[codex] Fix partial world-local exclusive writes (#2296)
...
# Conflicts:
#	packages/core/src/runtime.ts
#	packages/core/src/runtime/suspension-handler.ts
pranaygp added a commit that referenced this pull request Jun 11, 2026
…docs-run-idempotency
* origin-https/main: (115 commits)
[core] V2: unify wait+step queue dispatch in suspension processing (#1925)
fix(world-local,world-postgres): make duplicate hook_created idempotent (#2295)
docs(observability): remove MVP implementation detail bullet (#2367)
fix: settle aborted parallel steps before completing abortParallelWorkflow (#2244)
Add native v4 workflow attribute events (#2226)
Version Packages (beta) (#2326)
[ci] Fix flaky windows unit tests (#2359)
Capture Vercel runtime logs when e2e Vercel Prod lanes fail (#2356)
Fix e2e failure reporting under vitest 4 and preserve fetch error causes (#2355)
[core] Fix process crash from rejected waitUntil promises (#2336)
[core] Remove duplicate `waitUntil` for suspension handler async operations (#2345)
Prevent local tests from hanging (#2338)
feat(core): add optional namespace for queue topic prefix (#2305)
[web-shared] Show precise durations in the new trace viewer (#2335)
Validate unique workflow step IDs at build time (#2018)
Move run attributes into their own detail card (#2327)
[core] Forward-port stream reconnect to getReadable level (#2318)
[docs] Add "Step executed multiple times" error page (#2310)
Fix flickering on the detail panel when navigating the trace viewer (#2325)
Version Packages (beta) (#2254)
...
# Conflicts:
#	docs/content/docs/v4/cookbook/advanced/child-workflows.mdx
#	docs/content/docs/v5/cookbook/advanced/child-workflows.mdx
#	docs/content/docs/v5/cookbook/agent-patterns/durable-agent.mdx
pranaygp added a commit that referenced this pull request Jun 11, 2026
* origin/main:
[core] V2: unify wait+step queue dispatch in suspension processing (#1925)
fix(world-local,world-postgres): make duplicate hook_created idempotent (#2295)
docs(observability): remove MVP implementation detail bullet (#2367)
fix: settle aborted parallel steps before completing abortParallelWorkflow (#2244)
Add native v4 workflow attribute events (#2226)
Version Packages (beta) (#2326)
[ci] Fix flaky windows unit tests (#2359)
Capture Vercel runtime logs when e2e Vercel Prod lanes fail (#2356)
Fix e2e failure reporting under vitest 4 and preserve fetch error causes (#2355)
[core] Fix process crash from rejected waitUntil promises (#2336)
[core] Remove duplicate `waitUntil` for suspension handler async operations (#2345)
Prevent local tests from hanging (#2338)
feat(core): add optional namespace for queue topic prefix (#2305)
[web-shared] Show precise durations in the new trace viewer (#2335)
Validate unique workflow step IDs at build time (#2018)
Move run attributes into their own detail card (#2327)
[core] Forward-port stream reconnect to getReadable level (#2318)
[docs] Add "Step executed multiple times" error page (#2310)
Fix flickering on the detail panel when navigating the trace viewer (#2325)
VaguelySerious added a commit that referenced this pull request Jun 22, 2026
Addresses review feedback on #2318: a transient failure of the reopen
itself (`connect()` throwing inside `reconnect()`) was fatal — only
`reader.read()` errors were retried. A brief server unavailability during
the reconnect window is exactly the blip this wrapper exists to survive,
so count it against the reconnect budget and retry rather than erroring
the stream. Budget exhaustion (a server that stays down) still terminates.
Adds a test: a reopen that throws once then succeeds recovers transparently.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for freeto 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

@VaguelySerious@TooTallNate