feat(dev): Make t3 code dev instances shareable over Tailscale - #4556
Conversation
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
ApprovabilityVerdict: Needs human review This PR introduces a new Tailscale sharing feature with significant runtime behavior changes including port-scoped session cookies, extended dev token TTLs (5min→24h), new CORS configuration, and Tailscale CLI integration. An unresolved medium-severity comment identifies an issue with orphan process cleanup. Human review recommended for these auth-adjacent and feature-enablement changes. You can customize Macroscope's approvability policy. Learn more. |
A submodule's `.git` is also a file, so the file-vs-directory check alone classified every submodule as a linked worktree and handed it a worktree-local `.t3`. Read the `gitdir:` pointer and require it to land in `.git/worktrees` (compared as path segments, not a substring). Resolve cwd through a HostProcessWorkingDirectory reference rather than calling process.cwd() directly, so the precedence chain is testable. Add tests for the submodule and malformed-`.git`-file cases, plus end-to-end coverage of all four home-precedence outcomes, which had no test before. Document that worktree state lands in `.t3/userdata`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The `.git/worktrees` segment check rejected worktrees whose common dir is not named `.git`. A worktree of a bare repo points at `<repo>.git/worktrees/<name>`, and $GIT_COMMON_DIR can be anything. Git always uses the `worktrees/<name>` tail, so match on that instead; `<git-dir>/modules/<name>` still fails it. A blank `--home-dir` also counted as a selection under `??`, skipping the worktree default and landing on the shared home — the outcome this precedence exists to prevent. Trim before choosing. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Took ownership of this PR, reviewed it, and pushed one fix. Fixed:
|
Desktop dev is not single-origin: the renderer gets VITE_HTTP_URL and VITE_WS_URL baked to loopback, so a tailnet visitor would load the UI and then watch it dial its own 127.0.0.1 for the backend. Sharing also overwrote VITE_DEV_SERVER_URL, which is the origin Electron loads the renderer from. Warn and carry on serving locally instead of handing out a URL that is broken in a way the user cannot see. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
edc8e4f to
dbad3bc
Compare
|
Rebased onto the updated base — #4555 picked up two new commits ( Resolution kept the base's improvements and adapted this PR to them:
45/45 tests passing (the base's 5 new tests merged in cleanly), typecheck 0 errors, and the runner still resolves the same stable offset ( |
Two findings from review. stderrPreview carried the first 200 chars of raw CLI stderr into DevShareError.detail and on into dev-runner's logs. tailscale prints auth keys (tskey-...) and node names to stderr, and the package's own tests already seed a token there and assert it does not leak — stderrPreview walked straight past that invariant. Replace it with stderrDiagnostic, a closed set of labels (no-existing-handler, not-logged-in, permission-denied, unknown); callers match on the label and render our own wording. Unrecognized stderr degrades to "unknown" rather than falling back to text. Port probing was narrowed to loopback, but --host/T3CODE_HOST moves the backend onto another interface, and that interface decides whether the bind succeeds. A port free on loopback and taken there was selected and then failed to bind. Probe the configured host alongside loopback. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The runner deleted VITE_HTTP_URL/VITE_WS_URL for dev and dev:web, but vite.config.ts calls loadRepoEnv, which merges .env/.env.local underneath the process env. A developer with either URL in their .env got it back, and Vite baked it into the bundle — pinning the client to localhost and breaking every non-localhost origin. Invisible, too: the page still loads, then dials the visitor's own machine. Absence is not a usable signal, so state the intent: the runner sets T3CODE_SINGLE_ORIGIN_DEV=1, and Vite ignores both URLs when it is set. VITE_HTTP_URL is now pinned in `define` as well, since Vite's automatic VITE_ exposure would otherwise leak it past the check. Verified by A/B with a .env setting both URLs: the served module carries "http://localhost:13773" without this change and "" with it, proxying still 200. Also point the dev:desktop --share refusal at `dev` rather than `dev:web`, which starts no backend of its own. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
--host/T3CODE_HOST configures the backend. Vite takes its bind address from HOST, which the runner sets for desktop alone, so the web port stays on loopback. Probing it against the backend's interface could reject a port that was free where Vite would actually bind, shifting offsets or failing startup for no reason. The checker now takes the port's role. Passed explicitly rather than inferred from the port number, which stops discriminating once a large T3CODE_PORT_OFFSET pushes the web port past the server base. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Review round converged — CI green on Fixed
Declined, with reasoningRefusing Things I checked empirically rather than by reading
Still worth a human eyeTwo intentional behavior changes that will generate reports if they surprise people: session cookies are renamed to 65 tests, typecheck 0 errors, lint clean on changed files. Still babysitting. |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (5)
scripts/dev-runner.ts (3)
639-639: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueRecomputes the web port instead of reusing
env.PORT.
createDevRunnerEnvalready derived the same value intoenv.PORT; duplicating theBASE_WEB_PORT + webOffsetarithmetic here is a second source of truth that will drift if web-port resolution ever changes.♻️ Suggested change
- const sharedWebPort = BASE_WEB_PORT + webOffset; + const sharedWebPort = Number(env.PORT);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@scripts/dev-runner.ts` at line 639, Update the code near sharedWebPort to reuse the resolved env.PORT value produced by createDevRunnerEnv instead of recomputing BASE_WEB_PORT + webOffset. Remove the duplicate arithmetic while preserving the existing shared web-port behavior.
295-324: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value
T3CODE_PORTis now set identically in both branches.Lines 296 and 319 assign the same value; hoisting it above the
isDesktopModesplit would make the branch about the Vite URL/marker policy only.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@scripts/dev-runner.ts` around lines 295 - 324, Move the shared T3CODE_PORT assignment above the isDesktopMode conditional, then remove the duplicate assignments from both branches. Keep the existing Vite URL and T3CODE_SINGLE_ORIGIN_DEV handling unchanged so the split only controls URL and marker policy.
206-216: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low valueNumeric
T3CODE_DEV_INSTANCEbypasses the bounds applied elsewhere.Unlike
T3CODE_PORT_OFFSET(negative-checked) and the hashed path (% MAX_HASH_OFFSET), a numeric seed is used verbatim, soT3CODE_DEV_INSTANCE=90000resolves to an out-of-range port pair and surfaces asDevRunnerPortExhaustedErrorrather than a message naming the bad input. Consider validating it against the same offset range.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@scripts/dev-runner.ts` around lines 206 - 216, Update the numeric-seed branch in the dev-runner offset resolution to validate Number(seed) against the same allowed offset bounds used by the other paths, rejecting values outside that range with an error that identifies the invalid T3CODE_DEV_INSTANCE input; preserve the existing hashed behavior and valid numeric offsets.scripts/lib/dev-share.test.ts (1)
86-161: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueConsider covering the
no-tailnet-name/tailscale-unavailablebranches.Both produce user-facing hints and neither is exercised — a status payload without
Self.DNSNameand a non-zerostatusexit would close the gap cheaply given the existingspawnerLayer.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@scripts/lib/dev-share.test.ts` around lines 86 - 161, Add tests in the shareDevServer suite for the no-tailnet-name and tailscale-unavailable branches: configure spawnerLayer with a successful status response whose payload omits Self.DNSName, and with a non-zero status exit respectively. Flip each effect and assert the resulting DevShareError reason and user-facing hint message.packages/tailscale/src/tailscale.ts (1)
252-254: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueDuplicate
stderrDiagnosticOf(stderr)evaluation in both error constructions. Both sites call the classifier once for the presence check and again for the value; binding the result once makes the intent clearer and avoids re-running the regex list.
packages/tailscale/src/tailscale.ts#L252-L254: hoistconst stderrDiagnostic = stderrDiagnosticOf(stderr);before theTailscaleCommandExitErrorconstruction and spread...(stderrDiagnostic ? { stderrDiagnostic } : {}).packages/tailscale/src/tailscale.ts#L318-L320: apply the same hoist insiderunTailscaleCommand.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/tailscale/src/tailscale.ts` around lines 252 - 254, Hoist the result of stderrDiagnosticOf(stderr) into a local stderrDiagnostic before each TailscaleCommandExitError construction, then reuse it for the conditional stderrDiagnostic property. Apply this at packages/tailscale/src/tailscale.ts lines 252-254 and 318-320 within runTailscaleCommand, preserving the existing behavior when no diagnostic is available.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/server/src/auth/EnvironmentAuth.test.ts`:
- Around line 18-26: The makeServerConfigLayer helper currently allows overrides
to replace TEST_SERVER_PORT, which can desynchronize the server port and
makeCookieRequest’s fixed cookie name. Apply ...overrides before assigning port:
TEST_SERVER_PORT so the test port is always pinned while preserving all other
overrides.
In `@docs/reference/scripts.md`:
- Line 4: Update the documented base and sharing commands in the web-stack
startup instructions to use the required Bun workflow: replace the base command
with “bun run dev” and the sharing variant with “bun run dev:share”. Preserve
the existing descriptions of sharing behavior.
In `@packages/tailscale/src/tailscale.test.ts`:
- Around line 29-40: Update assertCarriesNoSecret to recursively inspect strings
inside nested objects, arrays, and causes, while preserving the message check.
Track visited objects with cycle protection so cyclic error structures terminate
safely, and retain field-specific context in assertion failures where practical.
---
Nitpick comments:
In `@packages/tailscale/src/tailscale.ts`:
- Around line 252-254: Hoist the result of stderrDiagnosticOf(stderr) into a
local stderrDiagnostic before each TailscaleCommandExitError construction, then
reuse it for the conditional stderrDiagnostic property. Apply this at
packages/tailscale/src/tailscale.ts lines 252-254 and 318-320 within
runTailscaleCommand, preserving the existing behavior when no diagnostic is
available.
In `@scripts/dev-runner.ts`:
- Line 639: Update the code near sharedWebPort to reuse the resolved env.PORT
value produced by createDevRunnerEnv instead of recomputing BASE_WEB_PORT +
webOffset. Remove the duplicate arithmetic while preserving the existing shared
web-port behavior.
- Around line 295-324: Move the shared T3CODE_PORT assignment above the
isDesktopMode conditional, then remove the duplicate assignments from both
branches. Keep the existing Vite URL and T3CODE_SINGLE_ORIGIN_DEV handling
unchanged so the split only controls URL and marker policy.
- Around line 206-216: Update the numeric-seed branch in the dev-runner offset
resolution to validate Number(seed) against the same allowed offset bounds used
by the other paths, rejecting values outside that range with an error that
identifies the invalid T3CODE_DEV_INSTANCE input; preserve the existing hashed
behavior and valid numeric offsets.
In `@scripts/lib/dev-share.test.ts`:
- Around line 86-161: Add tests in the shareDevServer suite for the
no-tailnet-name and tailscale-unavailable branches: configure spawnerLayer with
a successful status response whose payload omits Self.DNSName, and with a
non-zero status exit respectively. Flip each effect and assert the resulting
DevShareError reason and user-facing hint message.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 6650ded2-f1b8-41f0-88dd-2bcf360de729
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (22)
.agents/skills/test-t3-app/SKILL.mdAGENTS.mdapps/server/src/auth/EnvironmentAuth.test.tsapps/server/src/auth/EnvironmentAuth.tsapps/server/src/auth/EnvironmentAuthPolicy.test.tsapps/server/src/auth/EnvironmentAuthPolicy.tsapps/server/src/auth/PairingGrantStore.tsapps/server/src/auth/SessionStore.tsapps/server/src/auth/utils.tsapps/server/src/http.tsapps/web/vite.config.tsdocs/reference/scripts.mdpackage.jsonpackages/shared/package.jsonpackages/shared/src/devProxy.tspackages/tailscale/src/tailscale.test.tspackages/tailscale/src/tailscale.tsscripts/dev-runner.test.tsscripts/dev-runner.tsscripts/lib/dev-share.test.tsscripts/lib/dev-share.tsscripts/package.json
Make assertCarriesNoSecret recurse. It checked only top-level strings, so a leak inside a nested cause or an array would have passed while the comment claimed coverage for fields added later. Verified the gap is real: the shallow version passes on both a nested cause and an array carrying the token, and the recursive one fails on each. Walks message/cause explicitly (getters on Error subclasses are not own enumerable props) and tracks visited objects so cyclic causes terminate. Pin port after overrides in makeServerConfigLayer. makeCookieRequest builds the cookie name from TEST_SERVER_PORT, so an override that changed the port would leave the server reading one cookie name while requests sent another. Use bun run in docs/reference/scripts.md for the dev and dev:share lines, matching AGENTS.md. Confirmed `bun run dev --share` forwards the flag. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Rebasing onto the updated #4556 turned up two doc drifts: the base moved the script list from `vp run` to `bun run`, and the state-directory paragraph still described the fixed `userdata` preference that the mtime tiebreak replaced. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The Effect service conventions check was failing on this, correctly. The single DevShareError violated four rules at once: a `reason` discriminator chose both the user-facing message and the caller's remedy, the `message` getter was a lookup table over it, `detail` copied `cause.message` and was then used to build that message, and the underlying TailscaleCommandError was discarded entirely so the error chain ended at the wrapper. Now three classes, one per genuinely distinct failure: TailscaleUnavailableError and TailnetNameMissingError each have one fixed message and one remedy; DevServeFailedError keeps a `stage` discriminator, which is legitimate — both stages share the same semantics (a serve invocation failed for this port) and differ only in which one. Each wrapping construction now preserves the immediate error as `cause`, and every message derives solely from structural fields. unshareDevServer returns the structured cause rather than a flattened string so its caller can do the same. TailnetNameMissingError has no cause because none exists — the status read succeeded and simply had no name. Verified the chain end to end: the wrapped TailscaleCommandExitError is reachable with exitCode and stderrDiagnostic intact, and the auth token in stderr still never reaches the message. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Fixed the failing The check was right, and its finding had been sitting in an orphaned inline comment from an earlier commit — the force-pushes marked it outdated, so it never showed up in my unreplied-thread sweep. My fault for only ever checking live threads.
Now three classes. Every wrapping construction now keeps the immediate error as Verified the chain survives rather than assuming it: the wrapped 204 tests passing across |
Switching the cookie-name discriminator to devUrl alone dropped the port scope for packaged desktop, reverting #1898: desktop instances scan upward from 3773 for a free port and bind 127.0.0.1, so two of them share a hostname and would clobber each other's t3_session. Scope on devUrl OR desktop mode, and restore the assertions that had been relaxed to match the regression. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
HOST is Vite's own bind address and gates the HMR pin in vite.config.ts; the runner only sets it for dev:desktop. An exported one (container, CI, HOST=0.0.0.0 habit) survived into dev/dev:web and pinned the HMR socket to that address — invisible over a shared origin, since the page loads and only HMR dials the wrong machine. Clear it alongside the VITE_* URLs. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Pushed two fixes from a fresh review pass, and answered the remaining threads. Fixed
Declined, with reasoning (in the threads)
A deliberate line in the sand, per review discussion: the lease machinery at HEAD is internally consistent (prepare → claim → publish, uninterruptible, restore on failure) and this is a dev-only convenience feature — further hardening rounds against ever-rarer races are out of scope. Verified: full server suite 1623 passed, scripts 180, typecheck 0 errors, and a live 🤖 Generated with Claude Code |
Five commits of ownership machinery (lease files, claim/handoff ordering, restore-on-failure) existed to close one race: a fast restart where the old runner's finalizer tears down a mapping a new runner just installed on the same port. For a dev-only convenience that failure is visible — the shared URL stops working — and re-running --share fixes it, so the protocol cost more than the race it prevented. Each hardening round also surfaced new races inside the mechanism itself. Back to the plain shape: clear any stale mapping, serve, unshare on exit, registered atomically via acquireRelease. Stale mappings from killed runs were always handled by the pre-clear on the next share, and still are. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Removed the lease protocol in The five commits of ownership machinery ( Back to the plain shape: clear any stale mapping, serve, unshare on exit, registered atomically via To reviewers and bots alike: the fast-restart teardown race is now accepted behavior, documented in the comment at the share block in Net: −286 lines, PR is now 1,288 insertions (630 product / 632 tests). Verified after the cut: 175 script tests, typecheck 0 errors, and a live 🤖 Generated with Claude Code |
Stopping the stack has no obvious right answer, and the wrong answers are severe: killing the runner PID orphans the node --watch server children (vp does not reap them; they hold the port through the next start), and guessing a pkill pattern has twice killed the agent driving the session — its argv contains the worktree path — and can reach other worktrees' servers. Print the one safe command instead: a process-group kill, with the pgid resolved via ps because under `bun run dev` the group leader is bun, not the runner. Verified live: the printed command run verbatim leaves zero survivors with the worktree's T3CODE_HOME. AGENTS.md and the test-t3-app skill now direct agents to that line and name the pattern-kill footgun explicitly. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Lint convention: platform comes from the injected reference, not process.platform, so tests can provide it explicitly. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Two more commits: the dev-runner now prints the exact stop command at startup. Why ( The printed command targets the process group, with the pgid resolved via
177 script tests, typecheck 0 errors, lint clean. 🤖 Generated with Claude Code |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 08d9260. Configure here.
| export function resolveStopCommand(pid: number, platform: NodeJS.Platform): string { | ||
| return platform === "win32" | ||
| ? `taskkill /pid ${String(pid)} /T` | ||
| : `kill -TERM -"$(ps -o pgid= -p ${String(pid)} | tr -d ' ')"`; |
There was a problem hiding this comment.
Deferred PGID breaks orphan cleanup
Medium Severity
The Unix stop command embeds the runner PID and resolves the process group with ps only when the user runs it. After the runner has exited, that lookup fails or can hit a recycled PID, so the logged command may not stop orphaned node --watch children even though the new skill text says it should.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 08d9260. Configure here.
There was a problem hiding this comment.
Also resolved by the revert in 9055f7c5d — the stop command, and the skill text claiming it cleans up watchers, are both gone.
The finding is correct: resolving the pgid at execution time means the command stops meaning anything the moment the runner exits (lookup fails, or worse, hits a recycled PID). Together with the sibling thread about inherited process groups, the two failure modes bracket the design — wrong while running under a harness, wrong after exiting anywhere — so the right fix was removal, not repair.
Reverts faf6467 and 08d9260. The printed group-kill is unsafe in the launch context it was designed for: a runner spawned by an agent harness or automation tool (no job control) inherits its parent's process group, so `kill -TERM -<pgid>` reaches the harness and unrelated siblings — the exact collateral the command existed to prevent. My earlier live verification missed this because bash's job control gave the nohup'd runner its own group. Resolving the pgid at kill time also fails or hits a recycled PID once the runner has exited, so it cannot clean up the orphaned watch children either. Doing this safely means setsid at spawn plus a pidfile, which is more machinery than this PR should grow. Out. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…docs Single-origin browser dev proxies backend traffic to localhost. A backend bound only to a specific interface (--host 192.168.1.10) leaves localhost unanswered, so every proxied request fails in a way that reads as a broken server rather than an unsupported flag combination. Reject it at startup with the two working alternatives (wildcard bind, --share). dev:server and dev:desktop don't proxy and keep specific-interface binds. Also returns the docs this PR touched to `vp run dev` — vp is this repo's task runner and forwards --share fine; the `bun run` wording was introduced by this PR itself responding to a bot nudge, matching neither main nor the surrounding lines. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Acted on the external review feedback in Removed: the printed stop command (
|
Rebasing onto the updated #4556 turned up two doc drifts: the base moved the script list from `vp run` to `bun run`, and the state-directory paragraph still described the fixed `userdata` preference that the mtime tiebreak replaced. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
## What's Changed * feat(dev): Make t3 code dev instances shareable over Tailscale by @t3dotgg in pingdotgg/t3code#4556 * fix(dev): skip browser-blocked ports by @t3dotgg in pingdotgg/t3code#4608 **Full Changelog**: pingdotgg/t3code@v0.0.29-nightly.20260727.914...v0.0.29-nightly.20260727.915 Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.29-nightly.20260727.915
Opening a dev server from a remote client showed ERR_CONNECTION_REFUSED. The client rewrote `localhost:PORT` to the environment's hostname, kept the port and scheme, and navigated. That address only works if the port is independently published there, which it usually is not — dev servers bind loopback. The failure surfaced as a browser error page, indistinguishable from a broken app, so agents reported it as one. Reachability is now resolved by the environment, which is the only side that knows what is listening and what the tailnet routes. A new `preview.resolvePort` RPC answers with a URL it has verified: - a same-machine client keeps plain localhost; - an existing `tailscale serve` route is reused, at whatever port and scheme it was published under; - a port already answering on the environment's own address (WSL, LAN, a tunnel) uses that address and publishes nothing; - otherwise a tailnet-only HTTPS route is published, probed until it answers, and withdrawn when the dev server exits or the server shuts down. When none of those work it fails with a reason and a next action instead of a URL that cannot load. Navigation, the port list, chat links, and agent `preview_navigate`/`preview_open` all route through it; the old synchronous rewrite is kept for labels only. Two loopback-family bugs fell out of verifying this end to end. Vite's default `--host localhost` binds `::1` only, so `tailscale serve` mappings pinned to `127.0.0.1` proxy to nothing and answer 502 — `vp run dev --share` produced a URL that never worked. Both the new resolver and dev-share now target `localhost` and let tailscale pick the family. Upstream, not fork-local: both halves came from merged upstream PRs (pingdotgg#4011 rewrote the URL, pingdotgg#4556 added sharing) and the seam between them was never covered. Promote after merge.
Opening a dev server from a remote client showed ERR_CONNECTION_REFUSED. The client rewrote `localhost:PORT` to the environment's hostname, kept the port and scheme, and navigated. That address only works if the port is independently published there, which it usually is not — dev servers bind loopback. The failure surfaced as a browser error page, indistinguishable from a broken app, so agents reported it as one. Reachability is now resolved by the environment, which is the only side that knows what is listening and what the tailnet routes. A new `preview.resolvePort` RPC answers with a URL it has verified: - a same-machine client keeps plain localhost; - an existing `tailscale serve` route is reused, at whatever port and scheme it was published under; - a port already answering on the environment's own address (WSL, LAN, a tunnel) uses that address and publishes nothing; - otherwise a tailnet-only HTTPS route is published, probed until it answers, and withdrawn when the dev server exits or the server shuts down. When none of those work it fails with a reason and a next action instead of a URL that cannot load. Navigation, the port list, chat links, and agent `preview_navigate`/`preview_open` all route through it; the old synchronous rewrite is kept for labels only. Two loopback-family bugs fell out of verifying this end to end. Vite's default `--host localhost` binds `::1` only, so `tailscale serve` mappings pinned to `127.0.0.1` proxy to nothing and answer 502 — `vp run dev --share` produced a URL that never worked. Both the new resolver and dev-share now target `localhost` and let tailscale pick the family. Upstream, not fork-local: both halves came from merged upstream PRs (pingdotgg#4011 rewrote the URL, pingdotgg#4556 added sharing) and the seam between them was never covered. Promote after merge.
Opening a dev server from a remote client showed ERR_CONNECTION_REFUSED. The client rewrote `localhost:PORT` to the environment's hostname, kept the port and scheme, and navigated. That address only works if the port is independently published there, which it usually is not — dev servers bind loopback. The failure surfaced as a browser error page, indistinguishable from a broken app, so agents reported it as one. Reachability is now resolved by the environment, which is the only side that knows what is listening and what the tailnet routes. A new `preview.resolvePort` RPC answers with a URL it has verified: - a same-machine client keeps plain localhost; - an existing `tailscale serve` route is reused, at whatever port and scheme it was published under; - a port already answering on the environment's own address (WSL, LAN, a tunnel) uses that address and publishes nothing; - otherwise a tailnet-only HTTPS route is published, probed until it answers, and withdrawn when the dev server exits or the server shuts down. When none of those work it fails with a reason and a next action instead of a URL that cannot load. Navigation, the port list, chat links, and agent `preview_navigate`/`preview_open` all route through it; the old synchronous rewrite is kept for labels only. Two loopback-family bugs fell out of verifying this end to end. Vite's default `--host localhost` binds `::1` only, so `tailscale serve` mappings pinned to `127.0.0.1` proxy to nothing and answer 502 — `vp run dev --share` produced a URL that never worked. Both the new resolver and dev-share now target `localhost` and let tailscale pick the family. Upstream, not fork-local: both halves came from merged upstream PRs (pingdotgg#4011 rewrote the URL, pingdotgg#4556 added sharing) and the seam between them was never covered. Promote after merge.
Opening a dev server from a remote client showed ERR_CONNECTION_REFUSED. The client rewrote `localhost:PORT` to the environment's hostname, kept the port and scheme, and navigated. That address only works if the port is independently published there, which it usually is not — dev servers bind loopback. The failure surfaced as a browser error page, indistinguishable from a broken app, so agents reported it as one. Reachability is now resolved by the environment, which is the only side that knows what is listening and what the tailnet routes. A new `preview.resolvePort` RPC answers with a URL it has verified: - a same-machine client keeps plain localhost; - an existing `tailscale serve` route is reused, at whatever port and scheme it was published under; - a port already answering on the environment's own address (WSL, LAN, a tunnel) uses that address and publishes nothing; - otherwise a tailnet-only HTTPS route is published, probed until it answers, and withdrawn when the dev server exits or the server shuts down. When none of those work it fails with a reason and a next action instead of a URL that cannot load. Navigation, the port list, chat links, and agent `preview_navigate`/`preview_open` all route through it; the old synchronous rewrite is kept for labels only. Two loopback-family bugs fell out of verifying this end to end. Vite's default `--host localhost` binds `::1` only, so `tailscale serve` mappings pinned to `127.0.0.1` proxy to nothing and answer 502 — `vp run dev --share` produced a URL that never worked. Both the new resolver and dev-share now target `localhost` and let tailscale pick the family. Upstream, not fork-local: both halves came from merged upstream PRs (pingdotgg#4011 rewrote the URL, pingdotgg#4556 added sharing) and the seam between them was never covered. Promote after merge.
## What's Changed * Add middle-click close for right panel tabs by @huxcrux in https://github.com/pingdotgg/t3code/pull/3161 * fix: warm WSL before preflight in WSL-only backend mode by @UtkarshUsername in https://github.com/pingdotgg/t3code/pull/3588 * Add Claude Sonnet 5 as the default Claude model by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3620 * Restore the ultrathink frame border effect by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3625 * fix(dev): Fix electron dev launch and add test by @t3dotgg in https://github.com/pingdotgg/t3code/pull/3662 * Add adaptive split-view layout for iPad/mobile workspace by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3514 * fix(mobile): compile patched native pods from source on EAS by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3667 * Make the thread composer read as elevated liquid glass by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3668 * Upgrade Vite Plus and enable bundled dev opt-in by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3679 * Surface pending tasks in mobile home and draft flow by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3670 * fix(mobile): combined test branch — scroll, back-swipe, thread lists, computer switching by @t3dotgg in https://github.com/pingdotgg/t3code/pull/3687 * Add repo-root favicon.svg so t3 code shows its own icon by @t3dotgg in https://github.com/pingdotgg/t3code/pull/3683 * Load thread snapshots over HTTP before live sync by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3719 * Fix mobile legend anchor under automatic iOS insets by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3684 * Improve live activity routing and diagnostics by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3685 * Prevent Add Project sheet from collapsing on relayout by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3759 * Use variant-specific splash icons in mobile app by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3762 * Fix Expo widget asset wiring order by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3763 * Extend Done display to 15 minutes and show up to 5 Live Activity banner rows by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3761 * Clear VCS presentation state on finish by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3764 * Lead with the outcome when no agents are active in the Live Activity by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3768 * Add T3 Connect onboarding for mobile and web by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3765 * Revert "Add T3 Connect onboarding for mobile and web" by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3776 * Expose Clerk Google sign-in env vars to Expo by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3772 * Set up Cursor Cloud dev environment (web + Android toolchain) by @t3dotgg in https://github.com/pingdotgg/t3code/pull/3755 * Revert "Revert "Add T3 Connect onboarding for mobile and web"" by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3777 * Use rounded depth logo for production splash screen by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3780 * fix(release): stage pnpm 11 allowBuilds for desktop installs by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3781 * Upgrade Clerk toolchain to latest versions by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3785 * fix(release): bump electron-builder so pnpm 11 deduped deps land in the asar by @avocardow in https://github.com/pingdotgg/t3code/pull/3790 * Fix desktop native optional dependency packaging by @Prgm-code in https://github.com/pingdotgg/t3code/pull/3816 * [codex] Upgrade Clerk stack by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3821 * [codex] Preserve worktree metadata during branch sync by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3822 * feat(client): persist offline environment data and mobile preferences by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3795 * [codex] Label max and ultra reasoning by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3824 * fix(mobile): embed fonts and render project favicons reliably by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3823 * Show compact PR number badges in mobile thread rows by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3827 * Expose mobile PR indicator labels to accessibility by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3828 * Fix truncated chat error alert layout by @jakeleventhal in https://github.com/pingdotgg/t3code/pull/3899 * fix(marketing): show platform-appropriate commit shortcut on the website by @VedankPurohit in https://github.com/pingdotgg/t3code/pull/3644 * [codex] Add Android mobile support by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3579 * Use client-side fallbacks for missing project favicons by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3959 * Skip stale working-task notifications by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3961 * Prepare Android beta branding and review diff UI by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3967 * perf(web): duty-cycle status animations and remove fixed noise overlay by @t3dotgg in https://github.com/pingdotgg/t3code/pull/3978 * fix(docs): correct CI task-runner commands in ci.md by @kridaydave in https://github.com/pingdotgg/t3code/pull/3990 * fix(docs): repair broken source links in architecture overview by @kridaydave in https://github.com/pingdotgg/t3code/pull/3991 * fix(docs): replace stale codething-mvp absolute paths with repo-relative links by @kridaydave in https://github.com/pingdotgg/t3code/pull/3992 * docs: Add T3 Code Legal Docs by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3972 * Fix Legal modal header crash by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4000 * [codex] Fix onboarding connection status by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4001 * Isolate native diff highlight grammar state by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4029 * Fix macOS fullscreen titlebar spacing by @D3OXY in https://github.com/pingdotgg/t3code/pull/4019 * Prevent duplicate project workspace roots by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3829 * Normalize over-indented markdown list items by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4020 * Resolve localhost preview URLs for remote environments by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4011 * fix(mobile): Send composer images in upload wire format by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4035 * Fix iOS terminal Enter input encoding by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4043 * Add native mobile share target support by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4021 * [codex] Expand real-route app store screenshot harness by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4014 * fix(server): use CLAUDE_CONFIG_DIR instead of HOME for Claude instanc… by @dmstoykov in https://github.com/pingdotgg/t3code/pull/4017 * Fix dropped events during initial thread snapshot by @D3OXY in https://github.com/pingdotgg/t3code/pull/4079 * feat: show nightly update changelog tooltip by @HugoVizcainoSantana in https://github.com/pingdotgg/t3code/pull/3832 * fix(git): treat selected commit paths literally by @EricTsai83 in https://github.com/pingdotgg/t3code/pull/3998 * fix(server): stabilize non-repository Git diagnostics by @EricTsai83 in https://github.com/pingdotgg/t3code/pull/4077 * Refresh app icons across release variants by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4080 * Update marketing GitHub star count by @AmoonPod in https://github.com/pingdotgg/t3code/pull/4088 * fix(marketing): correct Cursor icon color by @AmoonPod in https://github.com/pingdotgg/t3code/pull/4090 * Normalize protocol-relative remote host input as https by @kridaydave in https://github.com/pingdotgg/t3code/pull/3971 * fix(cursor): default binary path to cursor-agent (avoid path conflict w/ grok) by @BunnyGamezsc in https://github.com/pingdotgg/t3code/pull/4094 * Fix documented task-runner commands (bun run -> vp) by @kridaydave in https://github.com/pingdotgg/t3code/pull/3965 * Allow preview panel to grow on wide displays by @olivoil in https://github.com/pingdotgg/t3code/pull/4044 * fix: prevent initial right-click from selecting a context menu item by @Fazalkadivar21 in https://github.com/pingdotgg/t3code/pull/3877 * Fix duplicate keybinding rule when replacing with an existing rule by @kridaydave in https://github.com/pingdotgg/t3code/pull/3969 * fix(server): image upload crashed dispatchCommand with a stack overflow by @t3dotgg in https://github.com/pingdotgg/t3code/pull/3952 * Remove unused code parameter from describePreviewError by @kridaydave in https://github.com/pingdotgg/t3code/pull/3970 * [codex] prevent ACP assistant ID collisions after restarts by @maxwellyoung in https://github.com/pingdotgg/t3code/pull/3932 * fix(web): inset Windows desktop scrollbars from resize edge by @nateEc in https://github.com/pingdotgg/t3code/pull/4097 * [codex] fix mobile composer Enter behavior by @maxwellyoung in https://github.com/pingdotgg/t3code/pull/3930 * feat(server): include runtime model and effort in Codex developer instructions by @t3dotgg in https://github.com/pingdotgg/t3code/pull/3948 * fix(ux): spamming cmd + , no longer stack opening settings by @jamesx0416 in https://github.com/pingdotgg/t3code/pull/2757 * fix(terminal): strip AppImage runtime env from spawned terminals by @leorivastech in https://github.com/pingdotgg/t3code/pull/3108 * fix(server): thread cwd through Claude capability probe (#2048) by @mvanhorn in https://github.com/pingdotgg/t3code/pull/2124 * [codex] fix: guard invalid web timestamps by @StiensWout in https://github.com/pingdotgg/t3code/pull/3515 * [codex] fix: tolerate invalid latest user message timestamps by @StiensWout in https://github.com/pingdotgg/t3code/pull/3521 * [codex] Fix provider update checks restore defaults by @StiensWout in https://github.com/pingdotgg/t3code/pull/3531 * fix(server): skip undecodable provider runtime rows when listing sessions by @t3dotgg in https://github.com/pingdotgg/t3code/pull/3951 * Share MCP OAuth locks across Codex shadow homes by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4104 * Preserve T3 Code identity in macOS development launcher by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4102 * fix(web): increase contrast of question option descriptions by @xxashxx-svg in https://github.com/pingdotgg/t3code/pull/3867 * feat: draft hero landing on the index route by @yordis in https://github.com/pingdotgg/t3code/pull/4055 * feat: file explorer mention actions and zoom-aware context menus by @yordis in https://github.com/pingdotgg/t3code/pull/4054 * fix(mobile): restore iOS home screen branding by @PixPMusic in https://github.com/pingdotgg/t3code/pull/4025 * perf(client): defer active thread cache writes by @Chrrxs in https://github.com/pingdotgg/t3code/pull/4006 * Default diffs to working changes by @jakeleventhal in https://github.com/pingdotgg/t3code/pull/3974 * Add Grok to marketing site provider list by @Aditya190803 in https://github.com/pingdotgg/t3code/pull/3484 * Fix reopening existing Diff tab by @jakeleventhal in https://github.com/pingdotgg/t3code/pull/3973 * Fix sending messages during active turns by @jakeleventhal in https://github.com/pingdotgg/t3code/pull/3919 * [codex] Route OpenCode missing-session errors through Effect by @StiensWout in https://github.com/pingdotgg/t3code/pull/3608 * [fix/feat:ui] Show default option badge by @sandersonstabo in https://github.com/pingdotgg/t3code/pull/3232 * [fix/feat:ui] Preserve open-in editor brand colors by @sandersonstabo in https://github.com/pingdotgg/t3code/pull/3225 * fix(web): handle macOS Home and End in composer by @GuilhermeVieiraDev in https://github.com/pingdotgg/t3code/pull/2508 * Allow failed remote environments to be removed by @zepi2509 in https://github.com/pingdotgg/t3code/pull/4084 * [codex] canonicalize client timestamps by @maxwellyoung in https://github.com/pingdotgg/t3code/pull/4112 * [fix/feat:ui] Make selected menu checks blue by @sandersonstabo in https://github.com/pingdotgg/t3code/pull/3234 * fix(desktop): Validate WSL node version against engine range after probe success by @UtkarshUsername in https://github.com/pingdotgg/t3code/pull/3621 * Refresh splash screen and favicon branding by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4120 * Add terminal selection copy action by @tarik02 in https://github.com/pingdotgg/t3code/pull/2904 * Add isolated app testing workflow by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4121 * feat(web): themed sidebar header art for nightly and dev builds by @maria-rcks in https://github.com/pingdotgg/t3code/pull/4130 * feat: add headless `t3 connect` setup for SSH hosts by @t3dotgg in https://github.com/pingdotgg/t3code/pull/3749 * Refine T3 Connect authorization surfaces by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4159 * fix: increase OpenCode server startup timeout from 5s to 30s by @UtkarshUsername in https://github.com/pingdotgg/t3code/pull/4132 * fix(shared): delete unused agentAwareness phase predicates by @kridaydave in https://github.com/pingdotgg/t3code/pull/4134 * fix(mobile): Stabilize native stack option updates by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4037 * Make test-t3-app skill discoverable by Claude Code by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4162 * fix(web): improve dev sidebar backdrop contrast & remove version pills by @maria-rcks in https://github.com/pingdotgg/t3code/pull/4166 * Fix draft banner stack overlap by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4164 * Add portable mobile app testing guidance by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4165 * fix(client): use lightweight connection probe by @eeinarsson in https://github.com/pingdotgg/t3code/pull/4137 * fix(server): resolve Claude SDK executable path on Windows npm installs by @nsxdavid in https://github.com/pingdotgg/t3code/pull/3740 * Fix project action preview settings persistence by @keeperxy in https://github.com/pingdotgg/t3code/pull/3842 * fix(desktop): allow clipboard writes in the preview browser by @carlosricojr in https://github.com/pingdotgg/t3code/pull/3889 * fix(web): handle sidebar shortcut before editors by @Bortlesboat in https://github.com/pingdotgg/t3code/pull/3921 * fix(server): recognize Bedrock-backed Claude as authenticated by @PieterVanZyl-Dev in https://github.com/pingdotgg/t3code/pull/3931 * Fix incorrect pluralization of “entry” by @McMelonTV in https://github.com/pingdotgg/t3code/pull/3933 * feat(server): title background-task work-log rows with the task name by @t3dotgg in https://github.com/pingdotgg/t3code/pull/3751 * fix: delegate OpenCode session titles to provider by @tris203 in https://github.com/pingdotgg/t3code/pull/3720 * Archive selected threads from the context menu by @theduke in https://github.com/pingdotgg/t3code/pull/3895 * fix(cli): support force removing projects by @Bortlesboat in https://github.com/pingdotgg/t3code/pull/3922 * fix: allow sidebar to be shrunk when wider than viewport by @shoaib050326 in https://github.com/pingdotgg/t3code/pull/2456 * fix(codex): show web search query and url in tool call details by @GuilhermeVieiraDev in https://github.com/pingdotgg/t3code/pull/2093 * Add Codex launch arguments setting by @jamesx0416 in https://github.com/pingdotgg/t3code/pull/2892 * [orchestration] Clear stale active turn when session becomes inactive by @Andrew-Forster in https://github.com/pingdotgg/t3code/pull/3159 * Regenerate Codex reset credit protocol bindings by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4173 * fix(preview): preserve direct localhost navigation by @Chrrxs in https://github.com/pingdotgg/t3code/pull/3939 * Synchronize mobile threads with authoritative shell snapshots by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4163 * Gate iOS glass layout on native support by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4032 * fix(opencode): resume the OpenCode session on follow-ups instead of starting an empty one by @vdmkotai in https://github.com/pingdotgg/t3code/pull/3617 * fix(server): use CLI for OpenCode health check instead of spawning server by @UtkarshUsername in https://github.com/pingdotgg/t3code/pull/4153 * fix(web): scope timeline minimap hover target to the side gutter by @xxashxx-svg in https://github.com/pingdotgg/t3code/pull/3869 * [codex] show complete approval details by @maxwellyoung in https://github.com/pingdotgg/t3code/pull/4111 * fix(web): paint text selection over composer chips by @yordis in https://github.com/pingdotgg/t3code/pull/4139 * [codex] preserve custom model slugs by @maxwellyoung in https://github.com/pingdotgg/t3code/pull/4168 * fix(web): preview workspace images in the file panel by @Rhiz3K in https://github.com/pingdotgg/t3code/pull/3996 * feat(web): drag files from the explorer into the chat composer by @yordis in https://github.com/pingdotgg/t3code/pull/4140 * fix(desktop): preserve main window bounds by @anirudhsama in https://github.com/pingdotgg/t3code/pull/3851 * perf(orchestration): speed up new-chat propagation and offline catch-up by @RusiruSadathana in https://github.com/pingdotgg/t3code/pull/4177 * Finale: upgrade changed files card to fix various UI issues by @sandersonstabo in https://github.com/pingdotgg/t3code/pull/4113 * Pass CLI OAuth config to hosted web deploy by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4186 * fix(web): always show environment chip for remote projects by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4217 * fix(web): keep composer editable while disconnected by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4241 * fix: better defaults — Claude 1M context, Codex gpt-5.6, worktrees from origin main by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4240 * fix(claude): handle all SDK stream messages; stop spurious work-log warning rows by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4244 * Sidebar v2 beta: flat thread list with a server-backed settled lifecycle by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4026 * fix(settings): validate the add-provider wizard step before advancing (#2813) by @leorivastech in https://github.com/pingdotgg/t3code/pull/3100 * fix(claude): isolate capability probe from user MCP servers by @jbbottoms in https://github.com/pingdotgg/t3code/pull/4015 * Preserve connecting status while a turn starts by @D3OXY in https://github.com/pingdotgg/t3code/pull/4101 * fix(server): stop restoring stale OpenCode models by @nateEc in https://github.com/pingdotgg/t3code/pull/4095 * [codex] keep scoped package references as text by @maxwellyoung in https://github.com/pingdotgg/t3code/pull/4167 * fix(web): default provider selection for users without Codex by @mfazekas in https://github.com/pingdotgg/t3code/pull/4117 * Unify temporary worktree branch naming by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4278 * fix(web): use message-square icon for settled icon-less project threads in sidebar v2 by @UtkarshUsername in https://github.com/pingdotgg/t3code/pull/4279 * Stabilize sidebar settling animations by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4280 * Restore Copy Link in chat link context menu by @caezium in https://github.com/pingdotgg/t3code/pull/4161 * fix(desktop): handle EPIPE errors on stdout/stderr to prevent crash dialog by @UtkarshUsername in https://github.com/pingdotgg/t3code/pull/4213 * Preserve draft thread highlighting during promotion by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4283 * Move mobile working timer into the thread timeline by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4285 * Stabilize PR status lookups and provider session lifecycle by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4281 * fix: open command palette instead of custom dialog for new thread picker in SidebarV2 by @UtkarshUsername in https://github.com/pingdotgg/t3code/pull/4269 * fix(server): don't drop sticky PR fallback when remote URL can't be resolved by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4289 * feat(web): copy branch name via right-click in the branch selector by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4275 * Add remote server updates and standalone service management by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4286 * Refine light-mode sidebar surfaces by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4268 * fix(mobile): don't mark Android VPN/Tailscale as offline when connected by @Wraient in https://github.com/pingdotgg/t3code/pull/3949 * improve and prevent silent thread branch drift and PR fetching by @justsomelegs in https://github.com/pingdotgg/t3code/pull/2284 * Refresh web application surfaces and dark-mode dialogs by @maria-rcks in https://github.com/pingdotgg/t3code/pull/4319 * fix(web): new-thread defaults ignored for remote environments by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4276 * feat: add "Auto" runtime mode — AI-reviewed approvals for Codex and Claude by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4272 * Add shared t3.json project configuration support by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4317 * Unify dialog glass and fix composer overlays by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4365 * fix(web): warn before silent Windows updates by @nateEc in https://github.com/pingdotgg/t3code/pull/4350 * [codex] Move project grouping to General settings by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4313 * [codex] Group project scopes in mobile thread lists by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4314 * [codex] Move mobile project grouping to General settings by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4315 * [codex] Deduplicate connection failure messaging by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4367 * Restore grouped project filtering in Sidebar V2 by @shivamhwp in https://github.com/pingdotgg/t3code/pull/4282 * [codex] restore Sidebar V2 project actions by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4373 * [codex] Group projects in new-thread pickers by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4312 * fix(web): restore dark composer toolbar styling by @maria-rcks in https://github.com/pingdotgg/t3code/pull/4375 * Fix thread tooltip folder icon color by @maria-rcks in https://github.com/pingdotgg/t3code/pull/4383 * fix(server): parse CLI version in update preflight by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4389 * fix(web): sidebar v2 polish — jump hints, working duration, in-flight fade, settled sort by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4274 * Fix logical project grouping labels on mobile by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4391 * Add preview color scheme controls and simplify project grouping by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4385 * fix(cli): publish nightly branded favicons by @maria-rcks in https://github.com/pingdotgg/t3code/pull/4372 * Fix thread loading flash by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4396 * fix(client-runtime): keep a warm thread un-settled despite a merged/closed PR by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4309 * Fix composer context strip alignment and glass shell by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4404 * Polish iOS git progress overlay with glass effects by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4387 * Improve composer glass fallbacks by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4406 * feat(web): collapse large git diffs by default to make chat more readable by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4409 * Stop new threads inheriting checkout/branch from viewed thread by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4411 * fix: tone down branch-mismatch banner by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4416 * fix: Claude Code skills discoverable for the composer $ picker by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4414 * fix(web): keep settled threads reachable when opened directly by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4413 * feat(sidebar-v2): thread snoozing by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4311 * Upgrade Clerk packages and Expo integration by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4440 * Increase light-mode contrast for user message bubbles by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4441 * Restore model picker layout and retain iterative test state by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4450 * Color settled PR labels on hover by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4451 * [codex] Fix glass hover compositing artifacts by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4446 * Add Claude Opus 5 model by @thomaslittle in https://github.com/pingdotgg/t3code/pull/4472 * feat(web): add collapse-all toggle to diff panel by @0x4bs3nt in https://github.com/pingdotgg/t3code/pull/4475 * feat(web): show fast mode as a bolt instead of a "Normal" label by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4488 * feat(dev): keep worktree dev state isolated on T3 Code dev servers by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4555 * feat(dev): Make t3 code dev instances shareable over Tailscale by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4556 * fix(dev): skip browser-blocked ports by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4608 * fix: cut websocket throughput in half by pruning activity payloads by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4622 * perf(mobile): defer work-log detail serialization by @saphid in https://github.com/pingdotgg/t3code/pull/4607 * test: account for lazy thread feed details by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4628 * feat(relay): limit managed tunnels per user by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4530 * Add managed tunnel limits migration by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4635 * Add background preview capture and picture-in-picture support by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4397 * feat(web): prompt stash — cmd+S saves the composer to a per-provider queue by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4453 * [codex] Upgrade Effect and Alchemy betas by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4643 * feat: allow new thread creation through project breadcrumbs by @0x4bs3nt in https://github.com/pingdotgg/t3code/pull/4638 * fix(web): scope PR state to the thread branch by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4460 * Drop redundant Relay user indexes by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4648 * feat(connect): release the Cloudflare tunnel when the environment shuts down by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4531 * Fix Relay Worker RuntimeContext wiring by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4653 * Fix live sidebar resize limits and defer Alchemy runtime context by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4655 * fix(web): constrain branch toolbar context by @maxktz in https://github.com/pingdotgg/t3code/pull/4657 * Keep MCP credentials alive across provider turns by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4659 * fix: close actions dropdown when editing by @0x4bs3nt in https://github.com/pingdotgg/t3code/pull/4660 * fix(preview): stabilize PiP viewport identity by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4661 * Add glass styling for thread tooltips and simplify preview tab handling by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4665 * Use tarball archiving for hosted web deploys by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4669 * fix(server): bound editor discovery during config loading by @maria-rcks in https://github.com/pingdotgg/t3code/pull/4291 * Prevent draft thread detail polling before shell registration by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4670 * feat: add configurable source control writing settings by @maria-rcks in https://github.com/pingdotgg/t3code/pull/4204 * feat(diff-panel): show total line additions and deletions by @0x4bs3nt in https://github.com/pingdotgg/t3code/pull/4674 * Clear provider update actions while updating by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4676 * Fix sidebar highlighting for draft threads by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4679 * Use glass surfaces for web toasts by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4681 * Show origin ref in branch trigger label by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4680 * fix(mobile): match react version to react-native 0.85.3 vendored renderer (19.2.3) by @KrzysztofMoch in https://github.com/pingdotgg/t3code/pull/4675 ## New Contributors * @avocardow made their first contribution in https://github.com/pingdotgg/t3code/pull/3790 * @Prgm-code made their first contribution in https://github.com/pingdotgg/t3code/pull/3816 * @jakeleventhal made their first contribution in https://github.com/pingdotgg/t3code/pull/3899 * @VedankPurohit made their first contribution in https://github.com/pingdotgg/t3code/pull/3644 * @kridaydave made their first contribution in https://github.com/pingdotgg/t3code/pull/3990 * @dmstoykov made their first contribution in https://github.com/pingdotgg/t3code/pull/4017 * @HugoVizcainoSantana made their first contribution in https://github.com/pingdotgg/t3code/pull/3832 * @EricTsai83 made their first contribution in https://github.com/pingdotgg/t3code/pull/3998 * @AmoonPod made their first contribution in https://github.com/pingdotgg/t3code/pull/4088 * @BunnyGamezsc made their first contribution in https://github.com/pingdotgg/t3code/pull/4094 * @olivoil made their first contribution in https://github.com/pingdotgg/t3code/pull/4044 * @Fazalkadivar21 made their first contribution in https://github.com/pingdotgg/t3code/pull/3877 * @maxwellyoung made their first contribution in https://github.com/pingdotgg/t3code/pull/3932 * @nateEc made their first contribution in https://github.com/pingdotgg/t3code/pull/4097 * @leorivastech made their first contribution in https://github.com/pingdotgg/t3code/pull/3108 * @xxashxx-svg made their first contribution in https://github.com/pingdotgg/t3code/pull/3867 * @yordis made their first contribution in https://github.com/pingdotgg/t3code/pull/4055 * @Chrrxs made their first contribution in https://github.com/pingdotgg/t3code/pull/4006 * @Aditya190803 made their first contribution in https://github.com/pingdotgg/t3code/pull/3484 * @zepi2509 made their first contribution in https://github.com/pingdotgg/t3code/pull/4084 * @eeinarsson made their first contribution in https://github.com/pingdotgg/t3code/pull/4137 * @keeperxy made their first contribution in https://github.com/pingdotgg/t3code/pull/3842 * @carlosricojr made their first contribution in https://github.com/pingdotgg/t3code/pull/3889 * @Bortlesboat made their first contribution in https://github.com/pingdotgg/t3code/pull/3921 * @PieterVanZyl-Dev made their first contribution in https://github.com/pingdotgg/t3code/pull/3931 * @McMelonTV made their first contribution in https://github.com/pingdotgg/t3code/pull/3933 * @tris203 made their first contribution in https://github.com/pingdotgg/t3code/pull/3720 * @theduke made their first contribution in https://github.com/pingdotgg/t3code/pull/3895 * @shoaib050326 made their first contribution in https://github.com/pingdotgg/t3code/pull/2456 * @vdmkotai made their first contribution in https://github.com/pingdotgg/t3code/pull/3617 * @Rhiz3K made their first contribution in https://github.com/pingdotgg/t3code/pull/3996 * @anirudhsama made their first contribution in https://github.com/pingdotgg/t3code/pull/3851 * @RusiruSadathana made their first contribution in https://github.com/pingdotgg/t3code/pull/4177 * @jbbottoms made their first contribution in https://github.com/pingdotgg/t3code/pull/4015 * @mfazekas made their first contribution in https://github.com/pingdotgg/t3code/pull/4117 * @caezium made their first contribution in https://github.com/pingdotgg/t3code/pull/4161 * @Wraient made their first contribution in https://github.com/pingdotgg/t3code/pull/3949 * @thomaslittle made their first contribution in https://github.com/pingdotgg/t3code/pull/4472 * @0x4bs3nt made their first contribution in https://github.com/pingdotgg/t3code/pull/4475 * @saphid made their first contribution in https://github.com/pingdotgg/t3code/pull/4607 * @maxktz made their first contribution in https://github.com/pingdotgg/t3code/pull/4657 * @KrzysztofMoch made their first contribution in https://github.com/pingdotgg/t3code/pull/4675 **Full Changelog**: https://github.com/pingdotgg/t3code/compare/v0.0.28...v0.0.29 ## What's Changed * Add middle-click close for right panel tabs by @huxcrux in https://github.com/pingdotgg/t3code/pull/3161 * fix: warm WSL before preflight in WSL-only backend mode by @UtkarshUsername in https://github.com/pingdotgg/t3code/pull/3588 * Add Claude Sonnet 5 as the default Claude model by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3620 * Restore the ultrathink frame border effect by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3625 * fix(dev): Fix electron dev launch and add test by @t3dotgg in https://github.com/pingdotgg/t3code/pull/3662 * Add adaptive split-view layout for iPad/mobile workspace by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3514 * fix(mobile): compile patched native pods from source on EAS by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3667 * Make the thread composer read as elevated liquid glass by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3668 * Upgrade Vite Plus and enable bundled dev opt-in by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3679 * Surface pending tasks in mobile home and draft flow by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3670 * fix(mobile): combined test branch — scroll, back-swipe, thread lists, computer switching by @t3dotgg in https://github.com/pingdotgg/t3code/pull/3687 * Add repo-root favicon.svg so t3 code shows its own icon by @t3dotgg in https://github.com/pingdotgg/t3code/pull/3683 * Load thread snapshots over HTTP before live sync by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3719 * Fix mobile legend anchor under automatic iOS insets by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3684 * Improve live activity routing and diagnostics by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3685 * Prevent Add Project sheet from collapsing on relayout by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3759 * Use variant-specific splash icons in mobile app by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3762 * Fix Expo widget asset wiring order by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3763 * Extend Done display to 15 minutes and show up to 5 Live Activity banner rows by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3761 * Clear VCS presentation state on finish by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3764 * Lead with the outcome when no agents are active in the Live Activity by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3768 * Add T3 Connect onboarding for mobile and web by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3765 * Revert "Add T3 Connect onboarding for mobile and web" by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3776 * Expose Clerk Google sign-in env vars to Expo by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3772 * Set up Cursor Cloud dev environment (web + Android toolchain) by @t3dotgg in https://github.com/pingdotgg/t3code/pull/3755 * Revert "Revert "Add T3 Connect onboarding for mobile and web"" by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3777 * Use rounded depth logo for production splash screen by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3780 * fix(release): stage pnpm 11 allowBuilds for desktop installs by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3781 * Upgrade Clerk toolchain to latest versions by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3785 * fix(release): bump electron-builder so pnpm 11 deduped deps land in the asar by @avocardow in https://github.com/pingdotgg/t3code/pull/3790 * Fix desktop native optional dependency packaging by @Prgm-code in https://github.com/pingdotgg/t3code/pull/3816 * [codex] Upgrade Clerk stack by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3821 * [codex] Preserve worktree metadata during branch sync by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3822 * feat(client): persist offline environment data and mobile preferences by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3795 * [codex] Label max and ultra reasoning by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3824 * fix(mobile): embed fonts and render project favicons reliably by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3823 * Show compact PR number badges in mobile thread rows by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3827 * Expose mobile PR indicator labels to accessibility by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3828 * Fix truncated chat error alert layout by @jakeleventhal in https://github.com/pingdotgg/t3code/pull/3899 * fix(marketing): show platform-appropriate commit shortcut on the website by @VedankPurohit in https://github.com/pingdotgg/t3code/pull/3644 * [codex] Add Android mobile support by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3579 * Use client-side fallbacks for missing project favicons by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3959 * Skip stale working-task notifications by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3961 * Prepare Android beta branding and review diff UI by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3967 * perf(web): duty-cycle status animations and remove fixed noise overlay by @t3dotgg in https://github.com/pingdotgg/t3code/pull/3978 * fix(docs): correct CI task-runner commands in ci.md by @kridaydave in https://github.com/pingdotgg/t3code/pull/3990 * fix(docs): repair broken source links in architecture overview by @kridaydave in https://github.com/pingdotgg/t3code/pull/3991 * fix(docs): replace stale codething-mvp absolute paths with repo-relative links by @kridaydave in https://github.com/pingdotgg/t3code/pull/3992 * docs: Add T3 Code Legal Docs by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3972 * Fix Legal modal header crash by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4000 * [codex] Fix onboarding connection status by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4001 * Isolate native diff highlight grammar state by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4029 * Fix macOS fullscreen titlebar spacing by @D3OXY in https://github.com/pingdotgg/t3code/pull/4019 * Prevent duplicate project workspace roots by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3829 * Normalize over-indented markdown list items by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4020 * Resolve localhost preview URLs for remote environments by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4011 * fix(mobile): Send composer images in upload wire format by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4035 * Fix iOS terminal Enter input encoding by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4043 * Add native mobile share target support by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4021 * [codex] Expand real-route app store screenshot harness by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4014 * fix(server): use CLAUDE_CONFIG_DIR instead of HOME for Claude instanc… by @dmstoykov in https://github.com/pingdotgg/t3code/pull/4017 * Fix dropped events during initial thread snapshot by @D3OXY in https://github.com/pingdotgg/t3code/pull/4079 * feat: show nightly update changelog tooltip by @HugoVizcainoSantana in https://github.com/pingdotgg/t3code/pull/3832 * fix(git): treat selected commit paths literally by @EricTsai83 in https://github.com/pingdotgg/t3code/pull/3998 * fix(server): stabilize non-repository Git diagnostics by @EricTsai83 in https://github.com/pingdotgg/t3code/pull/4077 * Refresh app icons across release variants by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4080 * Update marketing GitHub star count by @AmoonPod in https://github.com/pingdotgg/t3code/pull/4088 * fix(marketing): correct Cursor icon color by @AmoonPod in https://github.com/pingdotgg/t3code/pull/4090 * Normalize protocol-relative remote host input as https by @kridaydave in https://github.com/pingdotgg/t3code/pull/3971 * fix(cursor): default binary path to cursor-agent (avoid path conflict w/ grok) by @BunnyGamezsc in https://github.com/pingdotgg/t3code/pull/4094 * Fix documented task-runner commands (bun run -> vp) by @kridaydave in https://github.com/pingdotgg/t3code/pull/3965 * Allow preview panel to grow on wide displays by @olivoil in https://github.com/pingdotgg/t3code/pull/4044 * fix: prevent initial right-click from selecting a context menu item by @Fazalkadivar21 in https://github.com/pingdotgg/t3code/pull/3877 * Fix duplicate keybinding rule when replacing with an existing rule by @kridaydave in https://github.com/pingdotgg/t3code/pull/3969 * fix(server): image upload crashed dispatchCommand with a stack overflow by @t3dotgg in https://github.com/pingdotgg/t3code/pull/3952 * Remove unused code parameter from describePreviewError by @kridaydave in https://github.com/pingdotgg/t3code/pull/3970 * [codex] prevent ACP assistant ID collisions after restarts by @maxwellyoung in https://github.com/pingdotgg/t3code/pull/3932 * fix(web): inset Windows desktop scrollbars from resize edge by @nateEc in https://github.com/pingdotgg/t3code/pull/4097 * [codex] fix mobile composer Enter behavior by @maxwellyoung in https://github.com/pingdotgg/t3code/pull/3930 * feat(server): include runtime model and effort in Codex developer instructions by @t3dotgg in https://github.com/pingdotgg/t3code/pull/3948 * fix(ux): spamming cmd + , no longer stack opening settings by @jamesx0416 in https://github.com/pingdotgg/t3code/pull/2757 * fix(terminal): strip AppImage runtime env from spawned terminals by @leorivastech in https://github.com/pingdotgg/t3code/pull/3108 * fix(server): thread cwd through Claude capability probe (#2048) by @mvanhorn in https://github.com/pingdotgg/t3code/pull/2124 * [codex] fix: guard invalid web timestamps by @StiensWout in https://github.com/pingdotgg/t3code/pull/3515 * [codex] fix: tolerate invalid latest user message timestamps by @StiensWout in https://github.com/pingdotgg/t3code/pull/3521 * [codex] Fix provider update checks restore defaults by @StiensWout in https://github.com/pingdotgg/t3code/pull/3531 * fix(server): skip undecodable provider runtime rows when listing sessions by @t3dotgg in https://github.com/pingdotgg/t3code/pull/3951 * Share MCP OAuth locks across Codex shadow homes by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4104 * Preserve T3 Code identity in macOS development launcher by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4102 * fix(web): increase contrast of question option descriptions by @xxashxx-svg in https://github.com/pingdotgg/t3code/pull/3867 * feat: draft hero landing on the index route by @yordis in https://github.com/pingdotgg/t3code/pull/4055 * feat: file explorer mention actions and zoom-aware context menus by @yordis in https://github.com/pingdotgg/t3code/pull/4054 * fix(mobile): restore iOS home screen branding by @PixPMusic in https://github.com/pingdotgg/t3code/pull/4025 * perf(client): defer active thread cache writes by @Chrrxs in https://github.com/pingdotgg/t3code/pull/4006 * Default diffs to working changes by @jakeleventhal in https://github.com/pingdotgg/t3code/pull/3974 * Add Grok to marketing site provider list by @Aditya190803 in https://github.com/pingdotgg/t3code/pull/3484 * Fix reopening existing Diff tab by @jakeleventhal in https://github.com/pingdotgg/t3code/pull/3973 * Fix sending messages during active turns by @jakeleventhal in https://github.com/pingdotgg/t3code/pull/3919 * [codex] Route OpenCode missing-session errors through Effect by @StiensWout in https://github.com/pingdotgg/t3code/pull/3608 * [fix/feat:ui] Show default option badge by @sandersonstabo in https://github.com/pingdotgg/t3code/pull/3232 * [fix/feat:ui] Preserve open-in editor brand colors by @sandersonstabo in https://github.com/pingdotgg/t3code/pull/3225 * fix(web): handle macOS Home and End in composer by @GuilhermeVieiraDev in https://github.com/pingdotgg/t3code/pull/2508 * Allow failed remote environments to be removed by @zepi2509 in https://github.com/pingdotgg/t3code/pull/4084 * [codex] canonicalize client timestamps by @maxwellyoung in https://github.com/pingdotgg/t3code/pull/4112 * [fix/feat:ui] Make selected menu checks blue by @sandersonstabo in https://github.com/pingdotgg/t3code/pull/3234 * fix(desktop): Validate WSL node version against engine range after probe success by @UtkarshUsername in https://github.com/pingdotgg/t3code/pull/3621 * Refresh splash screen and favicon branding by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4120 * Add terminal selection copy action by @tarik02 in https://github.com/pingdotgg/t3code/pull/2904 * Add isolated app testing workflow by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4121 * feat(web): themed sidebar header art for nightly and dev builds by @maria-rcks in https://github.com/pingdotgg/t3code/pull/4130 * feat: add headless `t3 connect` setup for SSH hosts by @t3dotgg in https://github.com/pingdotgg/t3code/pull/3749 * Refine T3 Connect authorization surfaces by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4159 * fix: increase OpenCode server startup timeout from 5s to 30s by @UtkarshUsername in https://github.com/pingdotgg/t3code/pull/4132 * fix(shared): delete unused agentAwareness phase predicates by @kridaydave in https://github.com/pingdotgg/t3code/pull/4134 * fix(mobile): Stabilize native stack option updates by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4037 * Make test-t3-app skill discoverable by Claude Code by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4162 * fix(web): improve dev sidebar backdrop contrast & remove version pills by @maria-rcks in https://github.com/pingdotgg/t3code/pull/4166 * Fix draft banner stack overlap by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4164 * Add portable mobile app testing guidance by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4165 * fix(client): use lightweight connection probe by @eeinarsson in https://github.com/pingdotgg/t3code/pull/4137 * fix(server): resolve Claude SDK executable path on Windows npm installs by @nsxdavid in https://github.com/pingdotgg/t3code/pull/3740 * Fix project action preview settings persistence by @keeperxy in https://github.com/pingdotgg/t3code/pull/3842 * fix(desktop): allow clipboard writes in the preview browser by @carlosricojr in https://github.com/pingdotgg/t3code/pull/3889 * fix(web): handle sidebar shortcut before editors by @Bortlesboat in https://github.com/pingdotgg/t3code/pull/3921 * fix(server): recognize Bedrock-backed Claude as authenticated by @PieterVanZyl-Dev in https://github.com/pingdotgg/t3code/pull/3931 * Fix incorrect pluralization of “entry” by @McMelonTV in https://github.com/pingdotgg/t3code/pull/3933 * feat(server): title background-task work-log rows with the task name by @t3dotgg in https://github.com/pingdotgg/t3code/pull/3751 * fix: delegate OpenCode session titles to provider by @tris203 in https://github.com/pingdotgg/t3code/pull/3720 * Archive selected threads from the context menu by @theduke in https://github.com/pingdotgg/t3code/pull/3895 * fix(cli): support force removing projects by @Bortlesboat in https://github.com/pingdotgg/t3code/pull/3922 * fix: allow sidebar to be shrunk when wider than viewport by @shoaib050326 in https://github.com/pingdotgg/t3code/pull/2456 * fix(codex): show web search query and url in tool call details by @GuilhermeVieiraDev in https://github.com/pingdotgg/t3code/pull/2093 * Add Codex launch arguments setting by @jamesx0416 in https://github.com/pingdotgg/t3code/pull/2892 * [orchestration] Clear stale active turn when session becomes inactive by @Andrew-Forster in https://github.com/pingdotgg/t3code/pull/3159 * Regenerate Codex reset credit protocol bindings by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4173 * fix(preview): preserve direct localhost navigation by @Chrrxs in https://github.com/pingdotgg/t3code/pull/3939 * Synchronize mobile threads with authoritative shell snapshots by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4163 * Gate iOS glass layout on native support by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4032 * fix(opencode): resume the OpenCode session on follow-ups instead of starting an empty one by @vdmkotai in https://github.com/pingdotgg/t3code/pull/3617 * fix(server): use CLI for OpenCode health check instead of spawning server by @UtkarshUsername in https://github.com/pingdotgg/t3code/pull/4153 * fix(web): scope timeline minimap hover target to the side gutter by @xxashxx-svg in https://github.com/pingdotgg/t3code/pull/3869 * [codex] show complete approval details by @maxwellyoung in https://github.com/pingdotgg/t3code/pull/4111 * fix(web): paint text selection over composer chips by @yordis in https://github.com/pingdotgg/t3code/pull/4139 * [codex] preserve custom model slugs by @maxwellyoung in https://github.com/pingdotgg/t3code/pull/4168 * fix(web): preview workspace images in the file panel by @Rhiz3K in https://github.com/pingdotgg/t3code/pull/3996 * feat(web): drag files from the explorer into the chat composer by @yordis in https://github.com/pingdotgg/t3code/pull/4140 * fix(desktop): preserve main window bounds by @anirudhsama in https://github.com/pingdotgg/t3code/pull/3851 * perf(orchestration): speed up new-chat propagation and offline catch-up by @RusiruSadathana in https://github.com/pingdotgg/t3code/pull/4177 * Finale: upgrade changed files card to fix various UI issues by @sandersonstabo in https://github.com/pingdotgg/t3code/pull/4113 * Pass CLI OAuth config to hosted web deploy by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4186 * fix(web): always show environment chip for remote projects by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4217 * fix(web): keep composer editable while disconnected by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4241 * fix: better defaults — Claude 1M context, Codex gpt-5.6, worktrees from origin main by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4240 * fix(claude): handle all SDK stream messages; stop spurious work-log warning rows by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4244 * Sidebar v2 beta: flat thread list with a server-backed settled lifecycle by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4026 * fix(settings): validate the add-provider wizard step before advancing (#2813) by @leorivastech in https://github.com/pingdotgg/t3code/pull/3100 * fix(claude): isolate capability probe from user MCP servers by @jbbottoms in https://github.com/pingdotgg/t3code/pull/4015 * Preserve connecting status while a turn starts by @D3OXY in https://github.com/pingdotgg/t3code/pull/4101 * fix(server): stop restoring stale OpenCode models by @nateEc in https://github.com/pingdotgg/t3code/pull/4095 * [codex] keep scoped package references as text by @maxwellyoung in https://github.com/pingdotgg/t3code/pull/4167 * fix(web): default provider selection for users without Codex by @mfazekas in https://github.com/pingdotgg/t3code/pull/4117 * Unify temporary worktree branch naming by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4278 * fix(web): use message-square icon for settled icon-less project threads in sidebar v2 by @UtkarshUsername in https://github.com/pingdotgg/t3code/pull/4279 * Stabilize sidebar settling animations by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4280 * Restore Copy Link in chat link context menu by @caezium in https://github.com/pingdotgg/t3code/pull/4161 * fix(desktop): handle EPIPE errors on stdout/stderr to prevent crash dialog by @UtkarshUsername in https://github.com/pingdotgg/t3code/pull/4213 * Preserve draft thread highlighting during promotion by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4283 * Move mobile working timer into the thread timeline by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4285 * Stabilize PR status lookups and provider session lifecycle by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4281 * fix: open command palette instead of custom dialog for new thread picker in SidebarV2 by @UtkarshUsername in https://github.com/pingdotgg/t3code/pull/4269 * fix(server): don't drop sticky PR fallback when remote URL can't be resolved by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4289 * feat(web): copy branch name via right-click in the branch selector by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4275 * Add remote server updates and standalone service management by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4286 * Refine light-mode sidebar surfaces by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4268 * fix(mobile): don't mark Android VPN/Tailscale as offline when connected by @Wraient in https://github.com/pingdotgg/t3code/pull/3949 * improve and prevent silent thread branch drift and PR fetching by @justsomelegs in https://github.com/pingdotgg/t3code/pull/2284 * Refresh web application surfaces and dark-mode dialogs by @maria-rcks in https://github.com/pingdotgg/t3code/pull/4319 * fix(web): new-thread defaults ignored for remote environments by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4276 * feat: add "Auto" runtime mode — AI-reviewed approvals for Codex and Claude by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4272 * Add shared t3.json project configuration support by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4317 * Unify dialog glass and fix composer overlays by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4365 * fix(web): warn before silent Windows updates by @nateEc in https://github.com/pingdotgg/t3code/pull/4350 * [codex] Move project grouping to General settings by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4313 * [codex] Group project scopes in mobile thread lists by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4314 * [codex] Move mobile project grouping to General settings by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4315 * [codex] Deduplicate connection failure messaging by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4367 * Restore grouped project filtering in Sidebar V2 by @shivamhwp in https://github.com/pingdotgg/t3code/pull/4282 * [codex] restore Sidebar V2 project actions by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4373 * [codex] Group projects in new-thread pickers by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4312 * fix(web): restore dark composer toolbar styling by @maria-rcks in https://github.com/pingdotgg/t3code/pull/4375 * Fix thread tooltip folder icon color by @maria-rcks in https://github.com/pingdotgg/t3code/pull/4383 * fix(server): parse CLI version in update preflight by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4389 * fix(web): sidebar v2 polish — jump hints, working duration, in-flight fade, settled sort by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4274 * Fix logical project grouping labels on mobile by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4391 * Add preview color scheme controls and simplify project grouping by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4385 * fix(cli): publish nightly branded favicons by @maria-rcks in https://github.com/pingdotgg/t3code/pull/4372 * Fix thread loading flash by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4396 * fix(client-runtime): keep a warm thread un-settled despite a merged/closed PR by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4309 * Fix composer context strip alignment and glass shell by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4404 * Polish iOS git progress overlay with glass effects by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4387 * Improve composer glass fallbacks by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4406 * feat(web): collapse large git diffs by default to make chat more readable by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4409 * Stop new threads inheriting checkout/branch from viewed thread by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4411 * fix: tone down branch-mismatch banner by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4416 * fix: Claude Code skills discoverable for the composer $ picker by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4414 * fix(web): keep settled threads reachable when opened directly by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4413 * feat(sidebar-v2): thread snoozing by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4311 * Upgrade Clerk packages and Expo integration by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4440 * Increase light-mode contrast for user message bubbles by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4441 * Restore model picker layout and retain iterative test state by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4450 * Color settled PR labels on hover by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4451 * [codex] Fix glass hover compositing artifacts by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4446 * Add Claude Opus 5 model by @thomaslittle in https://github.com/pingdotgg/t3code/pull/4472 * feat(web): add collapse-all toggle to diff panel by @0x4bs3nt in https://github.com/pingdotgg/t3code/pull/4475 * feat(web): show fast mode as a bolt instead of a "Normal" label by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4488 * feat(dev): keep worktree dev state isolated on T3 Code dev servers by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4555 * feat(dev): Make t3 code dev instances shareable over Tailscale by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4556 * fix(dev): skip browser-blocked ports by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4608 * fix: cut websocket throughput in half by pruning activity payloads by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4622 * perf(mobile): defer work-log detail serialization by @saphid in https://github.com/pingdotgg/t3code/pull/4607 * test: account for lazy thread feed details by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4628 * feat(relay): limit managed tunnels per user by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4530 * Add managed tunnel limits migration by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4635 * Add background preview capture and picture-in-picture support by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4397 * feat(web): prompt stash — cmd+S saves the composer to a per-provider queue by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4453 * [codex] Upgrade Effect and Alchemy betas by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4643 * feat: allow new thread creation through project breadcrumbs by @0x4bs3nt in https://github.com/pingdotgg/t3code/pull/4638 * fix(web): scope PR state to the thread branch by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4460 * Drop redundant Relay user indexes by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4648 * feat(connect): release the Cloudflare tunnel when the environment shuts down by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4531 * Fix Relay Worker RuntimeContext wiring by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4653 * Fix live sidebar resize limits and defer Alchemy runtime context by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4655 * fix(web): constrain branch toolbar context by @maxktz in https://github.com/pingdotgg/t3code/pull/4657 * Keep MCP credentials alive across provider turns by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4659 * fix: close actions dropdown when editing by @0x4bs3nt in https://github.com/pingdotgg/t3code/pull/4660 * fix(preview): stabilize PiP viewport identity by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4661 * Add glass styling for thread tooltips and simplify preview tab handling by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4665 * Use tarball archiving for hosted web deploys by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4669 * fix(server): bound editor discovery during config loading by @maria-rcks in https://github.com/pingdotgg/t3code/pull/4291 * Prevent draft thread detail polling before shell registration by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4670 * feat: add configurable source control writing settings by @maria-rcks in https://github.com/pingdotgg/t3code/pull/4204 * feat(diff-panel): show total line additions and deletions by @0x4bs3nt in https://github.com/pingdotgg/t3code/pull/4674 * Clear provider update actions while updating by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4676 * Fix sidebar highlighting for draft threads by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4679 * Use glass surfaces for web toasts by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4681 * Show origin ref in branch trigger label by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4680 * fix(mobile): match react version to react-native 0.85.3 vendored renderer (19.2.3) by @KrzysztofMoch in https://github.com/pingdotgg/t3code/pull/4675 ## New Contributors * @avocardow made their first contribution in https://github.com/pingdotgg/t3code/pull/3790 * @Prgm-code made their first contribution in https://github.com/pingdotgg/t3code/pull/3816 * @jakeleventhal made their first contribution in https://github.com/pingdotgg/t3code/pull/3899 * @VedankPurohit made their first contribution in https://github.com/pingdotgg/t3code/pull/3644 * @kridaydave made their first contribution in https://github.com/pingdotgg/t3code/pull/3990 * @dmstoykov made their first contribution in https://github.com/pingdotgg/t3code/pull/4017 * @HugoVizcainoSantana made their first contribution in https://github.com/pingdotgg/t3code/pull/3832 * @EricTsai83 made their first contribution in https://github.com/pingdotgg/t3code/pull/3998 * @AmoonPod made their first contribution in https://github.com/pingdotgg/t3code/pull/4088 * @BunnyGamezsc made their first contribution in https://github.com/pingdotgg/t3code/pull/4094 * @olivoil made their first contribution in https://github.com/pingdotgg/t3code/pull/4044 * @Fazalkadivar21 made their first contribution in https://github.com/pingdotgg/t3code/pull/3877 * @maxwellyoung made their first contribution in https://github.com/pingdotgg/t3code/pull/3932 * @nateEc made their first contribution in https://github.com/pingdotgg/t3code/pull/4097 * @leorivastech made their first contribution in https://github.com/pingdotgg/t3code/pull/3108 * @xxashxx-svg made their first contribution in https://github.com/pingdotgg/t3code/pull/3867 * @yordis made their first contribution in https://github.com/pingdotgg/t3code/pull/4055 * @Chrrxs made their first contribution in https://github.com/pingdotgg/t3code/pull/4006 * @Aditya190803 made their first contribution in https://github.com/pingdotgg/t3code/pull/3484 * @zepi2509 made their first contribution …
Opening a dev server from a remote client showed ERR_CONNECTION_REFUSED. The client rewrote `localhost:PORT` to the environment's hostname, kept the port and scheme, and navigated. That address only works if the port is independently published there, which it usually is not — dev servers bind loopback. The failure surfaced as a browser error page, indistinguishable from a broken app, so agents reported it as one. Reachability is now resolved by the environment, which is the only side that knows what is listening and what the tailnet routes. A new `preview.resolvePort` RPC answers with a URL it has verified: - a same-machine client keeps plain localhost; - an existing `tailscale serve` route is reused, at whatever port and scheme it was published under; - a port already answering on the environment's own address (WSL, LAN, a tunnel) uses that address and publishes nothing; - otherwise a tailnet-only HTTPS route is published, probed until it answers, and withdrawn when the dev server exits or the server shuts down. When none of those work it fails with a reason and a next action instead of a URL that cannot load. Navigation, the port list, chat links, and agent `preview_navigate`/`preview_open` all route through it; the old synchronous rewrite is kept for labels only. Two loopback-family bugs fell out of verifying this end to end. Vite's default `--host localhost` binds `::1` only, so `tailscale serve` mappings pinned to `127.0.0.1` proxy to nothing and answer 502 — `vp run dev --share` produced a URL that never worked. Both the new resolver and dev-share now target `localhost` and let tailscale pick the family. Upstream, not fork-local: both halves came from merged upstream PRs (pingdotgg#4011 rewrote the URL, pingdotgg#4556 added sharing) and the seam between them was never covered. Promote after merge.
Reconciles this branch with pingdotgg#4556, which replaced the free-form `detail` and `configureUrl` fields on TailscaleCommandExitError with a closed `stderrDiagnostic` label set, on the grounds that stderr can carry auth keys and the field is logged. This branch needs to name a Serve failure and link the admin page, so rather than picking a side the label set is extended: - Adds `serve-not-enabled` and `no-https-certs` labels with their stderr patterns, so the two failures this PR reports are classified rather than quoted. - Adds `describeTailscaleStderrDiagnostic`, which turns a label into prose at the UI edge. The shared `message` deliberately stays generic, because that is the string pingdotgg#4556 was protecting — main's test asserting the generic form still holds unchanged. - Restores `configureUrl` as a separate field. It is exempt from the label-only rule because it is not lifted text: it is matched against a fixed pattern and validated to `https://login.tailscale.com/f/serve...`, so it cannot carry stderr contents, and without it there is no way to point the user at the fix. The desktop layer now derives its toast prose from the label via `describeTailscaleServeExitCause` instead of reading `cause.detail`. Note: `apps/desktop` DesktopObservability and DesktopBackendManager tests fail on this merge, and `packages/shared` plus `scripts` have typecheck errors. All are in main's own files — DesktopBackendManager is traceable to main adding `OtlpExporter.layerFlusher` — and are left untouched rather than fixed here. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015ajcLHZtGhRQRD6BwscU9R
`tailscale serve --https=<port> off` exits 1 with "handler does not exist"
when nothing is bound on that port. Confirmed against the real CLI:
$ tailscale serve --https=8443 off
error: failed to remove web serve: handler does not exist
exit=1
The eager teardown treated that as a failure, so settings never recorded
`enabled: false`: the toggle stayed on and the user was warned the backend
might still be reachable, when in fact nothing was serving. It bit hardest in
precisely the case the eager teardown was added for — settings saying enabled
while no child ever bound Serve — leaving the user unable to turn it off at all.
Upstream anticipated this: pingdotgg#4556's label set carries `no-existing-handler` with
a comment naming `serve off` on an unmapped port as the case callers should
recognize. Match on it and continue.
The rollback is now armed only when a binding was actually removed. Re-creating
one that never existed would expose the backend rather than restore it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015ajcLHZtGhRQRD6BwscU9R


Note
Dev mode overhaul — PR 2 of 3. The stack moves from worktree-isolated dev state → single-origin and Tailscale sharing → pairing and seed utilities. This PR is stacked on #4555, and #4557 is stacked on this PR.
What
dev:share/--sharesupport that publishes the web port with Tailscale and cleans up the mapping on exit.Why
Remote browsers could load the frontend but then try to connect to their own localhost for backend traffic. Sharing also needed the startup pairing URL, cookie policy, allowed origins, and Tailscale lifecycle to agree on the same public origin.
Impact
This is PR 2 of 3 and is stacked on #4555. Its diff is limited to single-origin browser hosting and Tailscale sharing.
Verification
Note
Add
--shareflag to publish dev instances over Tailscale--shareflag (anddev:sharenpm script) to the dev runner that publishes the web dev server over HTTPS on the tailnet viatailscale serve, augmentingT3CODE_DEV_ALLOWED_ORIGINSwith the tailnet origin so the backend accepts CORS requests from it.dev,dev:web) now run in single-origin mode:VITE_HTTP_URL/VITE_WS_URLare cleared and Vite proxies backend path prefixes (/api,/oauth,/.well-known,/ws) so the app resolves the backend fromwindow.location.originrather than a hardcoded URL, enabling access from non-localhost origins.TailscaleStderrDiagnostic) to produce sanitized error messages without leaking secrets or node names; share/unshare failures surface structuredDevShareErrortypes with stage-specific messages.t3_session_<port>) in desktop mode and in web dev mode when adevUrlis configured, so multiple instances on different ports do not collide.--hostvalues withDevRunnerHostNotProxiableError; inheriting a non-loopbackHOSTenv var will also be rejected.Macroscope summarized 83783d0.
Note
Medium Risk
Touches authentication (cookies, pairing TTL, CORS) and dev networking; mistakes could break local login or leak sensitive Tailscale stderr, though changes are dev-scoped with broad test coverage.
Overview
Browser dev becomes single-origin so the same UI works on localhost, tailnet, or LAN without baking in
localhostbackend URLs. The dev runner clearsVITE_HTTP_URL/VITE_WS_URL, setsT3CODE_SINGLE_ORIGIN_DEV=1, and Vite proxies/api,/oauth,/.well-known, and/wsvia a sharedDEV_PROXIED_PATH_PREFIXESlist; the server 404s those paths in dev instead of redirecting to Vite. Vite allows*.ts.nethosts and only pins HMR whenHOSTis set (desktop).--share/dev:sharepublishes the web port with Tailscale (scripts/lib/dev-share.ts), setsVITE_DEV_SERVER_URLand appends the tailnet origin toT3CODE_DEV_ALLOWED_ORIGINS, and removes the mapping on exit (skipped for dry-run and warned fordev:desktop). Tailscale CLI failures now expose sanitizedstderrDiagnosticlabels instead of raw stderr.Auth and cookies align with multi-instance dev: session cookies are
t3_session_<port>whendevUrlis set or in desktop mode; startup pairing tokens get a 24h TTL in dev viapurpose: "startup"; dev CORS includes configured and shared origins.Dev runner gains worktree-stable port offsets, loopback-focused port probing (avoids fighting
tailscale serve), bind-host-aware server probes, rejection of non-proxiable--hostfordev/dev:web, andDevRunnerHostNotProxiableError.Reviewed by Cursor Bugbot for commit 83783d0. Bugbot is set up for automated code reviews on this repo. Configure here.
Summary by CodeRabbit
New Features
dev:share, publishing the web app over HTTPS on a tailnet.Bug Fixes
Documentation