Summary
A scheduled task created through the Orchestrator MCP schedule_task tool that launches a fresh thread per run (bindToCurrentThread: false, or a projectId other than the caller's, which defaults to unbound) always provisions that thread's worktree from a hard-coded baseRef: "main" with startFromOrigin: true. In a Git project whose default branch is not main (for example master or trunk) and which has no main branch locally or on origin, every run creates a new top-level thread whose only run fails at workspace preparation with git worktree add failed. The scheduler records each of these runs as lastRunStatus: "succeeded", so the task stays enabled and keeps producing failed threads without any signal in the task list or to the scheduling agent.
Steps to reproduce
Known case, not executed end-to-end against a running T3 server:
- Open a Git project whose only branch is
master, with an origin remote that also has only master.
- From an agent thread in that project, call
schedule_task with {"title":"Hourly check","prompt":"Report repository status.","schedule":{"type":"interval","everyMs":3600000},"bindToCurrentThread":false}.
- Call
run_scheduled_task_now with the returned task id, or wait for the next interval.
- Open the thread created by that run, then call
list_scheduled_tasks.
The git commands the launch path issues for step 3 were executed in a disposable repository with the same shape (bare origin containing only master, plus a clone):
$ git remote get-url origin # exit 0
$ git fetch --quiet origin '+refs/heads/main:refs/remotes/origin/main'
fatal: couldn't find remote ref refs/heads/main # exit 128; code then falls back to `git fetch origin` (exit 0)
$ git show-ref --verify --quiet refs/remotes/origin/main # exit 1, so the local baseRef "main" is used
$ git -c checkout.workers=0 worktree add -b t3code/abc123 ../wt/t3code-abc123 main
fatal: invalid reference: main # exit 128
$ git symbolic-ref refs/remotes/origin/HEAD
refs/remotes/origin/master
$ git -c checkout.workers=0 worktree add -b t3code/ok ../wt/t3code-ok master
Preparing worktree (new branch 't3code/ok') # control, exit 0
A repository with only trunk and no remote fails the same worktree add ... main with fatal: invalid reference: main.
Expected behavior
The schedule_task tool description promises that an unbound task launches a working fresh thread per run: "use false only when the user wants a fresh top-level thread per run. Elsewhere each run launches a fresh thread." The tool exposes no base-branch parameter and says nothing about a main branch being required, so a fresh-thread run in a valid Git project with a different default branch should start its agent in a usable workspace.
Actual behavior
By static trace of the supported entry point: schedule_task stores { type: "worktree", baseRef: "main", startFromOrigin: true } for every unbound task, and update_scheduled_task re-applies the same value whenever bindToCurrentThread is passed. Each run calls ThreadLaunchService.launch, which returns after dispatching thread creation and forking workspace preparation into the background. Preparation finds no origin/main, keeps the literal local main, and runs git worktree add -b t3code/<id> <path> main, which fails as shown above. The thread receives a "Workspace preparation failed" item ending in git worktree add failed (stderr is dropped; see #4380). Because launch already returned, the scheduler's Effect.exit is a success, and it records lastRunStatus: "succeeded" with lastRunError: null, re-arms nextRunAt, and leaves the task enabled. The Settings badge and the list_scheduled_tasks summary both show success. Runtime reproduction through a running T3 server was not executed.
Evidence
Runtime reproduction through T3 was not run. The static proof covers the reachable path from the MCP tool: the default at OrchestratorMcpService.ts:1322-1324 makes any other-project projectId unbound; ScheduledTaskService.upsert stores the strategy without validation; GitVcsDriverCore.fetchRemote falls back to a plain fetch on a missing remote ref; remoteBranchExists is false; and createWorktree passes the unresolved main to git worktree add. The git commands were executed directly and produced the failures shown. The Settings UI scheduling path also defaults its base to main (apps/web/src/components/settings/ScheduledTasksSettings.tsx:595) but lets the user pick another branch, so it is not a clean control. GitVcsDriverCore.resolveDefaultBranchName already exists in the server.
Restoration check
Using the reproduction above (a master-only project with a master-only origin), an unbound scheduled run currently yields a thread whose run failed at workspace preparation with git worktree add failed, while the task reports lastRunStatus: "succeeded". After the fix, the same run starts its agent in a prepared worktree (in this fixture, from master). Control: a project that does have main must still prepare its fresh-thread runs from main, and bound tasks (bindToCurrentThread: true) must keep posting into the calling thread without a worktree.
Additional context
The scheduler records whether a run was dispatched, not whether its thread was prepared, for both bound and unbound runs; no contract text defines lastRunStatus, so this report treats the misleading "succeeded" status only as a consequence that hides the failure, not as a separate defect. Workarounds available today: use bindToCurrentThread: true (runs share the calling thread's context and checkout and queue behind its activity), or edit the task in Settings, Scheduled tasks, and pick a real base branch (a later update_scheduled_task that passes bindToCurrentThread resets the base to main). A non-Git project with an unbound schedule also fails every run at the same preparation step, with "Failed to resolve the VCS driver for this Git command."; that may share the restoration but was not traced further. Related: #4380 (git stderr discarded), discussion #6866 (configurable default base branch for new worktree threads).
Area
apps/server
Impact
Major degradation or frequent failure. Every fresh-thread run of an MCP-created schedule fails in Git projects without a main branch, and the scheduler records each run as succeeded, so failed threads accumulate unnoticed.
Version or commit
main at 6f9cea00ae967f38fa3cdc9c07f92031806f4264.
Environment
Source inspection of pingdotgg/t3code main at 6f9cea0. Git behavior checked with git 2.56.0 on macOS with global and system config disabled. The T3 server itself was not run.
Workaround
Editing the task's base branch in Settings, or binding runs to the calling thread, avoids the failure today, at the cost of manual per-task setup or giving up per-run fresh threads.
Before submitting
Summary
A scheduled task created through the Orchestrator MCP
schedule_tasktool that launches a fresh thread per run (bindToCurrentThread: false, or aprojectIdother than the caller's, which defaults to unbound) always provisions that thread's worktree from a hard-codedbaseRef: "main"withstartFromOrigin: true. In a Git project whose default branch is notmain(for examplemasterortrunk) and which has nomainbranch locally or onorigin, every run creates a new top-level thread whose only run fails at workspace preparation withgit worktree add failed. The scheduler records each of these runs aslastRunStatus: "succeeded", so the task stays enabled and keeps producing failed threads without any signal in the task list or to the scheduling agent.Steps to reproduce
Known case, not executed end-to-end against a running T3 server:
master, with anoriginremote that also has onlymaster.schedule_taskwith{"title":"Hourly check","prompt":"Report repository status.","schedule":{"type":"interval","everyMs":3600000},"bindToCurrentThread":false}.run_scheduled_task_nowwith the returned task id, or wait for the next interval.list_scheduled_tasks.The git commands the launch path issues for step 3 were executed in a disposable repository with the same shape (bare
origincontaining onlymaster, plus a clone):A repository with only
trunkand no remote fails the sameworktree add ... mainwithfatal: invalid reference: main.Expected behavior
The
schedule_tasktool description promises that an unbound task launches a working fresh thread per run: "use false only when the user wants a fresh top-level thread per run. Elsewhere each run launches a fresh thread." The tool exposes no base-branch parameter and says nothing about amainbranch being required, so a fresh-thread run in a valid Git project with a different default branch should start its agent in a usable workspace.Actual behavior
By static trace of the supported entry point:
schedule_taskstores{ type: "worktree", baseRef: "main", startFromOrigin: true }for every unbound task, andupdate_scheduled_taskre-applies the same value wheneverbindToCurrentThreadis passed. Each run callsThreadLaunchService.launch, which returns after dispatching thread creation and forking workspace preparation into the background. Preparation finds noorigin/main, keeps the literal localmain, and runsgit worktree add -b t3code/<id> <path> main, which fails as shown above. The thread receives a "Workspace preparation failed" item ending ingit worktree add failed(stderr is dropped; see #4380). Becauselaunchalready returned, the scheduler'sEffect.exitis a success, and it recordslastRunStatus: "succeeded"withlastRunError: null, re-armsnextRunAt, and leaves the task enabled. The Settings badge and thelist_scheduled_taskssummary both show success. Runtime reproduction through a running T3 server was not executed.Evidence
schedule_tasktool description in apps/server/src/mcp/toolkits/orchestrator/tools.ts:100.Runtime reproduction through T3 was not run. The static proof covers the reachable path from the MCP tool: the default at
OrchestratorMcpService.ts:1322-1324makes any other-projectprojectIdunbound;ScheduledTaskService.upsertstores the strategy without validation;GitVcsDriverCore.fetchRemotefalls back to a plain fetch on a missing remote ref;remoteBranchExistsis false; andcreateWorktreepasses the unresolvedmaintogit worktree add. The git commands were executed directly and produced the failures shown. The Settings UI scheduling path also defaults its base tomain(apps/web/src/components/settings/ScheduledTasksSettings.tsx:595) but lets the user pick another branch, so it is not a clean control.GitVcsDriverCore.resolveDefaultBranchNamealready exists in the server.Restoration check
Using the reproduction above (a
master-only project with amaster-onlyorigin), an unbound scheduled run currently yields a thread whose run failed at workspace preparation withgit worktree add failed, while the task reportslastRunStatus: "succeeded". After the fix, the same run starts its agent in a prepared worktree (in this fixture, frommaster). Control: a project that does havemainmust still prepare its fresh-thread runs frommain, and bound tasks (bindToCurrentThread: true) must keep posting into the calling thread without a worktree.Additional context
The scheduler records whether a run was dispatched, not whether its thread was prepared, for both bound and unbound runs; no contract text defines
lastRunStatus, so this report treats the misleading "succeeded" status only as a consequence that hides the failure, not as a separate defect. Workarounds available today: usebindToCurrentThread: true(runs share the calling thread's context and checkout and queue behind its activity), or edit the task in Settings, Scheduled tasks, and pick a real base branch (a laterupdate_scheduled_taskthat passesbindToCurrentThreadresets the base tomain). A non-Git project with an unbound schedule also fails every run at the same preparation step, with "Failed to resolve the VCS driver for this Git command."; that may share the restoration but was not traced further. Related: #4380 (git stderr discarded), discussion #6866 (configurable default base branch for new worktree threads).Area
apps/server
Impact
Major degradation or frequent failure. Every fresh-thread run of an MCP-created schedule fails in Git projects without a
mainbranch, and the scheduler records each run as succeeded, so failed threads accumulate unnoticed.Version or commit
mainat6f9cea00ae967f38fa3cdc9c07f92031806f4264.Environment
Source inspection of
pingdotgg/t3codemainat 6f9cea0. Git behavior checked with git 2.56.0 on macOS with global and system config disabled. The T3 server itself was not run.Workaround
Editing the task's base branch in Settings, or binding runs to the calling thread, avoids the failure today, at the cost of manual per-task setup or giving up per-run fresh threads.
Before submitting