Skip to content

Remove residual @MainActor overhead from nonisolated network layer#21806

Merged
ashleeradka merged 3 commits into
mainfrom
devin/1774560439-btwclient-task-isolation
Mar 26, 2026
Merged

Remove residual @MainActor overhead from nonisolated network layer#21806
ashleeradka merged 3 commits into
mainfrom
devin/1774560439-btwclient-task-isolation

Conversation

@devin-ai-integration

@devin-ai-integration devin-ai-integration Bot commented Mar 26, 2026

Copy link
Copy Markdown
Contributor

Summary

After #21729 moved the network layer off @MainActor, two residual isolation issues remained:

1. Unnecessary main-thread hop in BtwClient.sendMessage()
The Task { @MainActor in ... } wrapper forces every BTW streaming request onto the main thread, even though BtwClient and all downstream methods (streamBtw(), GatewayHTTPClient.streamPostWithRetry()) are now nonisolated. Changed to Task { ... } so the work runs on the cooperative thread pool.

Why this matters: Every BTW side-chain message unnecessarily blocked the main thread for the duration of SSE stream setup. With the network layer nonisolated, this hop has zero purpose and adds latency + main-thread contention.

2. Swift 6 cross-isolation errors in AuthService.resolveBaseURL()
The nonisolated functions resolveBaseURL() and normalizedBaseURL() reference platformURLOverrideEnvironmentKey, authServiceBaseURLDefaultsName, and defaultBaseURL — all private static let on the @MainActor class. Swift 6 language mode treats this as a cross-isolation error because static properties on a @MainActor class inherit actor isolation, even if they are immutable.

Fix: Move the three constants to module-level private let declarations (nonisolated by default). The values are unchanged — only their declaration site moved.

Why this is safe:

  • BtwClient: streamBtw() only calls GatewayHTTPClient.streamPostWithRetry() (nonisolated), JSONSerialization (thread-safe), and AsyncThrowingStream.Continuation (Sendable). No MainActor-dependent code.
  • AuthService: All three constants are immutable let values initialized once. Moving them to module scope changes when they initialize (module load vs. class static init) but this is equivalent — the values are compile-time #if DEBUG branches with no runtime dependencies.

References:

Review & Testing Checklist for Human

  • Build in Xcode with Swift 6 language mode — verify the three cross-isolation warnings (platformURLOverrideEnvironmentKey, authServiceBaseURLDefaultsName, defaultBaseURL) are gone and no new warnings appear.
  • Verify BTW side-chain streaming works — send a BTW message and confirm text deltas stream back correctly. This is the only code path affected by the Task change.
  • Verify base URL resolution — confirm the app connects to the correct backend. Test that VELLUM_PLATFORM_URL environment override and debug UserDefaults fallback still resolve correctly.

Notes

  • 2 files changed, no logic changes — only where constants are declared and which Task annotation is used.
  • All CI checks skipped (macOS/Xcode project, no macOS runners). Local Xcode build is the primary validation gate.

Link to Devin session: https://app.devin.ai/sessions/e19a9c46f61546368efaba6020397e17
Requested by: @ashleeradka


Open with Devin

BtwClient is no longer @mainactor (removed in #21729), and streamBtw()
only calls nonisolated GatewayHTTPClient methods. The Task { @mainactor in }
was adding a needless main-thread hop. Changed to Task { } so the
streaming work runs on the cooperative thread pool.

Co-Authored-By: ashlee@vellum.ai <ashlee@vellum.ai>
@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

…mpatibility

The nonisolated resolveBaseURL() and normalizedBaseURL() functions
reference platformURLOverrideEnvironmentKey, authServiceBaseURLDefaultsName,
and defaultBaseURL — all static let properties on the @mainactor class.
In Swift 6 language mode, this is an error (cross-isolation access).

Move the constant values to module-level private lets (nonisolated by
default) and alias them back into the class for backward compatibility.
The nonisolated functions now reference the module-level constants directly.

Co-Authored-By: ashlee@vellum.ai <ashlee@vellum.ai>
@devin-ai-integration devin-ai-integration Bot changed the title Remove unnecessary @MainActor hop from BtwClient.sendMessage() Follow-up fixes from #21729: BtwClient task isolation and AuthService Swift 6 warnings Mar 26, 2026
devin-ai-integration[bot]

This comment was marked as resolved.

The class-level private static lets (platformURLOverrideEnvironmentKey,
authServiceBaseURLDefaultsName, defaultBaseURL) are unused — all
consumers in resolveBaseURL() now reference the module-level constants
directly.

Co-Authored-By: ashlee@vellum.ai <ashlee@vellum.ai>
@devin-ai-integration devin-ai-integration Bot changed the title Follow-up fixes from #21729: BtwClient task isolation and AuthService Swift 6 warnings Remove residual @MainActor overhead from nonisolated network layer Mar 26, 2026
@ashleeradka ashleeradka merged commit e59ff7c into main Mar 26, 2026
4 checks passed
@ashleeradka ashleeradka deleted the devin/1774560439-btwclient-task-isolation branch March 26, 2026 21:36
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.

1 participant