Repository navigation
fix(server): scheduled runs that cannot get a worktree are reported as failed - #19774
realhasanshoaib wants to merge 5 commits into
Conversation
…s failed A scheduled task that launches a fresh thread per run with a worktree strategy was recorded as "succeeded" as soon as the thread launch was accepted, even when its worktree could never be made, such as in a folder that is not a Git repository. The run then failed inside its thread before the agent started, and the scheduler never said so. - The pre-launch worktree check now also refuses a folder with no `.git` in it or above it. A repository Git refuses to read is still left to the launch. - The scheduler runs that check before launching, so such a run is recorded as failed with the reason, and no dead thread is created. - schedule_task and update_scheduled_task take an optional workspace (root or worktree), so an agent can choose to run in the project folder; an explicit worktree that cannot be made is refused up front. The task summary now shows the workspace, and an update keeps it unless the binding or the workspace changes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
ApprovabilityVerdict: Approved at Macroscope's review found this PR approvable — This is a focused scheduler fix that records impossible worktree launches as failed without creating dead threads. The new MCP workspace selection is optional and preserves existing defaults, with coverage for repository validation and update behavior. You can add or adjust custom eligibility rules. Learn more. |
Root runs have nothing to check, and skipping the call keeps scheduler fixtures that stub only `launch` working. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…orted as failed A launch is accepted before its workspace is prepared, so a run whose worktree fetch or setup script failed still read "succeeded". The scheduler now waits for the launched run to leave preparation and, if it failed, marks that run failed. The write is guarded by the run's start time, so a later run's status is never overwritten. It also covers the hint that t3_thread_launch gives for a folder that is not a Git repository. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The watcher read the run's current status, so a retried preparation could hide its first failure, and a provider failure after setup could be blamed on setup. It now replays the new thread's run updates and decides from the run's first status other than "preparing". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
If an agent updates a worktree schedule and passes
The agent can't see this happen. The tool description promises "Only the provided fields change", so I'd treat a repeated type as no change: const workspaceStrategy =
!rebound &&
(input.workspace === undefined || input.workspace === existing.workspaceStrategy.type)
? existing.workspaceStrategy
: yield* scheduledTaskWorkspaceStrategy(existing.projectId, threadId !== null, input.workspace);I checked this with a scratch test at e0fd539. The three bad cases keep their saved strategy. The binding and type transitions (root to worktree, worktree to root, existing_worktree to worktree, bound to unbound and back) behave as before. The existing test "an update keeps the workspace unless the binding or the workspace changes" only asserts |
Problem
A scheduled task that starts each run in a fresh worktree says succeeded even when that worktree can never be made, for example in a folder that is not a Git repository, or when its setup script fails. The run dies before the agent starts, and the only trace is a failed thread nobody opens. We had seven agent-scheduled tasks in a No-project folder showing "succeeded" while none of them ever ran. Agents scheduling over MCP also cannot ask for the project folder instead of a worktree, which the Settings form already offers.
Score: 5/5.
Change
A scheduled run is now reported as failed whenever its workspace cannot be prepared:
failed(a fetch error or a failing setup script, for example), that run is marked failed.schedule_taskandupdate_scheduled_tasktake an optionalworkspace(rootorworktree). An explicitworktreethat cannot be made is refused when the task is saved. With no value, tasks keep today's worktree default. The task summary now showsworkspace, and an update keeps it unless the binding or the workspace changes.🟢 Changed (4)
.gitin it or above itworkspace; summary shows it; updates keep itt3_thread_launch⚪ Untouched (5)
Undo by reverting the commits. If the repository check were wrong, worktree tasks in that folder would be recorded as failed instead of launching.
Scope and approval
This addresses part of #17309. Julius's triage comment confirms the failure. The status fix is a focused fix of an obvious bug: the scheduler reports success for runs that never started.
The
workspaceoption is focused configuration of an established capability, as the template describes:How it relates to the other open PRs on #17309:
schedule_taskpicks root for folders that are not Git repositoriesVerification
Live, against isolated servers on an M4 Mac mini (
t3 serve --base-dir <temp>, loopback only, throwaway state). An outside MCP client signed in over OAuth with a pairing code, then scheduled and ran tasks in two projects: a plain folder, and a real Git repository whose setup script isexit 1. The same script ran againstmainand against this branch.mainfailedworkspace: "worktree"in the plain folderProject "Plain folder" (…) is not a Git repository, so it cannot get a worktree. Pass workspace:"root" to run in the project folder.workspace: "root"✅ Checked (7)
~/.t3/scratch.gitabove it, so it counts as not a repository.gitwalk on Windows pathspath.win32on drive, UNC and drive-root pathsC:\,\\server\share\andD:\vp test runon the touched test files plus every test that stubs the launch service, including the scheduler integration test (14 files); server and contracts typecheck;vp lintandvp fmtper fileTest cleanup: in the worktree-check tables, two rows caught only mutants another row already catches. I deleted them ("a base ref that resolves" and "a previous checkout there never was"). Every remaining test in that slice catches a mutant no other test catches.
Technical details
t3code/apps/server/src/orchestration-v2/ThreadLaunchService.ts
Lines 283 to 339 in 2911fbd
t3code/apps/server/src/scheduledTasks/ScheduledTaskService.ts
Lines 839 to 890 in 2911fbd
t3code/apps/server/src/scheduledTasks/ScheduledTaskService.ts
Lines 677 to 722 in 2911fbd
t3code/apps/server/src/mcp/OrchestratorMcpService.ts
Lines 1626 to 1649 in 2911fbd
ThreadLaunchService.ts.gitexists in it or above it. Anything else stays "a failing Git" and is left to the launch, as before.ScheduledTaskService.tsEffect.exit, so a refusal lands in the run's failed status. AftermarkCompleted, a fiber in the service scope replays the new thread'srun.updatedevents and takes the run's first status other thanpreparing. Onlyfailedis recorded, so a later retry or a provider failure afterstartingdoes not change the result. The write is guarded bylast_run_atandlast_run_status = 'succeeded', so it never touches a later run.OrchestratorMcpService.tsworkspacehandling. An update recomputes the strategy only when the binding really changes orworkspaceis passed.Two limits, by design: a server restart between a preparation failure and its record leaves that run as succeeded (restart recovery cancels runs that are still preparing). And a webhook delivery's log keeps "started", which is what happened to the delivery.
GPT-6 Astra (high) reviewed two rounds and raised four issues, all fixed here. Repository errors could silently pick root. An update repeating
bindToCurrentThread:falsedropped an explicit root. A retried preparation could hide its first failure. A provider failure could be blamed on setup.Model: Claude Opus 5.5. Harness: Claude Code in T3 Code. Reviewed by GPT-6 Astra (high).
🤖 Generated with Claude Code