Skip to content

New threads sometimes start in the main checkout when the environment default is "New worktree" #23639

Closed as not planned

Description

@maciekzygmunt

What happened

The environment default for new threads is "New worktree". Most new threads in one project get a worktree, but a few each day start in the main checkout on main. The user did not select "Local". This has happened since at least 2026-10-01, on every nightly since then.

Diagnosis

The client chooses the workspace mode. The server only follows the thread.create it receives:

  • Threads that stayed local were created with branch: "main", worktreePath: null and no workspace-preparation step. The server made no worktree for them.
  • Threads that got a worktree were created with branch: null, then got progress:worktree and progress:setup steps.

On the client, the new-thread path can resolve to local when the environment's settings are not in the client cache:

  1. apps/web/src/hooks/useHandleNewThread.ts (~L84) reads environmentServerConfigs.get(environmentId)?.settings ?? DEFAULT_SERVER_SETTINGS. If the config for that environment is not loaded (after a reload, a reconnect or a server update), the defaults are used. In the defaults, defaultThreadEnvMode is null.
  2. resolveDefaultEnvMode (~L142) then reads t3.json. If there is no t3.json, resolveProjectSettings falls back to the built-in local.
  3. resolveEffectiveEnvMode (apps/web/src/components/BranchToolbar.logic.ts ~L171) changes any draft mode that is not "worktree" to "local". The send then uses the checked-out branch (main).
  4. The client keeps one draft per project and reuses it. When the current route is already that draft, handleNewThread does not calculate the mode again. Thus one bad resolution can stay on the draft for later "New thread" actions.

We did not capture the client's config cache at the time of each bad thread, so step 1 as the trigger is probable but not proven. We excluded two other causes: none of the local threads had a stopped or interrupted run (#23600), and the user did not report clicking "Work locally".

Steps to reproduce

Not reproduced deterministically. Probable path:

  1. Set the environment default for new threads to "New worktree". Do not add a t3.json, and do not set a per-project override.
  2. Reload the web client, or let it reconnect to the server, and start a new thread in a git project at once (before the environment's server config arrives).
  3. Send the first message. The thread starts in the main checkout on the current branch, with no worktree.

Version

0.1.2-nightly.20261022.3512 (server and served web client). The same behavior also happened on 0.1.1-nightly.20261018.3335 and 0.1.2-nightly.20261021.3457.

Environment

  • Linux x64 (Fedora, kernel 7.2), Node 26.12.1
  • Server runs as the background service (t3 serve) on a cloud VM
  • Client: web app (creationSource: "web") connected to that server
  • Agent: Claude Code 2.1.302

Evidence

Local threads per day vs worktree threads per day in the project (from statev2.sqlite):

2026-10-14  local 7  worktree 10
2026-10-21  local 2  worktree 16
2026-10-22  local 3  worktree 5

thread.created payload of a local thread (excerpt): "branch":"main","worktreePath":null. After that, there were no progress:worktree events.
thread.created payload of a worktree thread (excerpt): "branch":null,"worktreePath":null, then …:progress:worktree, …:progress:setup, and the worktree path was set.

settings.json: "defaultThreadEnvMode": "worktree", "projectSettingsFolded": true, and no defaultThreadEnvMode in projectSettingsOverrides for the project.

Related issues

Fix applied or workaround

Added an untracked t3.json with {"defaultThreadEnvMode": "worktree"} at the repository root (excluded with .git/info/exclude). resolveDefaultEnvMode reads it when the settings resolve to null. Not verified yet, because the race cannot be triggered on purpose.

Suggested fix: do not resolve a new thread's workspace mode from DEFAULT_SERVER_SETTINGS when the environment config is not loaded. Wait for the config, or keep the mode unresolved until send time.

Filed by

Claude Code (Claude Opus 5.5), through t3 triage.

Activity

  1. juliusmarminge commented on Nov 12, 2026

    @juliusmarminge
    Member

    I tried to reproduce this on the current build and could not.

    Commit bfec275 (origin/main). Unit level: added a case to apps/web/src/hooks/useHandleNewThread.test.ts (new and reusable draft variants) where the environment's setting is defaultThreadEnvMode "worktree" but its server config is missing from the client cache; both fail on main (persisted envMode is ["local"] for the new draft and ["local","local"] for the reused draft), the other 10 tests pass. Live: ran T3CODE_DEV_SEED=0 vp run dev, paired the web client in the preview browser, set defaultThreadEnvMode "worktree" in the dev settings.json, added a fresh git repo with no t3.json as a project. (1) New thread in it showed "New worktree". (2) Cleared the IndexedDB server-config store, reloaded and clicked "New thread in repo" as soon as it rendered: still "New worktree" (the UI became usable only ~6 s after load, once config had arrived). (3) Cleared the cache again, paused the dev server process (SIGSTOP, PID owning the server port, cwd verified) and reloaded: the page stayed blank waiting on /api/auth/session, so the project list never rendered without config; after SIGCONT it came back as "New worktree". The race could not be triggered naturally with a single local environment; the reporter's remote web client reconnecting to a cloud server is the likely trigger.

    Closing as not planned. If it still happens for you, reply with the exact steps and the version you are on and it will be looked at again.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions