feat(desktop): attach to a running local backend#8115

closed6
main
GitHub fork: fix/desktop-attach-running-backend
openedAug 24, 2026byPPedro-Revez-Silva
View on GitHubTwo-way sync enabledPR last imported 21d ago

What Changed

Packaged Desktop now discovers a live same-machine background service or t3 serve runtime and attaches to it instead of starting a second backend. The attachment uses a process-scoped grant to establish an administrative Desktop session while continuing to serve the renderer bundled with the installed app.

Desktop development remains isolated. Settings β†’ Connections exposes an opt-out, reports the attached state, and leaves network-exposure controls with the server that owns them.

Attached backends are rediscovered after a restart or port change. Transient failures are retried silently; persistent secure-pairing failures offer Try Again, Start Separate Backend, Open in Browser, or Quit. Starting separately uses isolated state for that launch.

The background-service user documentation now covers Desktop attachment, ownership, recovery, and the escape hatch.

Why

A headless T3 Code server can be the durable environment used from web and mobile, but opening Desktop on the same machine currently creates a separate environment with different threads and settings. Desktop should be able to act as another client for the existing local environment without weakening development isolation or silently falling back when secure attachment fails.

Starting-point proposal: https://github.com/pingdotgg/t3code/discussions/8114

UI Changes

Before

Desktop manages its own backend, so Connections begins with its network controls and has no running-server attachment state.

After

Desktop is attached to the disposable local server. The page shows the attachment control and makes network exposure read-only because the external server owns it.

Service restart recovery

The attached server stops, Desktop retries and reloads its bundled renderer, and the same thread returns automatically without a decision dialog.

https://github.com/user-attachments/assets/9ffe624e-93ca-48bf-b432-3c5f8dc024fb

Verification

  • Focused Desktop tests: 618 passed
  • Focused server tests: 55 passed
  • Focused web tests: 16 passed
  • Typechecks passed for Desktop, server, web, contracts, and shared
  • Targeted lint, formatting, and git diff --check passed
  • Production Desktop build passed
  • Packaged Desktop manually verified against a disposable background server: secure attachment, retained thread state, restart rediscovery, silent retry, automatic recovery, and persistent-failure UX

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

Implemented and reviewed with GPT-5.6 through the Codex harness in T3 Code.


Note

High Risk Changes desktop startup, OAuth pairing, and server auth grants for local attachment; mis-handling could spawn duplicate backends or fail secure reconnection in production.

Overview Packaged Desktop can attach to an already-running local T3 Code server (background service or t3 serve) instead of always spawning its own backend, so the same environment can be used from Desktop without a duplicate process.

Discovery reads server-runtime.json (and systemd/launchd T3CODE_HOME hints), validates the server via /.well-known/t3/environment, and pairs with a new per-process desktopAttachToken exchanged for a bearer session. The server seeds that token in PairingGrantStore and persists it in runtime state; Desktop uses manageProcess: false with HTTP health watching, invalidates stale attachments on loss, and can reload the bundled renderer (local protocol handler) while API traffic goes to the attached origin. Development never auto-attaches; a new attachExistingLocalBackend setting (default on) plus IPC exposes attachment state and relaunches on toggle.

Bootstrap and runtime recovery add dialogs: retry pairing, start a separate backend (isolated desktop-independent home + port scan via extracted DesktopBackendPort), open in browser, or quit. The backend pool retries pairing silently before offering β€œstart separate” on persistent failures.

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

Note

Add Desktop app support for attaching to a running local backend

  • Desktop boot discovers an existing local backend (systemd/launchd) and pairs with it using a desktopAttachToken, avoiding a second server spawn
  • New IPC channels getExistingLocalBackendState and setAttachExistingLocalBackend let the renderer query and toggle attachment; toggling relaunches the app
  • DesktopBackendManager gains an attached-runner mode (manageProcess: false) with health probes every 2s and callbacks for configuration failure and unexpected shutdown
  • Connections settings page renders an 'Attach to running server' switch; effective auth scopes stay administrative until an attached session resolves
  • ElectronProtocol serves the bundled renderer locally when rendererRoot is set, with MIME types, CSP, and path-traversal protection, instead of proxying from the attached server
  • PairingGrantStore seeds a reusable admin grant (100-year TTL) from the configured desktopAttachToken; server runtime state is persisted with file mode 0600
  • Auth layer (DesktopLocalEnvironmentAuth, desktopAuth) caches bearer tokens keyed by URL + token + authSessionKey and reuses the attached token without a bootstrap exchange
  • Recovery dialogs on pairing failure offer retry, start separate backend, open in browser, or quit; after 4 auto-retry attempts on ExistingLocalBackendPairingError the pool prompts the user
  • Risk: new DesktopBackendConfiguration interface members (resolveExistingLocalBackend, invalidateExistingLocalBackendAttachment, useIndependentBackendForLaunch) require all implementations and test mocks to provide them; DesktopSettings defaults attachExistingLocalBackend to true, so existing users without the key will begin attaching on next launch

Macroscope summarized ed06cc2.

Added 2 commits

  • fix(desktop): attach to a running local backenda851c5c
  • fix(desktop): recover attached backends safelyff0be38
Synced from GitHub

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: 9349159a-765e-4c5d-9284-813da4a94da7

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.

MmacroscopeappBotreviewed Β· 3 inline threadsAug 24, 2026
Synced from GitHub

Reviewed the new attach-to-running-backend Effect code against the service conventions. Three violations in the changed scope: a wrapped failure that loses its cause and rebuilds its message from cause.message, and two Effect.catchTag uses that should be Effect.catchTags. Dependency passing (fileSystem/path/httpClient in input objects) matches the established pattern in this package, so it is not flagged.

Posted via Macroscope β€” Effect Service Conventions

apps/desktop/src/backend/DesktopBackendManager.tslines 495–497Outdated Β· ff0be38Open thread
……@@ -432,9 +455,62 @@ const decodeDesktopTelemetryControlLine = Schema.decodeUnknownEffect(
494+ Effect.as(true),
495+ Effect.catchTag("BackendReadinessTimeoutError", (error) =>
496+ (options.onReadinessFailure?.(error) ?? Effect.void).pipe(Effect.as(false)),
497+ ),
MmacroscopeappBoton lines 495–497Aug 24, 2026

backend/DesktopBackendManager.ts:497

Same tag is handled with Effect.catchTags a few lines above (line 473); this occurrence should use Effect.catchTags too rather than Effect.catchTag.

suggestion
1 Effect.catchTags({
2 BackendReadinessTimeoutError: (error) =>
3 (options.onReadinessFailure?.(error) ?? Effect.void).pipe(Effect.as(false)),
4 }),

No longer relevant as of 7bb191346942687efdb672729d62478bb64fe6d0

Sorry, I'm unable to act on this request because you do not have permissions within this repository.

Sorry, I'm unable to act on this request because you do not have permissions within this repository.

apps/desktop/src/backend/DesktopBackendConfiguration.tslines 820–822Outdated Β· ff0be38Open thread
……@@ -651,6 +742,125 @@ export const make = Effect.gen(function* () {
819+ Effect.map((value) => ({ _tag: "Success", value }) as const),
820+ Effect.catchTag("ExistingLocalBackendPairingError", (error) =>
821+ Effect.succeed({ _tag: "Failure", error } as const),
822+ ),
MmacroscopeappBoton lines 820–822Aug 24, 2026

backend/DesktopBackendConfiguration.ts:822

Statically known tagged failures should be recovered with Effect.catchTags({ ... }), including for a single tag; Effect.catchTag is not used by this convention.

suggestion
1 Effect.catchTags({
2 ExistingLocalBackendPairingError: (error) =>
3 Effect.succeed({ _tag: "Failure", error } as const),
4 }),

No longer relevant as of 7bb191346942687efdb672729d62478bb64fe6d0

Sorry, I'm unable to act on this request because you do not have permissions within this repository.

Sorry, I'm unable to act on this request because you do not have permissions within this repository.

apps/desktop/src/backend/DesktopExistingLocalBackend.tslines 331–336Outdated Β· ff0be38Open thread
……@@ -0,0 +1,346 @@
333+ new ExistingLocalBackendPairingError({
334+ baseDir: input.backend.baseDir,
335+ origin: input.backend.origin,
336+ detail: cause instanceof Error ? cause.message : "The server rejected the connection.",
MmacroscopeappBoton lines 331–336Aug 24, 2026

backend/DesktopExistingLocalBackend.ts:336

ExistingLocalBackendPairingError discards the underlying failure and rebuilds its user-facing message from cause.message through detail. Conventions ask that the immediate error be preserved as cause and that message be derived only from stable structural attributes (and that detail not just copy cause.message).

Consider adding cause: Schema.optionalKey(Schema.Defect()) to the error class (optional because the missing-credential case has no underlying failure) and replacing free-text detail with a bounded discriminator the message getter maps, e.g. reason: Schema.Literals(["missing-credential", "token-exchange-rejected", "server-unavailable"]) β€” or two separate error classes, since the credential-missing and exchange-rejected cases already produce distinct caller-visible messages. Then pass cause through here instead of cause.message.

No longer relevant as of 7bb191346942687efdb672729d62478bb64fe6d0

Fixed in 7bb191346. The error now carries a bounded reason, preserves the immediate failure as an optional cause, and derives its message only from stable structural fields.

Sorry, I'm unable to act on this request because you do not have permissions within this repository.

Sorry, I'm unable to act on this request because you do not have permissions within this repository.

apps/web/src/environments/primary/desktopAuth.tsline 11Outdated Β· ff0be38Open thread
……@@ -1,4 +1,15 @@
8+ const primary = window.desktopBridge
9+ ?.getLocalEnvironmentBootstraps?.()
10+ .find((entry) => entry.id === PRIMARY_LOCAL_ENVIRONMENT_ID);
11+ return [primary?.httpBaseUrl ?? "", primary?.bootstrapToken ?? ""].join("\u0000");
MmacroscopeappBoton line 11Aug 24, 2026

🟠 High primary/desktopAuth.ts:11

After a primary is re-paired to the same running server, readDesktopPrimaryBearerToken returns the previously cached promise instead of fetching the new attachedBearerToken, so renderer requests continue using a stale session. readDesktopPrimaryConfigKey only changes for a different httpBaseUrl or bootstrapToken; include the bridge's attachment/session generation in this key, or explicitly invalidate the cache when reattachment succeeds.

βœ… Resolved in 7bb191346942687efdb672729d62478bb64fe6d0

Fixed in 7bb191346. Attached configs now expose a fresh opaque auth-session key on every successful re-pair, and the renderer includes it in the bearer cache key. Regression coverage keeps the origin and bootstrap credential stable while verifying a second token fetch.

Sorry, I'm unable to act on this request because you do not have permissions within this repository.

Cchatgpt-codex-connectorBotreviewed Β· 2 inline threadsAug 24, 2026
Synced from GitHub

πŸ’‘ Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ff0be38fb6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with πŸ‘.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

apps/desktop/src/backend/DesktopBackendConfiguration.tslines 903–907Outdated Β· ff0be38Open thread
……@@ -675,12 +885,26 @@ export const make = Effect.gen(function* () {
904+ existingResolution._tag === "Disabled" && existingResolution.reason === "independent-launch"
905+ ? path.join(environment.baseDir, "desktop-independent")
906+ : environment.baseDir;
907+ return yield* resolvePrimaryStartConfig({ ...shared, resourceMonitorPath, t3Home }).pipe(
Cchatgpt-codex-connectorBoton lines 903–907Aug 24, 2026

P1 Badge Re-select a port for the separate-backend escape hatch

When reconnection pairing fails while the replacement server is still listening on the attached port, choosing β€œStart Separate Backend” only changes t3Home here; resolvePrimaryStartConfig continues using the DesktopServerExposure port configured from the attachment during startup. Because desktop mode treats that explicit port as fixed, the new child repeatedly fails to bind, so this recovery option cannot actually provide a way out. Re-run free-port selection and reconfigure exposure before starting the independent backend.

AGENTS.md reference: AGENTS.md:L73-L73

Useful? React with πŸ‘Β / πŸ‘Ž.

Fixed in 7bb191346. Independent recovery now checks the attached exposure port, scans for a free Desktop port when it is occupied, and reconfigures exposure before resolving the managed child. A focused regression test covers the 41773 β†’ 3774 handoff.

apps/desktop/src/backend/DesktopExistingLocalBackend.tslines 108–114Outdated Β· ff0be38Open thread
……@@ -0,0 +1,346 @@
111+ const home = extractT3HomeAssignment(quotedWhole ? raw.slice(1, -1) : raw, {
112+ remainderIsValue: quotedWhole,
113+ });
114+ if (home !== null && home.length > 0) resolvedHome = home;
Cchatgpt-codex-connectorBoton lines 108–114Aug 24, 2026

P2 Badge Decode systemd escaping before probing the custom home

When an installed service uses a custom T3CODE_HOME containing %, \, or ", renderBootServiceUnit escapes those characters for systemd, but this parser returns the escaped unit-file spelling rather than the effective environment value. Discovery consequently probes a nonexistent path (for example, t3%%data instead of t3%data) and may silently attach to a lower-priority backend or start another one. Decode the escaping emitted by quoteSystemdValue before adding this directory to the discovery seeds.

Useful? React with πŸ‘Β / πŸ‘Ž.

Fixed in 7bb191346. The systemd parser now decodes the percent, backslash, and quote escaping emitted by quoteSystemdValue, with focused coverage for all three cases.

CcursorBotreviewed Β· 1 inline threadAug 24, 2026
Synced from GitHub

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 ff0be38fb6d44a141e3468b075bd831813d2b769. Configure here.

apps/desktop/src/backend/DesktopBackendPool.tsline 156Outdated Β· ff0be38Open thread
……@@ -103,6 +105,58 @@ import * as ElectronDialog from "../electron/ElectronDialog.ts";
153+
154+ if (result.response === 1) {
155+ yield* input.useIndependentBackendForLaunch;
156+ return true;
CcursorBoton line 156Aug 24, 2026

Separate backend reuses attach port

High Severity

Choosing Start Separate Backend after a lost attachment only flips launch mode to independent-launch. Port selection already ran at bootstrap from the attached server, and resolvePrimaryStartConfig keeps using serverExposure.backendConfig.port. The escape hatch therefore tries to bind the same port as the local service instead of scanning for a free one the way startup does.

Additional Locations (2)

Reviewed by Cursor Bugbot for commit ff0be38fb6d44a141e3468b075bd831813d2b769. Configure here.

Fixed in 7bb191346. The independent-launch path now re-selects and applies a free port before the managed backend starts; the focused regression test verifies it does not reuse the attached port.

Synced from GitHub

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable β€” The PR introduces a default-on local-backend attachment workflow spanning Desktop startup, server authentication grants, bearer-session handling, IPC/contracts, recovery, and the Connections UI. Its cross-cutting runtime and security-sensitive behavior requires human review.

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

Added 1 commit

  • fix(desktop): harden attached backend recovery7bb1913
apps/desktop/src/backend/DesktopExistingLocalBackend.tslines 97–100Outdated Β· 7bb1913Open thread
……@@ -0,0 +1,375 @@
97+ const marker = "T3CODE_HOME=";
98+ const index = envValue.indexOf(marker);
99+ if (index === -1) return null;
100+ const rest = envValue.slice(index + marker.length);
MmacroscopeappBoton lines 97–100Aug 24, 2026

🟑 Medium backend/DesktopExistingLocalBackend.ts:97

extractT3HomeAssignment treats OLD_T3CODE_HOME=/wrong as a T3CODE_HOME assignment, so parseSystemdT3Home returns /wrong and skips the actual service home. Match T3CODE_HOME= only at the start of the value or after an assignment-token boundary such as whitespace.

βœ… Resolved in 4ef86b5f05c6350cb3e9111ef2152685cb0f4fd1

Fixed in 4ef86b5f0. Assignment discovery now requires a complete T3CODE_HOME token boundary, with regression coverage for OLD_T3CODE_HOME before the real assignment and by itself.

Sorry, I'm unable to act on this request because you do not have permissions within this repository.

apps/desktop/src/backend/DesktopExistingLocalBackend.tslines 123–125Outdated Β· 7bb1913Open thread
……@@ -0,0 +1,375 @@
122+ }
123+ if (options?.remainderIsValue === true) {
124+ return rest.length > 0 ? rest.replaceAll("%%", "%") : null;
125+ }
MmacroscopeappBoton lines 123–125Aug 24, 2026

🟑 Medium backend/DesktopExistingLocalBackend.ts:123

parseSystemdT3Home returns /srv/t3" "OTHER=value for Environment="T3CODE_HOME=/srv/t3" "OTHER=value", so Desktop searches a nonexistent base directory and fails to discover the running service. After the outer quotes are removed, remainderIsValue returns the entire remainder instead of stopping at the closing quote; stop at that quote before returning the value.

βœ… Resolved in 4ef86b5f05c6350cb3e9111ef2152685cb0f4fd1

Fixed in 4ef86b5f0. Whole-quoted assignment lists now stop the home value at the closing quote; a focused test covers a following OTHER assignment.

Sorry, I'm unable to act on this request because you do not have permissions within this repository.

MmacroscopeappBotreviewed Β· 1 inline threadAug 24, 2026
Synced from GitHub

UI Consistency: 1 issue found

One concrete regression in the changed Connections settings scope: a failed attachment-state IPC call silently removes existing desktop backend controls instead of degrading to a visible, recoverable state.

Everything else in scope looks consistent β€” the new row uses the shared SettingsRow + Switch primitives with an aria-label, no primitive geometry/focus-ring overrides, and the search catalog entry is registered via searchableSetting with desktopOnly.

Posted via Macroscope β€” UI Consistency

apps/web/src/components/settings/ConnectionsSettings.tsxlines 1995–2001Outdated Β· 7bb1913Open thread
……@@ -1971,6 +1986,49 @@ export function ConnectionsSettings() {
1998+ },
1999+ () => {
2000+ if (!cancelled) setExistingLocalBackendState(null);
2001+ },
MmacroscopeappBoton lines 1995–2001Aug 24, 2026

settings/ConnectionsSettings.tsx:2001

A rejected getExistingLocalBackendState() leaves existingLocalBackendState at null, which is indistinguishable from "still loading". resolveDesktopBackendControlState then returns canManageDesktopBackend: false / isAttached: false, so the new Attach row is hidden and the canManageDesktopBackend ? ... : isAttached ? ... : null branch below renders nothing β€” Network access, the endpoint rows and Tailscale HTTPS all disappear permanently for a desktop user who had them before this change, with no error or retry surfaced.

Consider distinguishing "load failed" from "pending" (e.g. a separate error/loaded flag) and, on failure, either keeping the managed rows visible or rendering a recovery row with retry β€” the same pattern this file already documents for the WSL state load failure ("keep a recovery row (with retry) visible instead of silently hiding the section"). Hiding only while genuinely pending preserves the intended attached-state behavior.

No longer relevant as of 4ef86b5f05c6350cb3e9111ef2152685cb0f4fd1

Fixed in 4ef86b5f0. A failed attachment-state load is now distinct from pending state and falls back to the managed Desktop controls, preserving Network access, endpoints, and Tailscale rows. The control-state regression test covers this failure path.

Sorry, I'm unable to act on this request because you do not have permissions within this repository.

MmacroscopeappBotreviewed Β· 2 inline threadsAug 24, 2026
Synced from GitHub

Two compatibility re-export shims were introduced while moving declarations to their new canonical modules. Conventions ask that the moved declaration have a single canonical path with every consumer updated mechanically, rather than leaving a re-export behind.

Posted via Macroscope β€” Effect Service Conventions

apps/server/src/cloud/bootService.tslines 37–41Outdated Β· 7bb1913Open thread
……@@ -29,12 +34,11 @@ import {
38+ BOOT_SERVICE_LAUNCHD_LABEL,
39+ BOOT_SERVICE_PLIST_FILE,
40+ BOOT_SERVICE_UNIT_FILE,
41+} from "@t3tools/shared/bootServiceIdentity";
MmacroscopeappBoton lines 37–41Aug 24, 2026

cloud/bootService.ts:41

These constants moved to @t3tools/shared/bootServiceIdentity, and no consumer reads them through BootService.* anymore, so this block is a dead compatibility re-export. Consider deleting it and letting callers import from the shared module directly (the local import at the top already covers this file's own usage).

suggestion
1export const BOOT_SERVICE_UNIT_ENV = "T3_BOOT_SERVICE_UNIT";

No longer relevant as of 4ef86b5f05c6350cb3e9111ef2152685cb0f4fd1

Fixed in 4ef86b5f0. The unused boot-service identity re-export was removed; consumers already import the shared identity module directly.

Sorry, I'm unable to act on this request because you do not have permissions within this repository.

apps/desktop/src/app/DesktopApp.tslines 33–33Outdated Β· 7bb1913Open thread
……@@ -29,28 +30,13 @@ import * as DesktopState from "./DesktopState.ts";
32-const DEFAULT_DESKTOP_BACKEND_PORT = 3773;
33-const MAX_TCP_PORT = 65_535;
34-const DESKTOP_BACKEND_PORT_PROBE_HOSTS = ["127.0.0.1", "0.0.0.0", "::"] as const;
33+export { DesktopBackendPortUnavailableError } from "../backend/DesktopBackendPort.ts";
MmacroscopeappBoton lines 33–33Aug 24, 2026

app/DesktopApp.ts:33

DesktopBackendPortUnavailableError now lives in ../backend/DesktopBackendPort.ts, so this line is a compatibility re-export shim. Its only consumer is apps/desktop/src/app/DesktopAppErrors.test.ts, which still imports the error from ./DesktopApp.ts.

Consider updating that test to import from ../backend/DesktopBackendPort.ts (moving the port-error assertion into a DesktopBackendPort test if that reads better) and dropping this re-export so the error has one canonical path.

No longer relevant as of 4ef86b5f05c6350cb3e9111ef2152685cb0f4fd1

Fixed in 4ef86b5f0. The port error test now imports from its canonical backend module and the compatibility re-export is removed.

Sorry, I'm unable to act on this request because you do not have permissions within this repository.

Added 1 commit

  • fix(desktop): cover attachment review edge cases4ef86b5
MmacroscopeappBotreviewed Β· 1 inline threadAug 24, 2026
Synced from GitHub

One finding on the new Connections row: the attach toggle immediately relaunches Desktop, but nothing in the row says so and there is no confirmation, unlike the equally consequential "WSL only" switch in the same section.

Posted via Macroscope β€” UI Consistency

apps/web/src/components/settings/ConnectionsSettings.tsxlines 3144–3144Outdated Β· 4ef86b5Open thread
……@@ -3062,9 +3131,42 @@ export function ConnectionsSettings() {
3141+ ? existingLocalBackendState.origin
3142+ ? `Connected to the T3 Code server already running at ${existingLocalBackendState.origin}. Network exposure is managed where that server was launched.`
3143+ : "Connected to the T3 Code server already running on this machine. Network exposure is managed where that server was launched."
3144+ : "Connect to a T3 Code server already running on this machine instead of starting another one."
MmacroscopeappBoton lines 3144–3144Aug 24, 2026

Flipping this switch calls setAttachExistingLocalBackend, which relaunches the app (lifecycle.relaunch) as soon as the value changes, so the window disappears with no warning and the awaited promise never resolves. Every other consequential backend switch in this section tells the user first: the "WSL only" row states "T3 Code restarts when you change this" and routes through the pending-change confirmation dialog. Consider at least stating the restart in the description here (and ideally reusing the existing confirm-before-apply pattern) so the row's behavior matches its neighbours.

suggestion
1 : "Connect to a T3 Code server already running on this machine instead of starting another one. T3 Code restarts when you change this."

Posted via Macroscope β€” UI Consistency

Fixed in 2d337ebf1. The attachment row now states that T3 Code restarts when the setting changes in both attached and unattached states.

Sorry, I'm unable to act on this request because you do not have permissions within this repository.

Added 1 commit

  • fix(web): clarify attachment relaunch behavior2d337eb
MmacroscopeappBotreviewed Β· 1 inline threadAug 24, 2026
Synced from GitHub

One finding on the changed session-scope derivation in ConnectionsSettings.tsx. The rest of the new attach row (shared SettingsRow + Switch, searchableSetting title/anchor, restart wording, load-failure fallback) matches the existing Connections patterns.

Posted via Macroscope β€” UI Consistency

apps/web/src/components/settings/ConnectionsSettings.tsxlines 1776–1780Outdated Β· 2d337ebOpen thread
……@@ -1755,12 +1763,22 @@ export function ConnectionsSettings() {
1777+ ? authenticatedSessionScopes
1778+ : desktopBridge
1779+ ? AuthAdministrativeScopes
1780+ : authenticatedSessionScopes;
MmacroscopeappBoton lines 1776–1780Aug 24, 2026

settings/ConnectionsSettings.tsx:1780

When attached, desktop now derives currentSessionScopes from the primary session instead of assuming AuthAdministrativeScopes. primarySessionState.data is null on first load (and stays null if that request fails), so authenticatedSessionScopes is null, canManageLocalBackend becomes false, and the whole This environment section falls back to the browser-style Administrative access row β€” which drops the WSL row, the network rows, and the new Attach to running server switch, i.e. the only in-app way to stop attaching. On a fresh load this also shows up as the section flipping to the fallback and back once the session resolves.

The attach toggle is a local desktop preference, so it should not depend on the remote session's scopes. Smallest fix: keep the previous desktop assumption until the session state has actually resolved.

suggestion
1 const currentSessionScopes =
2 isAttachedToExistingLocalBackend && primarySessionState.data !== null
3 ? authenticatedSessionScopes
4 : desktopBridge
5 ? AuthAdministrativeScopes
6 : authenticatedSessionScopes;

No longer relevant as of ed06cc25898e4726462b77ee80f76539c6cfaa6b

Fixed in ed06cc258. Desktop now keeps its administrative scope fallback until the attached primary session has actually resolved, so local attachment and WSL controls do not disappear during loading or a failed initial request. Focused tests cover both the loading fallback and the resolved remote scopes.

Sorry, I'm unable to act on this request because you do not have permissions within this repository.

Added 1 commit

  • fix(web): keep attachment controls during session loaded06cc2

Note

πŸ€– GPT-5.6 Sol responding on behalf of Theo

We're closing this PR as we clean up the T3 Code backlog. Thank you for taking the time to put this together.

Attaching desktop to an existing local backend needs one contract for authentication, version matching, shutdown ownership, updates, and recovery. This 51-file implementation adds that lifecycle before we have selected the contract.

If you believe we closed this in error, please reopen the PR and leave a comment explaining what we missed.

Synced from GitHub

Note

πŸ€– GPT-5.6 Sol responding on behalf of Theo

We're closing this PR as we clean up the T3 Code backlog. Thank you for taking the time to put this together.

Attaching desktop to an existing local backend needs one contract for authentication, version matching, shutdown ownership, updates, and recovery. This 51-file implementation adds that lifecycle before we have selected the contract.

If you believe we closed this in error, please reopen the PR and leave a comment explaining what we missed.

Thanks for the reply @t3dotgg.

Me and my bot will write a plan that addresses the architecture problems you wrote above and add it to the feature request I opened for this. There are some endpoints missing that I would also like to address to ensure the desktop has access to the attached backend's settings, just to avoid the user having to open another interface to update those.