Originally created by: kumaakh
When a fleet member's work_folder is a git worktree (not a regular repo clone), the fleet server's bash execution context can corrupt the .git pointer file, breaking all subsequent git operations for that member. This has never been observed with regular clones — it is specific to worktrees.
A git worktree is a secondary working tree linked to a parent repo. Unlike a regular clone, the worktree directory does not contain a .git/ directory — it contains a .git file with a single line:
gitdir: C:/akhil/git/apra-fleet/.git/worktrees/apra-fleet-skill-refactor
This path points to the worktree's metadata inside the parent repo's .git/worktrees/ directory. The path format in this file must match what the invoking git binary expects.
Fleet dispatches execute_command via a bash shell on Windows members. On Windows, bash is typically Git Bash (MINGW64) — it resolves C:/... paths natively, so git works correctly out of the box.
However, when git fails for unrelated reasons (credentials, index issues, etc.), the dispatched agent tries to diagnose and "fix" the worktree. It sees the C:/... path in .git, observes that bash paths use /mnt/c/... in WSL, and rewrites the .git file to use WSL path format:
gitdir: /mnt/c/akhil/git/apra-fleet/.git/worktrees/apra-fleet-skill-refactor
This makes bash git (if running under WSL) work temporarily, but breaks Windows git.exe — which is what the fleet server uses for authenticated pushes via Windows Credential Manager. Subsequent git push calls fail with:
fatal: not a git repository: /mnt/c/akhil/git/apra-fleet/.git/worktrees/apra-fleet-skill-refactor
The worktree is now in an inconsistent state that persists across all future dispatches until manually repaired with git worktree repair.
Regular clones have a .git/ directory — no path file to corrupt. The shell context (Git Bash vs WSL) does not affect the repository's internal structure. Worktrees are uniquely vulnerable because they store an absolute path in a plain text file.
.git to WSL path to unblock bash gitfatal: not a git repositorygit worktree repair via Windows git.exeTwo compounding issues:
Agents modify .git files — they should never do this. The .git file in a worktree is repo metadata, not a config file. Any agent that rewrites it is making an unrecoverable change.
No worktree awareness in execute_command — when the server detects a member's work_folder contains a .git file (vs a .git/ directory), it should handle git invocation differently: always use the Windows-native git binary for authenticated operations, and never allow command output to modify the .git file.
Server-side (execute_command / execute_prompt):
work_folder is a worktree: stat .git — if it's a file, it's a worktreegit.exe (Windows binary) for push/fetch/pull — never via bash git, which may resolve paths differently.git file: if a command attempts to overwrite .git, block itAgent context (tpl-doer.md):
.git files — if git fails in a worktree, report blocked, do not attempt to repair the pathFleet onboarding / member registration:
work_folder values that point to git worktrees until this is fixedgit worktree repair on registration to normalize the pathIf a Windows member's worktree .git file gets corrupted with a WSL path, run:
git.exe -C "C:\path\to\worktree" worktree repair
This restores the correct Windows-format gitdir: pointer without needing to know the correct path manually.
Originally posted by: kumaakh
Technical direction: Two independent fixes that together eliminate the problem:
Agent context rule (skills/pm/tpl-doer.md): add an explicit rule: 'Never modify .git files or any file inside .git/. If git fails in a worktree, report blocked — do not attempt path repair.' This is the cheapest fix and prevents recurrence immediately.
Server-side detection (src/tools/execute-prompt.ts or member registration): when work_folder is set, stat .git — if it's a file (not a directory), the member is using a worktree. Store isWorktree: boolean in the member registry. On Windows worktree members, execCommand should invoke git.exe (not bash git) for push/fetch/pull operations to avoid path format mismatch.
Registration guard (src/tools/register-member.ts): detect worktree .git file at registration time and warn the operator, suggesting git worktree repair first.
The workaround (git.exe worktree repair) is already documented in MEMORY.md. The doer template rule is the immediate action item — the server-side detection can follow in a polish sprint.
Originally posted by: kumaakh
Research: Git Worktree .git Corruption on Windows
1. Where does the fleet server resolve/use
work_folder?workFolderis defined on theAgentinterface atsrc/types.ts:17and used throughout:src/tools/execute-command.tsresolveTilde(input.run_from ?? agent.workFolder)— resolves folder for command executionsrc/tools/execute-prompt.tsresolveTilde(agent.workFolder)— sets up prompt execution contextsrc/services/strategy.tsthis.agent.workFolderin RemoteStrategy.deleteFilessrc/services/strategy.tsexec(wrapped, { cwd: this.agent.workFolder, ...in LocalStrategy.execCommandsrc/services/strategy.tssrc/services/strategy.ts2. Is there any existing worktree detection?
NO. No
.gitfile vs directory checks exist anywhere in the codebase.src/version.ts:24-28reads.git/HEADfor dev version detection, but only for the fleet server itselfgitcommand (notgit.exe) everywhere:src/os/windows.ts:259-260—gitCurrentBranch()invokesgitvia PowerShellsrc/os/linux.ts:230-231— same with bashgitsrc/os/windows.ts:219-220, 226— credential helper setup also uses baregit3. Best place to add worktree detection
Recommended: Strategy Layer (
src/services/strategy.ts)Add
isWorktree()to theAgentStrategyinterface (lines 15-23) and implement in bothRemoteStrategyandLocalStrategy:fs.statSync(path.join(workFolder, '.git')).isFile()— if.gitis a file (not directory), it's a worktreetest -f <workFolder>/.git && echo true || echo falseThen in
src/os/windows.ts:259-260and credential helper methods, usegit.exeexplicitly when worktree is detected. This is the root fix — bash-bundledgiton Windows mishandles the.gitfile reference in worktrees.Secondary option: Add detection in
execute-command.tsat line ~216 (before command wrapping), but this is higher in the stack and would need to be duplicated forexecute-prompt.ts.4. Guard against agent writes to
.gitfileAvailable hook points:
Hook 1:
execute-command.ts:130-170(after credential token resolution).gitpathssec://handles — same approach works/\.git[\\\/]/combined with write indicators (echo,Set-Content,tee,printf,>)Hook 2:
strategy.ts:28-30(SSH exec entry point in RemoteStrategy)isDangerous(command)guard before executing via SSHgit -C repo/.git-mirror)Limitation: Cannot reliably block all shell-level operations (e.g.
sh -c "echo x > .git/HEAD"). The real fix is usinggit.exeon Windows, not trying to filter every possible write. The command filtering is a defense-in-depth layer.5. tpl-doer.md change complexity
TRIVIAL — 1-2 lines of documentation.
The file is at
skills/pm/doer-reviewer.md. The safeguards section (lines 64-76) already has a table of restrictions. Add one row for.gitfile protection, and add a note to the "Git as transport" section (lines 78-82):6. Implementation complexity estimate
MEDIUM (~9.5 hours)
isWorktree()+ caching in both strategy classesgit.exeon WindowsgitCurrentBranch()+ credential helpers inwindows.tsexecute-command.tssec://patternisDangerous()guardWhy MEDIUM: Cross-platform concerns (Windows PowerShell escaping vs bash), false positive risk in command filtering, and need for real worktree testing make this more than a quick fix. But no architectural changes needed — it's localized to strategy, os-commands, and execute-command layers.
Root cause summary
On Windows with git worktrees,
.gitis a file (containinggitdir: /path/to/parent/.git/worktrees/name) not a directory. When bash-bundledgitruns in this context, it can mishandle the.gitfile reference. The fix is: detect worktree status, then enforce nativegit.exefor all git operations on Windows worktree members.