feat(mobile): keep Android connections alive in the background - #5179

Open
snipemanmike wants to merge 12 commits into
pingdotgg:mainfrom
snipemanmike:feat/android-background-connection
Open

feat(mobile): keep Android connections alive in the background#5179
snipemanmike wants to merge 12 commits into
pingdotgg:mainfrom
snipemanmike:feat/android-background-connection

Conversation

@snipemanmike

@snipemanmikesnipemanmike commented Aug 1, 2026

Copy link
Copy Markdown

What Changed

Android currently suspends the JavaScript connection runtime when T3 Code is backgrounded or the phone is locked. Reopening the app can therefore require a full reconnect and synchronization before current thread state appears.

This PR adds an Android-only, opt-in background connection mode that:

  • runs the existing client runtime from a foreground service backed by React Native Headless JS
  • shares the existing atom registry, connection supervisors, subscriptions, and outbox dispatcher instead of creating a second WebSocket stack
  • keeps every saved environment's shell state current while retaining full detail only for the last-opened and starting/running threads
  • preserves managed-relay authentication for cold T3 Connect startup
  • probes healthy sessions on resume without forcing reconnects or resubscriptions
  • restores the service after ordinary process reclamation, task swipe-away, package replacement, and reboot after unlock
  • exposes an Android setting and the platform-required silent foreground-service notification

This does not change the WebSocket protocol, synchronization reducer, server contracts, or server behavior.

The recovery work in #5154 improves what happens after Android has suspended the client. This PR addresses the preceding problem: while the setting is enabled, it prevents Android suspension from stopping the connection runtime in the first place. The approaches remain compatible.

Why

Mobile is commonly used to monitor or continue work running on another machine through a direct connection, Tailscale, or T3 Connect. Keeping the existing runtime alive means events can arrive while the app is backgrounded, outgoing messages continue draining, and reopening can render current state immediately rather than beginning a reconnect cycle.

The feature defaults off. Android force-stop remains an unavoidable boundary, and users must launch the app once afterward.

Verification

  • 18 focused test files passed (114 tests)
  • mobile and client-runtime typechecks passed
  • mobile lint passed
  • native module unit tests and Android release lint passed
  • arm64 production release APK built successfully, matched the expected signing certificate, and installed with replacement semantics without clearing app data
  • physical Android validation covered direct/Tailscale operation while backgrounded and locked, forced Doze, task swipe-away, Wi-Fi/cellular recovery, package replacement, and reboot restoration
  • T3 Connect ownership and cold-bootstrap paths are covered by focused tests; live T3 Connect validation was intentionally skipped
  • a clean emulator was used for UI evidence; it contains no account or personal data

UI Changes

Before:

Settings before

After:

Settings after

Interaction video (sanitized emulator)

Checklist

  • This PR is small and focused
  • I explained what changed and why
  • I included before/after screenshots for any UI changes
  • I included a video for animation/interaction changes

This is one focused Android runtime concern, but it is not a small diff; the native lifecycle, shared runtime ownership, relay authentication, resume semantics, tests, and documentation must land together to avoid partial behavior.

Implemented with GPT-5.6-sol through the Codex harness. Reviewed with Claude Fable through the Claude Code CLI.


Note

High Risk
Large cross-cutting change to Android FGS lifecycle, shared connection/outbox state, and managed-relay auth across UI unmount and account switches—regressions could affect credentials, reconnect behavior, or background message delivery.

Overview
Adds an opt-in Android “Keep connected in background” mode that keeps the existing mobile client runtime alive via a remoteMessaging foreground service and a single React Native Headless JS task—no second WebSocket or sync stack.

The new t3-background-connection Expo module owns native lifecycle: persistent enablement, ongoing notification, Wi‑Fi lock, boot/package-replace recovery, exponential restart backoff (only when battery optimization is ignored), and orderly stop when the user turns the feature off. JS registers the headless task at startup and coordinates background-root leases (environment catalog/shells, server configs, thread detail for the retained + active threads) plus acquireThreadOutboxDrain so queued messages still drain without a mounted UI.

T3 Connect / relay gains separate UI vs background managed-relay session ownership and backgroundManagedRelayAuth (cold Clerk bootstrap, holds during account transitions, retries). CloudAuthProvider no longer clears relay on UI unmount when background still owns the session. Returning to the app uses application-active-preserved when the native service and JS runtime are both ready, avoiding forced reconnects.

Settings expose the toggle, status, and optional battery-exemption prompt; the active thread is persisted as the retained target and cleared when environments/threads are removed or caches are wiped.

Reviewed by Cursor Bugbot for commit b36b040. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Add Android background connection with foreground service and shared outbox dispatcher

  • Adds the t3-background-connection Expo native module with a sticky foreground service, headless JS task, broadcast receiver (boot/package-replace/restart), state manager, exponential restart backoff (1s–5min), and battery-optimization exemption support
  • Adds JS background infrastructure: background task lifecycle, retained-thread persistence (single-flight load, serialized saves/clears), target selection, background relay-auth bootstrap with epoch invalidation and transition holds, and lease-based UI/background managed-relay session ownership
  • Refactors the thread outbox drain from React hooks to a registry-backed shared dispatcher (acquireThreadOutboxDrain) that reference-counts owners, coalesces drain requests, and retries through state-backed timers
  • Adds application-active-preserved wakeup type so EnvironmentSupervisor probes the connected session instead of reconnecting when both the native service and runtime are ready
  • Integrates into the app entrypoint, App.tsx startup coordinator, RootStackLayout retained-thread tracking, and Android-only settings UI with battery-exemption prompting
  • Risk: CloudAuthProvider account deactivation now invalidates background relay auth and clears both UI and background ownership; account switches suppress intermediate refresh. The outbox drain lifecycle changed from per-component to registry-scoped shared leases — multiple hook mounts share one dispatcher, and the final release stops drains, cancels retries, and unsubscribes listeners

Macroscope summarized b36b040.

@coderabbitai

coderabbitaiBot commented Aug 1, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 6f807a1b-4f6d-42c6-a833-ef045a6e163d

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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.

@github-actionsgithub-actionsBot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:XXL 1,000+ changed lines (additions + deletions). labels Aug 1, 2026
Comment threadapps/mobile/src/features/cloud/backgroundManagedRelayAuth.ts
@macroscopeapp

macroscopeappBot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This PR introduces a substantial Android background service and headless runtime that changes connection, authentication, outbox, persistence, and resume behavior, with intentional battery, data, and notification side effects. It also adds production lint suppressions, so the cross-cutting implementation requires human review.

You can add or adjust custom eligibility rules. Learn more.

@snipemanmike
snipemanmikeforce-pushed the feat/android-background-connection branch from 81c65f7 to e25b8e5CompareAugust 6, 2026 03:27
@juliusmarminge
juliusmarmingeforce-pushed the feat/android-background-connection branch from e25b8e5 to f258f81CompareAugust 10, 2026 10:58

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Reviewed the Effect-service surface of this PR (new MobileStorage operations, the imperative persistence bridge, and the touched client-runtime connection modules). The service definition order, inline Context.Service interface, make/layer exports, Foo["Service"] usage, and namespace subpath imports all follow the conventions; the Promise-based background modules stay on the imperative-adapter side and are not consumed by Effect services. One log-annotation nit noted inline.

Posted via Macroscope — Effect Service Conventions

Comment threadapps/mobile/src/persistence/mobile-storage.ts Outdated
camerontaylor added a commit to camerontaylor/t3code that referenced this pull request Aug 18, 2026
Keeps the mobile app connected to its environment while backgrounded on
Android via a foreground service and headless task (upstream PR pingdotgg#5179).
Carried until it lands upstream.
# Conflicts:
#	apps/mobile/src/App.tsx
#	apps/mobile/src/features/settings/SettingsRouteScreen.tsx
#	apps/mobile/src/state/use-thread-outbox-drain.ts
snipemanmikeand others added 11 commits September 1, 2026 19:42
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
@snipemanmike
snipemanmikeforce-pushed the feat/android-background-connection branch from 7b3cd80 to c7d27a5CompareSeptember 2, 2026 02:54

@cursorcursorBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want fixes drafted automatically? Bugbot Autofix can create code changes for findings. A team admin can enable Autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit c7d27a5. Configure here.

Comment threadapps/mobile/src/features/cloud/backgroundManagedRelayAuth.ts
Account switches invalidated the background auth epoch and deferred
explicit refreshes until the previous account's cleanup settled, but a
scheduled retry or a cold headless start could still bootstrap in that
window, read the new Clerk account, and publish its relay session before
environment removal and the token-cache reset finished.
Every bootstrap attempt now waits behind the pending sign-out cleanup and
account activation, while still capturing its epoch synchronously so an
invalidation issued right after it starts continues to supersede it.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL1,000+ changed lines (additions + deletions).vouch:unvouchedPR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@snipemanmike@juliusmarminge
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

feat(mobile): keep Android connections alive in the background - #5179

Open
snipemanmike wants to merge 12 commits into
pingdotgg:mainfrom
snipemanmike:feat/android-background-connection
Open

feat(mobile): keep Android connections alive in the background#5179
snipemanmike wants to merge 12 commits into
pingdotgg:mainfrom
snipemanmike:feat/android-background-connection

Conversation

@snipemanmike

@snipemanmikesnipemanmike commented Aug 1, 2026

Copy link
Copy Markdown

What Changed

Android currently suspends the JavaScript connection runtime when T3 Code is backgrounded or the phone is locked. Reopening the app can therefore require a full reconnect and synchronization before current thread state appears.

This PR adds an Android-only, opt-in background connection mode that:

  • runs the existing client runtime from a foreground service backed by React Native Headless JS
  • shares the existing atom registry, connection supervisors, subscriptions, and outbox dispatcher instead of creating a second WebSocket stack
  • keeps every saved environment's shell state current while retaining full detail only for the last-opened and starting/running threads
  • preserves managed-relay authentication for cold T3 Connect startup
  • probes healthy sessions on resume without forcing reconnects or resubscriptions
  • restores the service after ordinary process reclamation, task swipe-away, package replacement, and reboot after unlock
  • exposes an Android setting and the platform-required silent foreground-service notification

This does not change the WebSocket protocol, synchronization reducer, server contracts, or server behavior.

The recovery work in #5154 improves what happens after Android has suspended the client. This PR addresses the preceding problem: while the setting is enabled, it prevents Android suspension from stopping the connection runtime in the first place. The approaches remain compatible.

Why

Mobile is commonly used to monitor or continue work running on another machine through a direct connection, Tailscale, or T3 Connect. Keeping the existing runtime alive means events can arrive while the app is backgrounded, outgoing messages continue draining, and reopening can render current state immediately rather than beginning a reconnect cycle.

The feature defaults off. Android force-stop remains an unavoidable boundary, and users must launch the app once afterward.

Verification

  • 18 focused test files passed (114 tests)
  • mobile and client-runtime typechecks passed
  • mobile lint passed
  • native module unit tests and Android release lint passed
  • arm64 production release APK built successfully, matched the expected signing certificate, and installed with replacement semantics without clearing app data
  • physical Android validation covered direct/Tailscale operation while backgrounded and locked, forced Doze, task swipe-away, Wi-Fi/cellular recovery, package replacement, and reboot restoration
  • T3 Connect ownership and cold-bootstrap paths are covered by focused tests; live T3 Connect validation was intentionally skipped
  • a clean emulator was used for UI evidence; it contains no account or personal data

UI Changes

Before:

Settings before

After:

Settings after

Interaction video (sanitized emulator)

Checklist

  • This PR is small and focused
  • I explained what changed and why
  • I included before/after screenshots for any UI changes
  • I included a video for animation/interaction changes

This is one focused Android runtime concern, but it is not a small diff; the native lifecycle, shared runtime ownership, relay authentication, resume semantics, tests, and documentation must land together to avoid partial behavior.

Implemented with GPT-5.6-sol through the Codex harness. Reviewed with Claude Fable through the Claude Code CLI.


Note

High Risk
Large cross-cutting change to Android FGS lifecycle, shared connection/outbox state, and managed-relay auth across UI unmount and account switches—regressions could affect credentials, reconnect behavior, or background message delivery.

Overview
Adds an opt-in Android “Keep connected in background” mode that keeps the existing mobile client runtime alive via a remoteMessaging foreground service and a single React Native Headless JS task—no second WebSocket or sync stack.

The new t3-background-connection Expo module owns native lifecycle: persistent enablement, ongoing notification, Wi‑Fi lock, boot/package-replace recovery, exponential restart backoff (only when battery optimization is ignored), and orderly stop when the user turns the feature off. JS registers the headless task at startup and coordinates background-root leases (environment catalog/shells, server configs, thread detail for the retained + active threads) plus acquireThreadOutboxDrain so queued messages still drain without a mounted UI.

T3 Connect / relay gains separate UI vs background managed-relay session ownership and backgroundManagedRelayAuth (cold Clerk bootstrap, holds during account transitions, retries). CloudAuthProvider no longer clears relay on UI unmount when background still owns the session. Returning to the app uses application-active-preserved when the native service and JS runtime are both ready, avoiding forced reconnects.

Settings expose the toggle, status, and optional battery-exemption prompt; the active thread is persisted as the retained target and cleared when environments/threads are removed or caches are wiped.

Reviewed by Cursor Bugbot for commit b36b040. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Add Android background connection with foreground service and shared outbox dispatcher

  • Adds the t3-background-connection Expo native module with a sticky foreground service, headless JS task, broadcast receiver (boot/package-replace/restart), state manager, exponential restart backoff (1s–5min), and battery-optimization exemption support
  • Adds JS background infrastructure: background task lifecycle, retained-thread persistence (single-flight load, serialized saves/clears), target selection, background relay-auth bootstrap with epoch invalidation and transition holds, and lease-based UI/background managed-relay session ownership
  • Refactors the thread outbox drain from React hooks to a registry-backed shared dispatcher (acquireThreadOutboxDrain) that reference-counts owners, coalesces drain requests, and retries through state-backed timers
  • Adds application-active-preserved wakeup type so EnvironmentSupervisor probes the connected session instead of reconnecting when both the native service and runtime are ready
  • Integrates into the app entrypoint, App.tsx startup coordinator, RootStackLayout retained-thread tracking, and Android-only settings UI with battery-exemption prompting
  • Risk: CloudAuthProvider account deactivation now invalidates background relay auth and clears both UI and background ownership; account switches suppress intermediate refresh. The outbox drain lifecycle changed from per-component to registry-scoped shared leases — multiple hook mounts share one dispatcher, and the final release stops drains, cancels retries, and unsubscribes listeners

Macroscope summarized b36b040.

@coderabbitai

coderabbitaiBot commented Aug 1, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 6f807a1b-4f6d-42c6-a833-ef045a6e163d

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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.

@github-actionsgithub-actionsBot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:XXL 1,000+ changed lines (additions + deletions). labels Aug 1, 2026
Comment threadapps/mobile/src/features/cloud/backgroundManagedRelayAuth.ts
@macroscopeapp

macroscopeappBot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This PR introduces a substantial Android background service and headless runtime that changes connection, authentication, outbox, persistence, and resume behavior, with intentional battery, data, and notification side effects. It also adds production lint suppressions, so the cross-cutting implementation requires human review.

You can add or adjust custom eligibility rules. Learn more.

@snipemanmike
snipemanmikeforce-pushed the feat/android-background-connection branch from 81c65f7 to e25b8e5CompareAugust 6, 2026 03:27
@juliusmarminge
juliusmarmingeforce-pushed the feat/android-background-connection branch from e25b8e5 to f258f81CompareAugust 10, 2026 10:58

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Reviewed the Effect-service surface of this PR (new MobileStorage operations, the imperative persistence bridge, and the touched client-runtime connection modules). The service definition order, inline Context.Service interface, make/layer exports, Foo["Service"] usage, and namespace subpath imports all follow the conventions; the Promise-based background modules stay on the imperative-adapter side and are not consumed by Effect services. One log-annotation nit noted inline.

Posted via Macroscope — Effect Service Conventions

Comment threadapps/mobile/src/persistence/mobile-storage.ts Outdated
camerontaylor added a commit to camerontaylor/t3code that referenced this pull request Aug 18, 2026
Keeps the mobile app connected to its environment while backgrounded on
Android via a foreground service and headless task (upstream PR pingdotgg#5179).
Carried until it lands upstream.
# Conflicts:
#	apps/mobile/src/App.tsx
#	apps/mobile/src/features/settings/SettingsRouteScreen.tsx
#	apps/mobile/src/state/use-thread-outbox-drain.ts
snipemanmikeand others added 11 commits September 1, 2026 19:42
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
@snipemanmike
snipemanmikeforce-pushed the feat/android-background-connection branch from 7b3cd80 to c7d27a5CompareSeptember 2, 2026 02:54

@cursorcursorBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want fixes drafted automatically? Bugbot Autofix can create code changes for findings. A team admin can enable Autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit c7d27a5. Configure here.

Comment threadapps/mobile/src/features/cloud/backgroundManagedRelayAuth.ts
Account switches invalidated the background auth epoch and deferred
explicit refreshes until the previous account's cleanup settled, but a
scheduled retry or a cold headless start could still bootstrap in that
window, read the new Clerk account, and publish its relay session before
environment removal and the token-cache reset finished.
Every bootstrap attempt now waits behind the pending sign-out cleanup and
account activation, while still capturing its epoch synchronously so an
invalidation issued right after it starts continues to supersede it.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL1,000+ changed lines (additions + deletions).vouch:unvouchedPR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@snipemanmike@juliusmarminge
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(mobile): keep Android connections alive in the background - #5179

Open
snipemanmike wants to merge 12 commits into
pingdotgg:mainfrom
snipemanmike:feat/android-background-connection
Open

feat(mobile): keep Android connections alive in the background#5179
snipemanmike wants to merge 12 commits into
pingdotgg:mainfrom
snipemanmike:feat/android-background-connection

Conversation

@snipemanmike

@snipemanmikesnipemanmike commented Aug 1, 2026

Copy link
Copy Markdown

What Changed

Android currently suspends the JavaScript connection runtime when T3 Code is backgrounded or the phone is locked. Reopening the app can therefore require a full reconnect and synchronization before current thread state appears.

This PR adds an Android-only, opt-in background connection mode that:

  • runs the existing client runtime from a foreground service backed by React Native Headless JS
  • shares the existing atom registry, connection supervisors, subscriptions, and outbox dispatcher instead of creating a second WebSocket stack
  • keeps every saved environment's shell state current while retaining full detail only for the last-opened and starting/running threads
  • preserves managed-relay authentication for cold T3 Connect startup
  • probes healthy sessions on resume without forcing reconnects or resubscriptions
  • restores the service after ordinary process reclamation, task swipe-away, package replacement, and reboot after unlock
  • exposes an Android setting and the platform-required silent foreground-service notification

This does not change the WebSocket protocol, synchronization reducer, server contracts, or server behavior.

The recovery work in #5154 improves what happens after Android has suspended the client. This PR addresses the preceding problem: while the setting is enabled, it prevents Android suspension from stopping the connection runtime in the first place. The approaches remain compatible.

Why

Mobile is commonly used to monitor or continue work running on another machine through a direct connection, Tailscale, or T3 Connect. Keeping the existing runtime alive means events can arrive while the app is backgrounded, outgoing messages continue draining, and reopening can render current state immediately rather than beginning a reconnect cycle.

The feature defaults off. Android force-stop remains an unavoidable boundary, and users must launch the app once afterward.

Verification

  • 18 focused test files passed (114 tests)
  • mobile and client-runtime typechecks passed
  • mobile lint passed
  • native module unit tests and Android release lint passed
  • arm64 production release APK built successfully, matched the expected signing certificate, and installed with replacement semantics without clearing app data
  • physical Android validation covered direct/Tailscale operation while backgrounded and locked, forced Doze, task swipe-away, Wi-Fi/cellular recovery, package replacement, and reboot restoration
  • T3 Connect ownership and cold-bootstrap paths are covered by focused tests; live T3 Connect validation was intentionally skipped
  • a clean emulator was used for UI evidence; it contains no account or personal data

UI Changes

Before:

Settings before

After:

Settings after

Interaction video (sanitized emulator)

Checklist

  • This PR is small and focused
  • I explained what changed and why
  • I included before/after screenshots for any UI changes
  • I included a video for animation/interaction changes

This is one focused Android runtime concern, but it is not a small diff; the native lifecycle, shared runtime ownership, relay authentication, resume semantics, tests, and documentation must land together to avoid partial behavior.

Implemented with GPT-5.6-sol through the Codex harness. Reviewed with Claude Fable through the Claude Code CLI.


Note

High Risk
Large cross-cutting change to Android FGS lifecycle, shared connection/outbox state, and managed-relay auth across UI unmount and account switches—regressions could affect credentials, reconnect behavior, or background message delivery.

Overview
Adds an opt-in Android “Keep connected in background” mode that keeps the existing mobile client runtime alive via a remoteMessaging foreground service and a single React Native Headless JS task—no second WebSocket or sync stack.

The new t3-background-connection Expo module owns native lifecycle: persistent enablement, ongoing notification, Wi‑Fi lock, boot/package-replace recovery, exponential restart backoff (only when battery optimization is ignored), and orderly stop when the user turns the feature off. JS registers the headless task at startup and coordinates background-root leases (environment catalog/shells, server configs, thread detail for the retained + active threads) plus acquireThreadOutboxDrain so queued messages still drain without a mounted UI.

T3 Connect / relay gains separate UI vs background managed-relay session ownership and backgroundManagedRelayAuth (cold Clerk bootstrap, holds during account transitions, retries). CloudAuthProvider no longer clears relay on UI unmount when background still owns the session. Returning to the app uses application-active-preserved when the native service and JS runtime are both ready, avoiding forced reconnects.

Settings expose the toggle, status, and optional battery-exemption prompt; the active thread is persisted as the retained target and cleared when environments/threads are removed or caches are wiped.

Reviewed by Cursor Bugbot for commit b36b040. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Add Android background connection with foreground service and shared outbox dispatcher

  • Adds the t3-background-connection Expo native module with a sticky foreground service, headless JS task, broadcast receiver (boot/package-replace/restart), state manager, exponential restart backoff (1s–5min), and battery-optimization exemption support
  • Adds JS background infrastructure: background task lifecycle, retained-thread persistence (single-flight load, serialized saves/clears), target selection, background relay-auth bootstrap with epoch invalidation and transition holds, and lease-based UI/background managed-relay session ownership
  • Refactors the thread outbox drain from React hooks to a registry-backed shared dispatcher (acquireThreadOutboxDrain) that reference-counts owners, coalesces drain requests, and retries through state-backed timers
  • Adds application-active-preserved wakeup type so EnvironmentSupervisor probes the connected session instead of reconnecting when both the native service and runtime are ready
  • Integrates into the app entrypoint, App.tsx startup coordinator, RootStackLayout retained-thread tracking, and Android-only settings UI with battery-exemption prompting
  • Risk: CloudAuthProvider account deactivation now invalidates background relay auth and clears both UI and background ownership; account switches suppress intermediate refresh. The outbox drain lifecycle changed from per-component to registry-scoped shared leases — multiple hook mounts share one dispatcher, and the final release stops drains, cancels retries, and unsubscribes listeners

Macroscope summarized b36b040.

@coderabbitai

coderabbitaiBot commented Aug 1, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 6f807a1b-4f6d-42c6-a833-ef045a6e163d

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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.

@github-actionsgithub-actionsBot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:XXL 1,000+ changed lines (additions + deletions). labels Aug 1, 2026
Comment threadapps/mobile/src/features/cloud/backgroundManagedRelayAuth.ts
@macroscopeapp

macroscopeappBot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This PR introduces a substantial Android background service and headless runtime that changes connection, authentication, outbox, persistence, and resume behavior, with intentional battery, data, and notification side effects. It also adds production lint suppressions, so the cross-cutting implementation requires human review.

You can add or adjust custom eligibility rules. Learn more.

@snipemanmike
snipemanmikeforce-pushed the feat/android-background-connection branch from 81c65f7 to e25b8e5CompareAugust 6, 2026 03:27
@juliusmarminge
juliusmarmingeforce-pushed the feat/android-background-connection branch from e25b8e5 to f258f81CompareAugust 10, 2026 10:58

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Reviewed the Effect-service surface of this PR (new MobileStorage operations, the imperative persistence bridge, and the touched client-runtime connection modules). The service definition order, inline Context.Service interface, make/layer exports, Foo["Service"] usage, and namespace subpath imports all follow the conventions; the Promise-based background modules stay on the imperative-adapter side and are not consumed by Effect services. One log-annotation nit noted inline.

Posted via Macroscope — Effect Service Conventions

Comment threadapps/mobile/src/persistence/mobile-storage.ts Outdated
camerontaylor added a commit to camerontaylor/t3code that referenced this pull request Aug 18, 2026
Keeps the mobile app connected to its environment while backgrounded on
Android via a foreground service and headless task (upstream PR pingdotgg#5179).
Carried until it lands upstream.
# Conflicts:
#	apps/mobile/src/App.tsx
#	apps/mobile/src/features/settings/SettingsRouteScreen.tsx
#	apps/mobile/src/state/use-thread-outbox-drain.ts
snipemanmikeand others added 11 commits September 1, 2026 19:42
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
@snipemanmike
snipemanmikeforce-pushed the feat/android-background-connection branch from 7b3cd80 to c7d27a5CompareSeptember 2, 2026 02:54

@cursorcursorBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want fixes drafted automatically? Bugbot Autofix can create code changes for findings. A team admin can enable Autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit c7d27a5. Configure here.

Comment threadapps/mobile/src/features/cloud/backgroundManagedRelayAuth.ts
Account switches invalidated the background auth epoch and deferred
explicit refreshes until the previous account's cleanup settled, but a
scheduled retry or a cold headless start could still bootstrap in that
window, read the new Clerk account, and publish its relay session before
environment removal and the token-cache reset finished.
Every bootstrap attempt now waits behind the pending sign-out cleanup and
account activation, while still capturing its epoch synchronously so an
invalidation issued right after it starts continues to supersede it.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL1,000+ changed lines (additions + deletions).vouch:unvouchedPR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@snipemanmike@juliusmarminge
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(mobile): keep Android connections alive in the background - #5179

Open
snipemanmike wants to merge 12 commits into
pingdotgg:mainfrom
snipemanmike:feat/android-background-connection
Open

feat(mobile): keep Android connections alive in the background#5179
snipemanmike wants to merge 12 commits into
pingdotgg:mainfrom
snipemanmike:feat/android-background-connection

Conversation

@snipemanmike

@snipemanmikesnipemanmike commented Aug 1, 2026

Copy link
Copy Markdown

What Changed

Android currently suspends the JavaScript connection runtime when T3 Code is backgrounded or the phone is locked. Reopening the app can therefore require a full reconnect and synchronization before current thread state appears.

This PR adds an Android-only, opt-in background connection mode that:

  • runs the existing client runtime from a foreground service backed by React Native Headless JS
  • shares the existing atom registry, connection supervisors, subscriptions, and outbox dispatcher instead of creating a second WebSocket stack
  • keeps every saved environment's shell state current while retaining full detail only for the last-opened and starting/running threads
  • preserves managed-relay authentication for cold T3 Connect startup
  • probes healthy sessions on resume without forcing reconnects or resubscriptions
  • restores the service after ordinary process reclamation, task swipe-away, package replacement, and reboot after unlock
  • exposes an Android setting and the platform-required silent foreground-service notification

This does not change the WebSocket protocol, synchronization reducer, server contracts, or server behavior.

The recovery work in #5154 improves what happens after Android has suspended the client. This PR addresses the preceding problem: while the setting is enabled, it prevents Android suspension from stopping the connection runtime in the first place. The approaches remain compatible.

Why

Mobile is commonly used to monitor or continue work running on another machine through a direct connection, Tailscale, or T3 Connect. Keeping the existing runtime alive means events can arrive while the app is backgrounded, outgoing messages continue draining, and reopening can render current state immediately rather than beginning a reconnect cycle.

The feature defaults off. Android force-stop remains an unavoidable boundary, and users must launch the app once afterward.

Verification

  • 18 focused test files passed (114 tests)
  • mobile and client-runtime typechecks passed
  • mobile lint passed
  • native module unit tests and Android release lint passed
  • arm64 production release APK built successfully, matched the expected signing certificate, and installed with replacement semantics without clearing app data
  • physical Android validation covered direct/Tailscale operation while backgrounded and locked, forced Doze, task swipe-away, Wi-Fi/cellular recovery, package replacement, and reboot restoration
  • T3 Connect ownership and cold-bootstrap paths are covered by focused tests; live T3 Connect validation was intentionally skipped
  • a clean emulator was used for UI evidence; it contains no account or personal data

UI Changes

Before:

Settings before

After:

Settings after

Interaction video (sanitized emulator)

Checklist

  • This PR is small and focused
  • I explained what changed and why
  • I included before/after screenshots for any UI changes
  • I included a video for animation/interaction changes

This is one focused Android runtime concern, but it is not a small diff; the native lifecycle, shared runtime ownership, relay authentication, resume semantics, tests, and documentation must land together to avoid partial behavior.

Implemented with GPT-5.6-sol through the Codex harness. Reviewed with Claude Fable through the Claude Code CLI.


Note

High Risk
Large cross-cutting change to Android FGS lifecycle, shared connection/outbox state, and managed-relay auth across UI unmount and account switches—regressions could affect credentials, reconnect behavior, or background message delivery.

Overview
Adds an opt-in Android “Keep connected in background” mode that keeps the existing mobile client runtime alive via a remoteMessaging foreground service and a single React Native Headless JS task—no second WebSocket or sync stack.

The new t3-background-connection Expo module owns native lifecycle: persistent enablement, ongoing notification, Wi‑Fi lock, boot/package-replace recovery, exponential restart backoff (only when battery optimization is ignored), and orderly stop when the user turns the feature off. JS registers the headless task at startup and coordinates background-root leases (environment catalog/shells, server configs, thread detail for the retained + active threads) plus acquireThreadOutboxDrain so queued messages still drain without a mounted UI.

T3 Connect / relay gains separate UI vs background managed-relay session ownership and backgroundManagedRelayAuth (cold Clerk bootstrap, holds during account transitions, retries). CloudAuthProvider no longer clears relay on UI unmount when background still owns the session. Returning to the app uses application-active-preserved when the native service and JS runtime are both ready, avoiding forced reconnects.

Settings expose the toggle, status, and optional battery-exemption prompt; the active thread is persisted as the retained target and cleared when environments/threads are removed or caches are wiped.

Reviewed by Cursor Bugbot for commit b36b040. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Add Android background connection with foreground service and shared outbox dispatcher

  • Adds the t3-background-connection Expo native module with a sticky foreground service, headless JS task, broadcast receiver (boot/package-replace/restart), state manager, exponential restart backoff (1s–5min), and battery-optimization exemption support
  • Adds JS background infrastructure: background task lifecycle, retained-thread persistence (single-flight load, serialized saves/clears), target selection, background relay-auth bootstrap with epoch invalidation and transition holds, and lease-based UI/background managed-relay session ownership
  • Refactors the thread outbox drain from React hooks to a registry-backed shared dispatcher (acquireThreadOutboxDrain) that reference-counts owners, coalesces drain requests, and retries through state-backed timers
  • Adds application-active-preserved wakeup type so EnvironmentSupervisor probes the connected session instead of reconnecting when both the native service and runtime are ready
  • Integrates into the app entrypoint, App.tsx startup coordinator, RootStackLayout retained-thread tracking, and Android-only settings UI with battery-exemption prompting
  • Risk: CloudAuthProvider account deactivation now invalidates background relay auth and clears both UI and background ownership; account switches suppress intermediate refresh. The outbox drain lifecycle changed from per-component to registry-scoped shared leases — multiple hook mounts share one dispatcher, and the final release stops drains, cancels retries, and unsubscribes listeners

Macroscope summarized b36b040.

@coderabbitai

coderabbitaiBot commented Aug 1, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 6f807a1b-4f6d-42c6-a833-ef045a6e163d

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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.

@github-actionsgithub-actionsBot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:XXL 1,000+ changed lines (additions + deletions). labels Aug 1, 2026
Comment threadapps/mobile/src/features/cloud/backgroundManagedRelayAuth.ts
@macroscopeapp

macroscopeappBot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This PR introduces a substantial Android background service and headless runtime that changes connection, authentication, outbox, persistence, and resume behavior, with intentional battery, data, and notification side effects. It also adds production lint suppressions, so the cross-cutting implementation requires human review.

You can add or adjust custom eligibility rules. Learn more.

@snipemanmike
snipemanmikeforce-pushed the feat/android-background-connection branch from 81c65f7 to e25b8e5CompareAugust 6, 2026 03:27
@juliusmarminge
juliusmarmingeforce-pushed the feat/android-background-connection branch from e25b8e5 to f258f81CompareAugust 10, 2026 10:58

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Reviewed the Effect-service surface of this PR (new MobileStorage operations, the imperative persistence bridge, and the touched client-runtime connection modules). The service definition order, inline Context.Service interface, make/layer exports, Foo["Service"] usage, and namespace subpath imports all follow the conventions; the Promise-based background modules stay on the imperative-adapter side and are not consumed by Effect services. One log-annotation nit noted inline.

Posted via Macroscope — Effect Service Conventions

Comment threadapps/mobile/src/persistence/mobile-storage.ts Outdated
camerontaylor added a commit to camerontaylor/t3code that referenced this pull request Aug 18, 2026
Keeps the mobile app connected to its environment while backgrounded on
Android via a foreground service and headless task (upstream PR pingdotgg#5179).
Carried until it lands upstream.
# Conflicts:
#	apps/mobile/src/App.tsx
#	apps/mobile/src/features/settings/SettingsRouteScreen.tsx
#	apps/mobile/src/state/use-thread-outbox-drain.ts
snipemanmikeand others added 11 commits September 1, 2026 19:42
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
@snipemanmike
snipemanmikeforce-pushed the feat/android-background-connection branch from 7b3cd80 to c7d27a5CompareSeptember 2, 2026 02:54

@cursorcursorBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want fixes drafted automatically? Bugbot Autofix can create code changes for findings. A team admin can enable Autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit c7d27a5. Configure here.

Comment threadapps/mobile/src/features/cloud/backgroundManagedRelayAuth.ts
Account switches invalidated the background auth epoch and deferred
explicit refreshes until the previous account's cleanup settled, but a
scheduled retry or a cold headless start could still bootstrap in that
window, read the new Clerk account, and publish its relay session before
environment removal and the token-cache reset finished.
Every bootstrap attempt now waits behind the pending sign-out cleanup and
account activation, while still capturing its epoch synchronously so an
invalidation issued right after it starts continues to supersede it.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL1,000+ changed lines (additions + deletions).vouch:unvouchedPR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@snipemanmike@juliusmarminge
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

feat(mobile): keep Android connections alive in the background - #5179

Open
snipemanmike wants to merge 12 commits into
pingdotgg:mainfrom
snipemanmike:feat/android-background-connection
Open

feat(mobile): keep Android connections alive in the background#5179
snipemanmike wants to merge 12 commits into
pingdotgg:mainfrom
snipemanmike:feat/android-background-connection

Conversation

@snipemanmike

@snipemanmikesnipemanmike commented Aug 1, 2026

Copy link
Copy Markdown

What Changed

Android currently suspends the JavaScript connection runtime when T3 Code is backgrounded or the phone is locked. Reopening the app can therefore require a full reconnect and synchronization before current thread state appears.

This PR adds an Android-only, opt-in background connection mode that:

  • runs the existing client runtime from a foreground service backed by React Native Headless JS
  • shares the existing atom registry, connection supervisors, subscriptions, and outbox dispatcher instead of creating a second WebSocket stack
  • keeps every saved environment's shell state current while retaining full detail only for the last-opened and starting/running threads
  • preserves managed-relay authentication for cold T3 Connect startup
  • probes healthy sessions on resume without forcing reconnects or resubscriptions
  • restores the service after ordinary process reclamation, task swipe-away, package replacement, and reboot after unlock
  • exposes an Android setting and the platform-required silent foreground-service notification

This does not change the WebSocket protocol, synchronization reducer, server contracts, or server behavior.

The recovery work in #5154 improves what happens after Android has suspended the client. This PR addresses the preceding problem: while the setting is enabled, it prevents Android suspension from stopping the connection runtime in the first place. The approaches remain compatible.

Why

Mobile is commonly used to monitor or continue work running on another machine through a direct connection, Tailscale, or T3 Connect. Keeping the existing runtime alive means events can arrive while the app is backgrounded, outgoing messages continue draining, and reopening can render current state immediately rather than beginning a reconnect cycle.

The feature defaults off. Android force-stop remains an unavoidable boundary, and users must launch the app once afterward.

Verification

  • 18 focused test files passed (114 tests)
  • mobile and client-runtime typechecks passed
  • mobile lint passed
  • native module unit tests and Android release lint passed
  • arm64 production release APK built successfully, matched the expected signing certificate, and installed with replacement semantics without clearing app data
  • physical Android validation covered direct/Tailscale operation while backgrounded and locked, forced Doze, task swipe-away, Wi-Fi/cellular recovery, package replacement, and reboot restoration
  • T3 Connect ownership and cold-bootstrap paths are covered by focused tests; live T3 Connect validation was intentionally skipped
  • a clean emulator was used for UI evidence; it contains no account or personal data

UI Changes

Before:

Settings before

After:

Settings after

Interaction video (sanitized emulator)

Checklist

  • This PR is small and focused
  • I explained what changed and why
  • I included before/after screenshots for any UI changes
  • I included a video for animation/interaction changes

This is one focused Android runtime concern, but it is not a small diff; the native lifecycle, shared runtime ownership, relay authentication, resume semantics, tests, and documentation must land together to avoid partial behavior.

Implemented with GPT-5.6-sol through the Codex harness. Reviewed with Claude Fable through the Claude Code CLI.


Note

High Risk
Large cross-cutting change to Android FGS lifecycle, shared connection/outbox state, and managed-relay auth across UI unmount and account switches—regressions could affect credentials, reconnect behavior, or background message delivery.

Overview
Adds an opt-in Android “Keep connected in background” mode that keeps the existing mobile client runtime alive via a remoteMessaging foreground service and a single React Native Headless JS task—no second WebSocket or sync stack.

The new t3-background-connection Expo module owns native lifecycle: persistent enablement, ongoing notification, Wi‑Fi lock, boot/package-replace recovery, exponential restart backoff (only when battery optimization is ignored), and orderly stop when the user turns the feature off. JS registers the headless task at startup and coordinates background-root leases (environment catalog/shells, server configs, thread detail for the retained + active threads) plus acquireThreadOutboxDrain so queued messages still drain without a mounted UI.

T3 Connect / relay gains separate UI vs background managed-relay session ownership and backgroundManagedRelayAuth (cold Clerk bootstrap, holds during account transitions, retries). CloudAuthProvider no longer clears relay on UI unmount when background still owns the session. Returning to the app uses application-active-preserved when the native service and JS runtime are both ready, avoiding forced reconnects.

Settings expose the toggle, status, and optional battery-exemption prompt; the active thread is persisted as the retained target and cleared when environments/threads are removed or caches are wiped.

Reviewed by Cursor Bugbot for commit b36b040. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Add Android background connection with foreground service and shared outbox dispatcher

  • Adds the t3-background-connection Expo native module with a sticky foreground service, headless JS task, broadcast receiver (boot/package-replace/restart), state manager, exponential restart backoff (1s–5min), and battery-optimization exemption support
  • Adds JS background infrastructure: background task lifecycle, retained-thread persistence (single-flight load, serialized saves/clears), target selection, background relay-auth bootstrap with epoch invalidation and transition holds, and lease-based UI/background managed-relay session ownership
  • Refactors the thread outbox drain from React hooks to a registry-backed shared dispatcher (acquireThreadOutboxDrain) that reference-counts owners, coalesces drain requests, and retries through state-backed timers
  • Adds application-active-preserved wakeup type so EnvironmentSupervisor probes the connected session instead of reconnecting when both the native service and runtime are ready
  • Integrates into the app entrypoint, App.tsx startup coordinator, RootStackLayout retained-thread tracking, and Android-only settings UI with battery-exemption prompting
  • Risk: CloudAuthProvider account deactivation now invalidates background relay auth and clears both UI and background ownership; account switches suppress intermediate refresh. The outbox drain lifecycle changed from per-component to registry-scoped shared leases — multiple hook mounts share one dispatcher, and the final release stops drains, cancels retries, and unsubscribes listeners

Macroscope summarized b36b040.

@coderabbitai

coderabbitaiBot commented Aug 1, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 6f807a1b-4f6d-42c6-a833-ef045a6e163d

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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.

@github-actionsgithub-actionsBot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:XXL 1,000+ changed lines (additions + deletions). labels Aug 1, 2026
Comment threadapps/mobile/src/features/cloud/backgroundManagedRelayAuth.ts
@macroscopeapp

macroscopeappBot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This PR introduces a substantial Android background service and headless runtime that changes connection, authentication, outbox, persistence, and resume behavior, with intentional battery, data, and notification side effects. It also adds production lint suppressions, so the cross-cutting implementation requires human review.

You can add or adjust custom eligibility rules. Learn more.

@snipemanmike
snipemanmikeforce-pushed the feat/android-background-connection branch from 81c65f7 to e25b8e5CompareAugust 6, 2026 03:27
@juliusmarminge
juliusmarmingeforce-pushed the feat/android-background-connection branch from e25b8e5 to f258f81CompareAugust 10, 2026 10:58

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Reviewed the Effect-service surface of this PR (new MobileStorage operations, the imperative persistence bridge, and the touched client-runtime connection modules). The service definition order, inline Context.Service interface, make/layer exports, Foo["Service"] usage, and namespace subpath imports all follow the conventions; the Promise-based background modules stay on the imperative-adapter side and are not consumed by Effect services. One log-annotation nit noted inline.

Posted via Macroscope — Effect Service Conventions

Comment threadapps/mobile/src/persistence/mobile-storage.ts Outdated
camerontaylor added a commit to camerontaylor/t3code that referenced this pull request Aug 18, 2026
Keeps the mobile app connected to its environment while backgrounded on
Android via a foreground service and headless task (upstream PR pingdotgg#5179).
Carried until it lands upstream.
# Conflicts:
#	apps/mobile/src/App.tsx
#	apps/mobile/src/features/settings/SettingsRouteScreen.tsx
#	apps/mobile/src/state/use-thread-outbox-drain.ts
snipemanmikeand others added 11 commits September 1, 2026 19:42
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
@snipemanmike
snipemanmikeforce-pushed the feat/android-background-connection branch from 7b3cd80 to c7d27a5CompareSeptember 2, 2026 02:54

@cursorcursorBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want fixes drafted automatically? Bugbot Autofix can create code changes for findings. A team admin can enable Autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit c7d27a5. Configure here.

Comment threadapps/mobile/src/features/cloud/backgroundManagedRelayAuth.ts
Account switches invalidated the background auth epoch and deferred
explicit refreshes until the previous account's cleanup settled, but a
scheduled retry or a cold headless start could still bootstrap in that
window, read the new Clerk account, and publish its relay session before
environment removal and the token-cache reset finished.
Every bootstrap attempt now waits behind the pending sign-out cleanup and
account activation, while still capturing its epoch synchronously so an
invalidation issued right after it starts continues to supersede it.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL1,000+ changed lines (additions + deletions).vouch:unvouchedPR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@snipemanmike@juliusmarminge
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(mobile): keep Android connections alive in the background - #5179

Open
snipemanmike wants to merge 12 commits into
pingdotgg:mainfrom
snipemanmike:feat/android-background-connection
Open

feat(mobile): keep Android connections alive in the background#5179
snipemanmike wants to merge 12 commits into
pingdotgg:mainfrom
snipemanmike:feat/android-background-connection

Conversation

@snipemanmike

@snipemanmikesnipemanmike commented Aug 1, 2026

Copy link
Copy Markdown

What Changed

Android currently suspends the JavaScript connection runtime when T3 Code is backgrounded or the phone is locked. Reopening the app can therefore require a full reconnect and synchronization before current thread state appears.

This PR adds an Android-only, opt-in background connection mode that:

  • runs the existing client runtime from a foreground service backed by React Native Headless JS
  • shares the existing atom registry, connection supervisors, subscriptions, and outbox dispatcher instead of creating a second WebSocket stack
  • keeps every saved environment's shell state current while retaining full detail only for the last-opened and starting/running threads
  • preserves managed-relay authentication for cold T3 Connect startup
  • probes healthy sessions on resume without forcing reconnects or resubscriptions
  • restores the service after ordinary process reclamation, task swipe-away, package replacement, and reboot after unlock
  • exposes an Android setting and the platform-required silent foreground-service notification

This does not change the WebSocket protocol, synchronization reducer, server contracts, or server behavior.

The recovery work in #5154 improves what happens after Android has suspended the client. This PR addresses the preceding problem: while the setting is enabled, it prevents Android suspension from stopping the connection runtime in the first place. The approaches remain compatible.

Why

Mobile is commonly used to monitor or continue work running on another machine through a direct connection, Tailscale, or T3 Connect. Keeping the existing runtime alive means events can arrive while the app is backgrounded, outgoing messages continue draining, and reopening can render current state immediately rather than beginning a reconnect cycle.

The feature defaults off. Android force-stop remains an unavoidable boundary, and users must launch the app once afterward.

Verification

  • 18 focused test files passed (114 tests)
  • mobile and client-runtime typechecks passed
  • mobile lint passed
  • native module unit tests and Android release lint passed
  • arm64 production release APK built successfully, matched the expected signing certificate, and installed with replacement semantics without clearing app data
  • physical Android validation covered direct/Tailscale operation while backgrounded and locked, forced Doze, task swipe-away, Wi-Fi/cellular recovery, package replacement, and reboot restoration
  • T3 Connect ownership and cold-bootstrap paths are covered by focused tests; live T3 Connect validation was intentionally skipped
  • a clean emulator was used for UI evidence; it contains no account or personal data

UI Changes

Before:

Settings before

After:

Settings after

Interaction video (sanitized emulator)

Checklist

  • This PR is small and focused
  • I explained what changed and why
  • I included before/after screenshots for any UI changes
  • I included a video for animation/interaction changes

This is one focused Android runtime concern, but it is not a small diff; the native lifecycle, shared runtime ownership, relay authentication, resume semantics, tests, and documentation must land together to avoid partial behavior.

Implemented with GPT-5.6-sol through the Codex harness. Reviewed with Claude Fable through the Claude Code CLI.


Note

High Risk
Large cross-cutting change to Android FGS lifecycle, shared connection/outbox state, and managed-relay auth across UI unmount and account switches—regressions could affect credentials, reconnect behavior, or background message delivery.

Overview
Adds an opt-in Android “Keep connected in background” mode that keeps the existing mobile client runtime alive via a remoteMessaging foreground service and a single React Native Headless JS task—no second WebSocket or sync stack.

The new t3-background-connection Expo module owns native lifecycle: persistent enablement, ongoing notification, Wi‑Fi lock, boot/package-replace recovery, exponential restart backoff (only when battery optimization is ignored), and orderly stop when the user turns the feature off. JS registers the headless task at startup and coordinates background-root leases (environment catalog/shells, server configs, thread detail for the retained + active threads) plus acquireThreadOutboxDrain so queued messages still drain without a mounted UI.

T3 Connect / relay gains separate UI vs background managed-relay session ownership and backgroundManagedRelayAuth (cold Clerk bootstrap, holds during account transitions, retries). CloudAuthProvider no longer clears relay on UI unmount when background still owns the session. Returning to the app uses application-active-preserved when the native service and JS runtime are both ready, avoiding forced reconnects.

Settings expose the toggle, status, and optional battery-exemption prompt; the active thread is persisted as the retained target and cleared when environments/threads are removed or caches are wiped.

Reviewed by Cursor Bugbot for commit b36b040. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Add Android background connection with foreground service and shared outbox dispatcher

  • Adds the t3-background-connection Expo native module with a sticky foreground service, headless JS task, broadcast receiver (boot/package-replace/restart), state manager, exponential restart backoff (1s–5min), and battery-optimization exemption support
  • Adds JS background infrastructure: background task lifecycle, retained-thread persistence (single-flight load, serialized saves/clears), target selection, background relay-auth bootstrap with epoch invalidation and transition holds, and lease-based UI/background managed-relay session ownership
  • Refactors the thread outbox drain from React hooks to a registry-backed shared dispatcher (acquireThreadOutboxDrain) that reference-counts owners, coalesces drain requests, and retries through state-backed timers
  • Adds application-active-preserved wakeup type so EnvironmentSupervisor probes the connected session instead of reconnecting when both the native service and runtime are ready
  • Integrates into the app entrypoint, App.tsx startup coordinator, RootStackLayout retained-thread tracking, and Android-only settings UI with battery-exemption prompting
  • Risk: CloudAuthProvider account deactivation now invalidates background relay auth and clears both UI and background ownership; account switches suppress intermediate refresh. The outbox drain lifecycle changed from per-component to registry-scoped shared leases — multiple hook mounts share one dispatcher, and the final release stops drains, cancels retries, and unsubscribes listeners

Macroscope summarized b36b040.

@coderabbitai

coderabbitaiBot commented Aug 1, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 6f807a1b-4f6d-42c6-a833-ef045a6e163d

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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.

@github-actionsgithub-actionsBot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:XXL 1,000+ changed lines (additions + deletions). labels Aug 1, 2026
Comment threadapps/mobile/src/features/cloud/backgroundManagedRelayAuth.ts
@macroscopeapp

macroscopeappBot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This PR introduces a substantial Android background service and headless runtime that changes connection, authentication, outbox, persistence, and resume behavior, with intentional battery, data, and notification side effects. It also adds production lint suppressions, so the cross-cutting implementation requires human review.

You can add or adjust custom eligibility rules. Learn more.

@snipemanmike
snipemanmikeforce-pushed the feat/android-background-connection branch from 81c65f7 to e25b8e5CompareAugust 6, 2026 03:27
@juliusmarminge
juliusmarmingeforce-pushed the feat/android-background-connection branch from e25b8e5 to f258f81CompareAugust 10, 2026 10:58

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Reviewed the Effect-service surface of this PR (new MobileStorage operations, the imperative persistence bridge, and the touched client-runtime connection modules). The service definition order, inline Context.Service interface, make/layer exports, Foo["Service"] usage, and namespace subpath imports all follow the conventions; the Promise-based background modules stay on the imperative-adapter side and are not consumed by Effect services. One log-annotation nit noted inline.

Posted via Macroscope — Effect Service Conventions

Comment threadapps/mobile/src/persistence/mobile-storage.ts Outdated
camerontaylor added a commit to camerontaylor/t3code that referenced this pull request Aug 18, 2026
Keeps the mobile app connected to its environment while backgrounded on
Android via a foreground service and headless task (upstream PR pingdotgg#5179).
Carried until it lands upstream.
# Conflicts:
#	apps/mobile/src/App.tsx
#	apps/mobile/src/features/settings/SettingsRouteScreen.tsx
#	apps/mobile/src/state/use-thread-outbox-drain.ts
snipemanmikeand others added 11 commits September 1, 2026 19:42
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
@snipemanmike
snipemanmikeforce-pushed the feat/android-background-connection branch from 7b3cd80 to c7d27a5CompareSeptember 2, 2026 02:54

@cursorcursorBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want fixes drafted automatically? Bugbot Autofix can create code changes for findings. A team admin can enable Autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit c7d27a5. Configure here.

Comment threadapps/mobile/src/features/cloud/backgroundManagedRelayAuth.ts
Account switches invalidated the background auth epoch and deferred
explicit refreshes until the previous account's cleanup settled, but a
scheduled retry or a cold headless start could still bootstrap in that
window, read the new Clerk account, and publish its relay session before
environment removal and the token-cache reset finished.
Every bootstrap attempt now waits behind the pending sign-out cleanup and
account activation, while still capturing its epoch synchronously so an
invalidation issued right after it starts continues to supersede it.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL1,000+ changed lines (additions + deletions).vouch:unvouchedPR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@snipemanmike@juliusmarminge
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(mobile): keep Android connections alive in the background - #5179

Open
snipemanmike wants to merge 12 commits into
pingdotgg:mainfrom
snipemanmike:feat/android-background-connection
Open

feat(mobile): keep Android connections alive in the background#5179
snipemanmike wants to merge 12 commits into
pingdotgg:mainfrom
snipemanmike:feat/android-background-connection

Conversation

@snipemanmike

@snipemanmikesnipemanmike commented Aug 1, 2026

Copy link
Copy Markdown

What Changed

Android currently suspends the JavaScript connection runtime when T3 Code is backgrounded or the phone is locked. Reopening the app can therefore require a full reconnect and synchronization before current thread state appears.

This PR adds an Android-only, opt-in background connection mode that:

  • runs the existing client runtime from a foreground service backed by React Native Headless JS
  • shares the existing atom registry, connection supervisors, subscriptions, and outbox dispatcher instead of creating a second WebSocket stack
  • keeps every saved environment's shell state current while retaining full detail only for the last-opened and starting/running threads
  • preserves managed-relay authentication for cold T3 Connect startup
  • probes healthy sessions on resume without forcing reconnects or resubscriptions
  • restores the service after ordinary process reclamation, task swipe-away, package replacement, and reboot after unlock
  • exposes an Android setting and the platform-required silent foreground-service notification

This does not change the WebSocket protocol, synchronization reducer, server contracts, or server behavior.

The recovery work in #5154 improves what happens after Android has suspended the client. This PR addresses the preceding problem: while the setting is enabled, it prevents Android suspension from stopping the connection runtime in the first place. The approaches remain compatible.

Why

Mobile is commonly used to monitor or continue work running on another machine through a direct connection, Tailscale, or T3 Connect. Keeping the existing runtime alive means events can arrive while the app is backgrounded, outgoing messages continue draining, and reopening can render current state immediately rather than beginning a reconnect cycle.

The feature defaults off. Android force-stop remains an unavoidable boundary, and users must launch the app once afterward.

Verification

  • 18 focused test files passed (114 tests)
  • mobile and client-runtime typechecks passed
  • mobile lint passed
  • native module unit tests and Android release lint passed
  • arm64 production release APK built successfully, matched the expected signing certificate, and installed with replacement semantics without clearing app data
  • physical Android validation covered direct/Tailscale operation while backgrounded and locked, forced Doze, task swipe-away, Wi-Fi/cellular recovery, package replacement, and reboot restoration
  • T3 Connect ownership and cold-bootstrap paths are covered by focused tests; live T3 Connect validation was intentionally skipped
  • a clean emulator was used for UI evidence; it contains no account or personal data

UI Changes

Before:

Settings before

After:

Settings after

Interaction video (sanitized emulator)

Checklist

  • This PR is small and focused
  • I explained what changed and why
  • I included before/after screenshots for any UI changes
  • I included a video for animation/interaction changes

This is one focused Android runtime concern, but it is not a small diff; the native lifecycle, shared runtime ownership, relay authentication, resume semantics, tests, and documentation must land together to avoid partial behavior.

Implemented with GPT-5.6-sol through the Codex harness. Reviewed with Claude Fable through the Claude Code CLI.


Note

High Risk
Large cross-cutting change to Android FGS lifecycle, shared connection/outbox state, and managed-relay auth across UI unmount and account switches—regressions could affect credentials, reconnect behavior, or background message delivery.

Overview
Adds an opt-in Android “Keep connected in background” mode that keeps the existing mobile client runtime alive via a remoteMessaging foreground service and a single React Native Headless JS task—no second WebSocket or sync stack.

The new t3-background-connection Expo module owns native lifecycle: persistent enablement, ongoing notification, Wi‑Fi lock, boot/package-replace recovery, exponential restart backoff (only when battery optimization is ignored), and orderly stop when the user turns the feature off. JS registers the headless task at startup and coordinates background-root leases (environment catalog/shells, server configs, thread detail for the retained + active threads) plus acquireThreadOutboxDrain so queued messages still drain without a mounted UI.

T3 Connect / relay gains separate UI vs background managed-relay session ownership and backgroundManagedRelayAuth (cold Clerk bootstrap, holds during account transitions, retries). CloudAuthProvider no longer clears relay on UI unmount when background still owns the session. Returning to the app uses application-active-preserved when the native service and JS runtime are both ready, avoiding forced reconnects.

Settings expose the toggle, status, and optional battery-exemption prompt; the active thread is persisted as the retained target and cleared when environments/threads are removed or caches are wiped.

Reviewed by Cursor Bugbot for commit b36b040. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Add Android background connection with foreground service and shared outbox dispatcher

  • Adds the t3-background-connection Expo native module with a sticky foreground service, headless JS task, broadcast receiver (boot/package-replace/restart), state manager, exponential restart backoff (1s–5min), and battery-optimization exemption support
  • Adds JS background infrastructure: background task lifecycle, retained-thread persistence (single-flight load, serialized saves/clears), target selection, background relay-auth bootstrap with epoch invalidation and transition holds, and lease-based UI/background managed-relay session ownership
  • Refactors the thread outbox drain from React hooks to a registry-backed shared dispatcher (acquireThreadOutboxDrain) that reference-counts owners, coalesces drain requests, and retries through state-backed timers
  • Adds application-active-preserved wakeup type so EnvironmentSupervisor probes the connected session instead of reconnecting when both the native service and runtime are ready
  • Integrates into the app entrypoint, App.tsx startup coordinator, RootStackLayout retained-thread tracking, and Android-only settings UI with battery-exemption prompting
  • Risk: CloudAuthProvider account deactivation now invalidates background relay auth and clears both UI and background ownership; account switches suppress intermediate refresh. The outbox drain lifecycle changed from per-component to registry-scoped shared leases — multiple hook mounts share one dispatcher, and the final release stops drains, cancels retries, and unsubscribes listeners

Macroscope summarized b36b040.

@coderabbitai

coderabbitaiBot commented Aug 1, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 6f807a1b-4f6d-42c6-a833-ef045a6e163d

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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.

@github-actionsgithub-actionsBot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:XXL 1,000+ changed lines (additions + deletions). labels Aug 1, 2026
Comment threadapps/mobile/src/features/cloud/backgroundManagedRelayAuth.ts
@macroscopeapp

macroscopeappBot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This PR introduces a substantial Android background service and headless runtime that changes connection, authentication, outbox, persistence, and resume behavior, with intentional battery, data, and notification side effects. It also adds production lint suppressions, so the cross-cutting implementation requires human review.

You can add or adjust custom eligibility rules. Learn more.

@snipemanmike
snipemanmikeforce-pushed the feat/android-background-connection branch from 81c65f7 to e25b8e5CompareAugust 6, 2026 03:27
@juliusmarminge
juliusmarmingeforce-pushed the feat/android-background-connection branch from e25b8e5 to f258f81CompareAugust 10, 2026 10:58

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Reviewed the Effect-service surface of this PR (new MobileStorage operations, the imperative persistence bridge, and the touched client-runtime connection modules). The service definition order, inline Context.Service interface, make/layer exports, Foo["Service"] usage, and namespace subpath imports all follow the conventions; the Promise-based background modules stay on the imperative-adapter side and are not consumed by Effect services. One log-annotation nit noted inline.

Posted via Macroscope — Effect Service Conventions

Comment threadapps/mobile/src/persistence/mobile-storage.ts Outdated
camerontaylor added a commit to camerontaylor/t3code that referenced this pull request Aug 18, 2026
Keeps the mobile app connected to its environment while backgrounded on
Android via a foreground service and headless task (upstream PR pingdotgg#5179).
Carried until it lands upstream.
# Conflicts:
#	apps/mobile/src/App.tsx
#	apps/mobile/src/features/settings/SettingsRouteScreen.tsx
#	apps/mobile/src/state/use-thread-outbox-drain.ts
snipemanmikeand others added 11 commits September 1, 2026 19:42
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
@snipemanmike
snipemanmikeforce-pushed the feat/android-background-connection branch from 7b3cd80 to c7d27a5CompareSeptember 2, 2026 02:54

@cursorcursorBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want fixes drafted automatically? Bugbot Autofix can create code changes for findings. A team admin can enable Autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit c7d27a5. Configure here.

Comment threadapps/mobile/src/features/cloud/backgroundManagedRelayAuth.ts
Account switches invalidated the background auth epoch and deferred
explicit refreshes until the previous account's cleanup settled, but a
scheduled retry or a cold headless start could still bootstrap in that
window, read the new Clerk account, and publish its relay session before
environment removal and the token-cache reset finished.
Every bootstrap attempt now waits behind the pending sign-out cleanup and
account activation, while still capturing its epoch synchronously so an
invalidation issued right after it starts continues to supersede it.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL1,000+ changed lines (additions + deletions).vouch:unvouchedPR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@snipemanmike@juliusmarminge
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

feat(mobile): keep Android connections alive in the background - #5179

Open
snipemanmike wants to merge 12 commits into
pingdotgg:mainfrom
snipemanmike:feat/android-background-connection
Open

feat(mobile): keep Android connections alive in the background#5179
snipemanmike wants to merge 12 commits into
pingdotgg:mainfrom
snipemanmike:feat/android-background-connection

Conversation

@snipemanmike

@snipemanmikesnipemanmike commented Aug 1, 2026

Copy link
Copy Markdown

What Changed

Android currently suspends the JavaScript connection runtime when T3 Code is backgrounded or the phone is locked. Reopening the app can therefore require a full reconnect and synchronization before current thread state appears.

This PR adds an Android-only, opt-in background connection mode that:

  • runs the existing client runtime from a foreground service backed by React Native Headless JS
  • shares the existing atom registry, connection supervisors, subscriptions, and outbox dispatcher instead of creating a second WebSocket stack
  • keeps every saved environment's shell state current while retaining full detail only for the last-opened and starting/running threads
  • preserves managed-relay authentication for cold T3 Connect startup
  • probes healthy sessions on resume without forcing reconnects or resubscriptions
  • restores the service after ordinary process reclamation, task swipe-away, package replacement, and reboot after unlock
  • exposes an Android setting and the platform-required silent foreground-service notification

This does not change the WebSocket protocol, synchronization reducer, server contracts, or server behavior.

The recovery work in #5154 improves what happens after Android has suspended the client. This PR addresses the preceding problem: while the setting is enabled, it prevents Android suspension from stopping the connection runtime in the first place. The approaches remain compatible.

Why

Mobile is commonly used to monitor or continue work running on another machine through a direct connection, Tailscale, or T3 Connect. Keeping the existing runtime alive means events can arrive while the app is backgrounded, outgoing messages continue draining, and reopening can render current state immediately rather than beginning a reconnect cycle.

The feature defaults off. Android force-stop remains an unavoidable boundary, and users must launch the app once afterward.

Verification

  • 18 focused test files passed (114 tests)
  • mobile and client-runtime typechecks passed
  • mobile lint passed
  • native module unit tests and Android release lint passed
  • arm64 production release APK built successfully, matched the expected signing certificate, and installed with replacement semantics without clearing app data
  • physical Android validation covered direct/Tailscale operation while backgrounded and locked, forced Doze, task swipe-away, Wi-Fi/cellular recovery, package replacement, and reboot restoration
  • T3 Connect ownership and cold-bootstrap paths are covered by focused tests; live T3 Connect validation was intentionally skipped
  • a clean emulator was used for UI evidence; it contains no account or personal data

UI Changes

Before:

Settings before

After:

Settings after

Interaction video (sanitized emulator)

Checklist

  • This PR is small and focused
  • I explained what changed and why
  • I included before/after screenshots for any UI changes
  • I included a video for animation/interaction changes

This is one focused Android runtime concern, but it is not a small diff; the native lifecycle, shared runtime ownership, relay authentication, resume semantics, tests, and documentation must land together to avoid partial behavior.

Implemented with GPT-5.6-sol through the Codex harness. Reviewed with Claude Fable through the Claude Code CLI.


Note

High Risk
Large cross-cutting change to Android FGS lifecycle, shared connection/outbox state, and managed-relay auth across UI unmount and account switches—regressions could affect credentials, reconnect behavior, or background message delivery.

Overview
Adds an opt-in Android “Keep connected in background” mode that keeps the existing mobile client runtime alive via a remoteMessaging foreground service and a single React Native Headless JS task—no second WebSocket or sync stack.

The new t3-background-connection Expo module owns native lifecycle: persistent enablement, ongoing notification, Wi‑Fi lock, boot/package-replace recovery, exponential restart backoff (only when battery optimization is ignored), and orderly stop when the user turns the feature off. JS registers the headless task at startup and coordinates background-root leases (environment catalog/shells, server configs, thread detail for the retained + active threads) plus acquireThreadOutboxDrain so queued messages still drain without a mounted UI.

T3 Connect / relay gains separate UI vs background managed-relay session ownership and backgroundManagedRelayAuth (cold Clerk bootstrap, holds during account transitions, retries). CloudAuthProvider no longer clears relay on UI unmount when background still owns the session. Returning to the app uses application-active-preserved when the native service and JS runtime are both ready, avoiding forced reconnects.

Settings expose the toggle, status, and optional battery-exemption prompt; the active thread is persisted as the retained target and cleared when environments/threads are removed or caches are wiped.

Reviewed by Cursor Bugbot for commit b36b040. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Add Android background connection with foreground service and shared outbox dispatcher

  • Adds the t3-background-connection Expo native module with a sticky foreground service, headless JS task, broadcast receiver (boot/package-replace/restart), state manager, exponential restart backoff (1s–5min), and battery-optimization exemption support
  • Adds JS background infrastructure: background task lifecycle, retained-thread persistence (single-flight load, serialized saves/clears), target selection, background relay-auth bootstrap with epoch invalidation and transition holds, and lease-based UI/background managed-relay session ownership
  • Refactors the thread outbox drain from React hooks to a registry-backed shared dispatcher (acquireThreadOutboxDrain) that reference-counts owners, coalesces drain requests, and retries through state-backed timers
  • Adds application-active-preserved wakeup type so EnvironmentSupervisor probes the connected session instead of reconnecting when both the native service and runtime are ready
  • Integrates into the app entrypoint, App.tsx startup coordinator, RootStackLayout retained-thread tracking, and Android-only settings UI with battery-exemption prompting
  • Risk: CloudAuthProvider account deactivation now invalidates background relay auth and clears both UI and background ownership; account switches suppress intermediate refresh. The outbox drain lifecycle changed from per-component to registry-scoped shared leases — multiple hook mounts share one dispatcher, and the final release stops drains, cancels retries, and unsubscribes listeners

Macroscope summarized b36b040.

@coderabbitai

coderabbitaiBot commented Aug 1, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 6f807a1b-4f6d-42c6-a833-ef045a6e163d

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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.

@github-actionsgithub-actionsBot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:XXL 1,000+ changed lines (additions + deletions). labels Aug 1, 2026
Comment threadapps/mobile/src/features/cloud/backgroundManagedRelayAuth.ts
@macroscopeapp

macroscopeappBot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This PR introduces a substantial Android background service and headless runtime that changes connection, authentication, outbox, persistence, and resume behavior, with intentional battery, data, and notification side effects. It also adds production lint suppressions, so the cross-cutting implementation requires human review.

You can add or adjust custom eligibility rules. Learn more.

@snipemanmike
snipemanmikeforce-pushed the feat/android-background-connection branch from 81c65f7 to e25b8e5CompareAugust 6, 2026 03:27
@juliusmarminge
juliusmarmingeforce-pushed the feat/android-background-connection branch from e25b8e5 to f258f81CompareAugust 10, 2026 10:58

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Reviewed the Effect-service surface of this PR (new MobileStorage operations, the imperative persistence bridge, and the touched client-runtime connection modules). The service definition order, inline Context.Service interface, make/layer exports, Foo["Service"] usage, and namespace subpath imports all follow the conventions; the Promise-based background modules stay on the imperative-adapter side and are not consumed by Effect services. One log-annotation nit noted inline.

Posted via Macroscope — Effect Service Conventions

Comment threadapps/mobile/src/persistence/mobile-storage.ts Outdated
camerontaylor added a commit to camerontaylor/t3code that referenced this pull request Aug 18, 2026
Keeps the mobile app connected to its environment while backgrounded on
Android via a foreground service and headless task (upstream PR pingdotgg#5179).
Carried until it lands upstream.
# Conflicts:
#	apps/mobile/src/App.tsx
#	apps/mobile/src/features/settings/SettingsRouteScreen.tsx
#	apps/mobile/src/state/use-thread-outbox-drain.ts
snipemanmikeand others added 11 commits September 1, 2026 19:42
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
@snipemanmike
snipemanmikeforce-pushed the feat/android-background-connection branch from 7b3cd80 to c7d27a5CompareSeptember 2, 2026 02:54

@cursorcursorBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want fixes drafted automatically? Bugbot Autofix can create code changes for findings. A team admin can enable Autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit c7d27a5. Configure here.

Comment threadapps/mobile/src/features/cloud/backgroundManagedRelayAuth.ts
Account switches invalidated the background auth epoch and deferred
explicit refreshes until the previous account's cleanup settled, but a
scheduled retry or a cold headless start could still bootstrap in that
window, read the new Clerk account, and publish its relay session before
environment removal and the token-cache reset finished.
Every bootstrap attempt now waits behind the pending sign-out cleanup and
account activation, while still capturing its epoch synchronously so an
invalidation issued right after it starts continues to supersede it.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL1,000+ changed lines (additions + deletions).vouch:unvouchedPR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@snipemanmike@juliusmarminge