Skip to content

feat(proxy): add zero-downtime rolling workers - #1209

Merged
murdore merged 1 commit into
releasefrom
feat/proxy-rolling-worker-handoff
Jul 19, 2026
Merged

murdore merged 1 commit into
releasefrom
feat/proxy-rolling-worker-handoff

Conversation

@murdore

@murdore murdore commented Jul 18, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • keep the public proxy listener stable while serving workers activate, drain, crash, recover, or update
  • transfer accepted TCP sockets directly over IPC with an activation handshake and bounded recovery queue
  • validate exact installed versions, perform durable rollback on failed updates, and expose supervisor/worker lifecycle state
  • preserve existing proxy request, response, telemetry, config reload, and account-routing behavior inside each worker

Safety and rollout

  • stacked on fix(proxy): resolve reliability review findings #1208; rebase this single commit onto release after fix(proxy): resolve reliability review findings #1208 merges
  • no live proxy, global package, launchd service, account config, or endpoint was modified during development or testing
  • existing installations need one deliberate service restart to install the stable supervisor; subsequent serving-worker updates are restartless
  • future changes to the stable supervisor or IPC protocol still require an explicit service migration/restart

Verification

  • strict TypeScript: pass
  • focused Vitest: 112/112 pass
  • repository Vitest excluding standalone envGuard: 313/313 pass
  • bugfix continuous suite: 101/101 pass
  • env guard suite: 118/118 pass
  • CLI and production builds: pass
  • repository check: pass
  • lint: 0 errors; 42 existing warnings
  • pre-commit validation, security scan, build, publint, and commit-message validation: pass
  • lifecycle benchmark: 25,000 requests, 100,000 events, zero drops
  • transport benchmark: 1,000 requests per path, 0.156 ms added p95
  • rolling benchmark: 500 requests per path, 0.078 ms added p95, 0.075 ms added p95 TTFB, 100 concurrent handoff requests with zero failures, streamed body preserved

Fault coverage

  • candidate startup/readiness/activation failure keeps the old worker active
  • active worker crash keeps the listener alive and retries with capped backoff
  • socket-transfer timeout/failure cannot replay a request or duplicate upstream work
  • malformed handles, early worker messages/exits, state-write failures, queue overflow, and parent disconnect are contained
  • failed package updates reinstall and validate the previous running version before future restarts

Summary by CodeRabbit

  • New Features

    • Added rolling proxy upgrades with supervisor-managed worker generations and direct socket handoff to minimize disruption.
    • Expanded proxy status and handoff/upgrades visibility (serving mode, supervisor/worker details, streaming/candidate phases).
  • Bug Fixes

    • Improved reliability for worker startup failures, recovery, socket transfer/ack timeouts, and graceful draining/termination.
    • Strengthened upgrade activation and rollback when candidates fail validation.
  • Tests

    • Added rolling handoff reliability coverage and updated launch/updater regression assertions.
    • Added a handoff performance benchmark run for rolling upgrades.

@vercel

vercel Bot commented Jul 18, 2026 •

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
neurolink Ready Ready Preview, Comment Jul 19, 2026 9:41am

@coderabbitai

coderabbitai Bot commented Jul 18, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a macOS rolling proxy supervisor with generation-based socket workers, stable-listener handoff, recovery and rollback handling, expanded CLI status/state integration, cross-process tests, and a rolling performance benchmark.

Changes

Rolling proxy worker lifecycle

Layer / File(s) Summary
Rolling worker contracts and state
src/lib/types/*.ts, src/lib/proxy/rollingWorkerProtocol.ts, src/lib/utils/errorHandling.ts
Adds IPC message types, supervisor snapshots, socket-worker runtime types, persisted supervisor state, environment markers, runtime message validation, and lifecycle errors.
Worker spawning, supervision, and socket runtime
src/lib/proxy/rollingWorkerProcess.ts, src/lib/proxy/rollingWorkerSupervisor.ts, src/lib/proxy/socketWorkerRuntime.ts
Implements generation-based worker replacement, socket transfer and queuing, activation, draining, shutdown, failure tracking, and HTTP handling for transferred sockets.
Stable listener and recovery orchestration
src/lib/proxy/rollingProxyServer.ts
Adds a stable TCP listener that forwards sockets to rolling workers and retries failed activation with bounded exponential backoff.
Launchd startup, state, status, and rollback integration
src/cli/commands/proxy.ts
Integrates supervisor startup, socket-worker mode, trampoline execution, SIGUSR2 activation, status reporting, conditional shutdown, rollback, and supervisor-state cleanup.
Fixtures, tests, and performance automation
test/fixtures/proxySocketWorker.cjs, test/proxyRollingWorkerHandoff.test.ts, test/continuous-test-suite-bugfixes.ts, test/proxyReliabilityHardening.test.ts, tools/testing/proxyRollingHandoffBenchmark.ts, package.json
Adds worker fixtures, unit and integration coverage, updated updater assertions, rolling handoff benchmarks, and benchmark script wiring.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ProxyGuard
  participant LaunchdSupervisor
  participant RollingProxyServer
  participant RollingWorkerSupervisor
  participant SocketWorker
  ProxyGuard->>LaunchdSupervisor: request rolling activation
  LaunchdSupervisor->>RollingProxyServer: replace worker version
  RollingProxyServer->>RollingWorkerSupervisor: start candidate
  RollingWorkerSupervisor->>SocketWorker: send activation and sockets
  SocketWorker-->>RollingWorkerSupervisor: report ready, activated, and drained
  RollingWorkerSupervisor-->>RollingProxyServer: publish rolling snapshot
Loading

Possibly related PRs

Suggested reviewers: copilot, tara-ag

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 6.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: zero-downtime rolling worker support for the proxy.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/proxy-rolling-worker-handoff

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@murdore

murdore commented Jul 18, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

Stacking note: PR #1208 is still open and this PR is based on its single commit. The repository policy computes the count against release, so it currently sees two commits. After #1208 merges, this branch will be rebased onto updated origin/release and force-pushed with lease as exactly one commit, then all release-target checks will be rerun. No implementation commit needs squashing into #1208.

@coderabbitai

coderabbitai Bot commented Jul 18, 2026 •

Copy link
Copy Markdown

@murdore Understood—the temporary two-commit count is due to the stack on #1208. I’ll review this PR in that context; the post-merge rebase and release-target validation will establish the final one-commit state.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 12

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/cli/commands/proxy.ts`:
- Around line 4495-4496: Update the uninstall cleanup flow around
clearProxyState and clearProxySupervisorState so supervisor state is not deleted
until launchd shutdown is confirmed. Handle both the missing-plist path and
swallowed launchctl unload failures by failing the uninstall or verifying and
terminating the saved supervisor PID before clearing state.
- Around line 3839-3873: Update the rollback flow surrounding
resolveGlobalInstaller and validateInstalledVersion so that after reinstalling
runningVersion and calling writeTrampoline, it explicitly replaces the active
supervisor generation with runningVersion and waits for that generation to pass
health validation. Ensure rollback completion is reported only after the
supervisor targets and successfully serves runningVersion, including when the
prior replacement is still in flight.

In `@src/lib/proxy/rollingProxyServer.ts`:
- Around line 42-55: Update the explicit replacement flow around
supervisor.replace to cancel any pending recoveryTimer before replacing the
worker, then revalidate the desired version/state before allowing recovery
scheduling or launch to continue. Ensure the recovery timer callback and
replacement path cannot race, and prevent scheduleRecovery from starting another
generation after the requested worker is active. Preserve recoveryFailures
handling for genuine recovery failures.

In `@src/lib/proxy/rollingWorkerSupervisor.ts`:
- Around line 95-112: Replace the generic errors at all three lifecycle failure
sites with typed errors created through ErrorFactory: update replace in
src/lib/proxy/rollingWorkerSupervisor.ts lines 95-112 for closed,
invalid-version, and conflicting-replacement failures; update the worker-spawn
failure in src/lib/proxy/rollingWorkerProcess.ts lines 74-76; and update the
listener-startup failure in src/lib/proxy/rollingProxyServer.ts lines 112-116.
Preserve the existing messages and rejection behavior while using the
appropriate ErrorFactory classifications.
- Around line 440-447: Update flushQueuedSockets to capture this.active in a
local worker reference after the active check and use that same reference for
every transferSocket call while draining queuedSockets. Preserve the existing
early return and timeout cleanup, avoiding repeated reads of this.active after
sendSocket may synchronously clear it.

In `@src/lib/proxy/socketWorkerRuntime.ts`:
- Around line 259-279: The socket worker’s drain handling currently destroys
accepted-but-uncommitted sockets before their commit arrives. Update the message
handling around pendingSockets to track graceful-drain state, reject only
sockets offered after the drain request, and defer runtime.drain() until
existing pending sockets are committed or canceled; preserve normal
commit/cancel behavior and add a delayed-ack regression test covering this
sequence.

In `@test/fixtures/proxySocketWorker.cjs`:
- Around line 76-79: Update the message handler registered by
process.on("message") to destroy the transferred socket before returning when
message.generation does not match generation. Preserve the existing early-return
behavior after ensuring the mismatched-generation socket handle is closed.

In `@test/proxyRollingWorkerHandoff.test.ts`:
- Around line 558-575: Make the “replays readiness emitted before a supervisor
subscribes” test deterministic by adding an explicit synchronization signal from
the worker fixture confirming readiness was emitted, instead of relying on the
100ms sleep. Await that signal before calling handle.onMessage, while preserving
the existing timeout and readiness assertion.
- Around line 142-144: Ensure the integration tests in proxyRollingWorkerHandoff
register spawned workers, servers, and listeners in a cleanup stack as they are
created, then have afterEach close or kill every registered resource even when
assertions or timeouts fail. Preserve the existing successful cleanup behavior
while making teardown unconditional alongside vi.useRealTimers().

In `@tools/testing/proxyRollingHandoffBenchmark.ts`:
- Around line 102-119: Move the startRollingProxyServer initialization into the
try-protected scope so failures during setup reach cleanup, and update the
finally cleanup around rolling.close and direct.close to invoke both
independently with Promise.allSettled. Ensure a rejection from either close does
not prevent the other server from being closed.
- Around line 53-75: Update the request flow around the outgoing request and
response listeners to add a request timeout and reject when the response closes
or is aborted before completing. Preserve the existing end-based metrics
resolution, while ensuring timeout and premature response termination settle the
Promise as failures instead of allowing the benchmark to hang.
- Around line 124-171: Update the benchmark’s CPU and heap measurement around
the rolling path in proxyRollingHandoffBenchmark so it includes metrics from
test/fixtures/proxySocketWorker.cjs, rather than sampling only the parent
process. Aggregate worker CPU and heap usage through IPC, or execute both paths
under a shared process model, and use the combined values when calculating
cpuMicrosPerRequest and heapGrowthBytes.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 2ab924d1-9aeb-4c71-a1fa-51713e31a323

📥 Commits

Reviewing files that changed from the base of the PR and between d37f278 and 099b06f.

📒 Files selected for processing (14)
  • package.json
  • src/cli/commands/proxy.ts
  • src/lib/proxy/rollingProxyServer.ts
  • src/lib/proxy/rollingWorkerProcess.ts
  • src/lib/proxy/rollingWorkerProtocol.ts
  • src/lib/proxy/rollingWorkerSupervisor.ts
  • src/lib/proxy/socketWorkerRuntime.ts
  • src/lib/types/cli.ts
  • src/lib/types/proxy.ts
  • test/continuous-test-suite-bugfixes.ts
  • test/fixtures/proxySocketWorker.cjs
  • test/proxyReliabilityHardening.test.ts
  • test/proxyRollingWorkerHandoff.test.ts
  • tools/testing/proxyRollingHandoffBenchmark.ts

Comment thread src/cli/commands/proxy.ts
Comment thread src/cli/commands/proxy.ts
Comment thread src/lib/proxy/rollingProxyServer.ts
Comment thread src/lib/proxy/rollingWorkerSupervisor.ts
Comment thread src/lib/proxy/rollingWorkerSupervisor.ts
Comment thread test/proxyRollingWorkerHandoff.test.ts Outdated
Comment thread test/proxyRollingWorkerHandoff.test.ts
Comment thread tools/testing/proxyRollingHandoffBenchmark.ts
Comment thread tools/testing/proxyRollingHandoffBenchmark.ts Outdated
Comment thread tools/testing/proxyRollingHandoffBenchmark.ts
@murdore
murdore force-pushed the feat/proxy-rolling-worker-handoff branch from 099b06f to c4b46de Compare July 19, 2026 04:58
@murdore
murdore requested a review from Tara-ag July 19, 2026 09:29
Base automatically changed from fix/proxy-review-debt to release July 19, 2026 09:30
Copilot AI review requested due to automatic review settings July 19, 2026 09:40
@murdore
murdore force-pushed the feat/proxy-rolling-worker-handoff branch from c4b46de to 96bdbd2 Compare July 19, 2026 09:40
@github-actions

Copy link
Copy Markdown
Contributor

✅ Single Commit Policy - COMPLIANT

Status: Policy requirements met • 1 commit • Valid format • Ready for merge

📊 View validation details

📝 Commit Details

  • Hash: 96bdbd235bf4bffe0b59ef70165638d9fdc63fd6
  • Message: feat(proxy): add zero-downtime rolling workers
  • Author: Sachin Sharma

✅ Validation Results

  • Single commit requirement met
  • No merge commits in branch
  • Semantic commit message format verified
  • Ready for squash merge to release branch

🤖 Automated validation by NeuroLink Single Commit Enforcement

@github-actions

Copy link
Copy Markdown
Contributor

🤖 AI Review & Build Compliance ✅

Status: AI analysis complete • Build rules validated • Ready for review

📊 View detailed analysis results

🛡️ Analysis Complete

  • ✅ Security scan (vulnerabilities, API keys)
  • ✅ TypeScript safety & code quality
  • ✅ Error handling & best practices
  • ✅ Build rule enforcement validated
  • ✅ Commit format & compliance checks

📋 Ready for Merge When

  • All CI checks passing
  • Manual review approved
  • Any AI-flagged issues resolved

🤖 AI analysis complete - check individual code comments for specific feedback

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR introduces a zero-downtime “rolling worker” architecture for the proxy: a stable supervisor keeps the public TCP listener bound while swapping serving worker processes via IPC socket transfer + activation handshake, including recovery/rollback support and richer status visibility.

Changes:

  • Added rolling supervisor/worker IPC protocol, socket-worker runtime, and stable rolling proxy listener with recovery backoff.
  • Integrated rolling supervision into the proxy CLI (launchd supervisor mode, updater activation via SIGUSR2, supervisor/worker status reporting, safer uninstall).
  • Added extensive tests/fixtures plus a new rolling handoff performance benchmark and CI wiring.

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
tools/testing/proxyRollingHandoffBenchmark.ts Adds benchmark covering concurrent handoff + stream preservation and overhead budgets.
test/proxyRollingWorkerHandoff.test.ts Adds unit/integration coverage for supervisor lifecycle, socket transfer semantics, draining, and recovery.
test/proxyReliabilityHardening.test.ts Extends invariants to ensure rolling-supervisor plumbing is present in launchd lifecycle source.
test/fixtures/proxySocketWorker.cjs Adds IPC socket-worker fixture used by tests/benchmarks, including metrics logging and commit/cancel handshake.
test/continuous-test-suite-bugfixes.ts Updates regression suite expectations for new activation/validation flow and logging changes.
src/lib/utils/errorHandling.ts Introduces a typed error code/factory helper for rolling worker lifecycle failures.
src/lib/types/proxy.ts Adds protocol + runtime + supervisor/server types for rolling worker handoff.
src/lib/types/cli.ts Adds persisted supervisor state types and records supervisor PID in proxy state.
src/lib/proxy/socketWorkerRuntime.ts Implements accepting transferred sockets into an HTTP server and the worker-side IPC adapter.
src/lib/proxy/rollingWorkerSupervisor.ts Implements worker generation management, activation handshake, socket queueing, drain/shutdown, and failure recording.
src/lib/proxy/rollingWorkerProtocol.ts Defines env flags and type guards for IPC messages.
src/lib/proxy/rollingWorkerProcess.ts Implements spawning IPC workers and the socket offer/accept/commit/cancel handshake.
src/lib/proxy/rollingProxyServer.ts Implements stable listener + recovery loop around the RollingWorkerSupervisor.
src/cli/commands/proxy.ts Integrates supervisor mode, rolling activation, rollback activation, status output enhancements, and uninstall hardening.
package.json Adds rolling benchmark script and includes it in the proxy performance aggregate.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/cli/commands/proxy.ts
Comment on lines +3113 to +3117
status.mode = state
? state.passthrough
? "passthrough"
: "full"
: null;
Comment thread src/cli/commands/proxy.ts
Comment on lines +213 to +217
const args = execFileSync("ps", ["-p", String(pid), "-o", "args="], {
encoding: "utf-8",
stdio: ["ignore", "pipe", "ignore"],
});
return /neurolink/i.test(args);

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/cli/commands/proxy.ts (1)

1637-1639: 🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

Log line remains injectable despite sanitizeForLog. sanitizeForLog (per its definition) only truncates and masks secret patterns — it does not strip \r/\n. Since model is client-controlled (request body) and c.req.path is unsanitized here, a crafted value can inject forged log lines (CWE-117).

🛡️ Strip control characters from interpolated request-derived values
-      const logModel = sanitizeForLog(String(model));
+      const logModel = sanitizeForLog(String(model)).replace(/[\r\n]+/g, " ");
       logger.always(
-        `[proxy] ${c.req.method} ${c.req.path} → model=${logModel} ${stream} tools=${toolCount}`,
+        `[proxy] ${c.req.method} ${c.req.path.replace(/[\r\n]+/g, " ")} → model=${logModel} ${stream} tools=${toolCount}`,
       );
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/cli/commands/proxy.ts` around lines 1637 - 1639, Update the logging call
in the proxy command around logModel to remove carriage returns, newlines, and
other control characters from every request-derived interpolated value,
including model and c.req.path, before passing the message to logger.always.
Preserve the existing sanitizeForLog behavior while ensuring crafted request
values cannot create additional log lines.

Source: Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/lib/proxy/rollingProxyServer.ts`:
- Around line 120-127: Update the address-shape failure branch in the rolling
proxy listener setup to handle supervisor.close() before rethrowing the
ErrorFactory.proxyWorkerLifecycle error, matching the awaited cleanup behavior
of the nearby catch path and preventing a discarded rejection.

---

Outside diff comments:
In `@src/cli/commands/proxy.ts`:
- Around line 1637-1639: Update the logging call in the proxy command around
logModel to remove carriage returns, newlines, and other control characters from
every request-derived interpolated value, including model and c.req.path, before
passing the message to logger.always. Preserve the existing sanitizeForLog
behavior while ensuring crafted request values cannot create additional log
lines.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 52f2f46b-fd7f-4513-9051-d7cb86ac217a

📥 Commits

Reviewing files that changed from the base of the PR and between 099b06f and 96bdbd2.

📒 Files selected for processing (15)
  • package.json
  • src/cli/commands/proxy.ts
  • src/lib/proxy/rollingProxyServer.ts
  • src/lib/proxy/rollingWorkerProcess.ts
  • src/lib/proxy/rollingWorkerProtocol.ts
  • src/lib/proxy/rollingWorkerSupervisor.ts
  • src/lib/proxy/socketWorkerRuntime.ts
  • src/lib/types/cli.ts
  • src/lib/types/proxy.ts
  • src/lib/utils/errorHandling.ts
  • test/continuous-test-suite-bugfixes.ts
  • test/fixtures/proxySocketWorker.cjs
  • test/proxyReliabilityHardening.test.ts
  • test/proxyRollingWorkerHandoff.test.ts
  • tools/testing/proxyRollingHandoffBenchmark.ts
🚧 Files skipped from review as they are similar to previous changes (11)
  • src/lib/proxy/rollingWorkerProtocol.ts
  • package.json
  • src/lib/types/cli.ts
  • test/proxyReliabilityHardening.test.ts
  • test/fixtures/proxySocketWorker.cjs
  • src/lib/types/proxy.ts
  • src/lib/proxy/socketWorkerRuntime.ts
  • test/proxyRollingWorkerHandoff.test.ts
  • test/continuous-test-suite-bugfixes.ts
  • tools/testing/proxyRollingHandoffBenchmark.ts
  • src/lib/proxy/rollingWorkerSupervisor.ts

Comment on lines +120 to +127
const address = listener.address();
if (!address || typeof address === "string") {
listener.close();
void supervisor.close();
throw ErrorFactory.proxyWorkerLifecycle(
"rolling proxy listener did not expose a TCP address",
);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Unhandled rejection risk on the address-shape failure path.

listener.close() is fire-and-forget and void supervisor.close(); discards its promise without a .catch, unlike the .catch block at lines 104-107 which properly awaits supervisor.close() before rethrowing. If supervisor.close() ever rejects here, it becomes an unhandled rejection on top of the thrown ErrorFactory error.

🛡️ Proposed fix
   if (!address || typeof address === "string") {
-    listener.close();
-    void supervisor.close();
+    await new Promise<void>((resolve) => listener.close(() => resolve()));
+    await supervisor.close().catch(() => {});
     throw ErrorFactory.proxyWorkerLifecycle(
       "rolling proxy listener did not expose a TCP address",
     );
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const address = listener.address();
if (!address || typeof address === "string") {
listener.close();
void supervisor.close();
throw ErrorFactory.proxyWorkerLifecycle(
"rolling proxy listener did not expose a TCP address",
);
}
const address = listener.address();
if (!address || typeof address === "string") {
await new Promise<void>((resolve) => listener.close(() => resolve()));
await supervisor.close().catch(() => {});
throw ErrorFactory.proxyWorkerLifecycle(
"rolling proxy listener did not expose a TCP address",
);
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/proxy/rollingProxyServer.ts` around lines 120 - 127, Update the
address-shape failure branch in the rolling proxy listener setup to handle
supervisor.close() before rethrowing the ErrorFactory.proxyWorkerLifecycle
error, matching the awaited cleanup behavior of the nearby catch path and
preventing a discarded rejection.

@murdore
murdore merged commit 93e2067 into release Jul 19, 2026
18 checks passed
@murdore
murdore deleted the feat/proxy-rolling-worker-handoff branch July 19, 2026 10:02
@github-actions

Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 9.94.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

This branch was successfully deployed

1 active deployment
Preview — 96bdbd23 Deployed Jul 19, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants