Menu

#91 bug: git worktree .git file path corrupted by bash shell context on Windows members

open
nobody
None
2026-04-26
2026-04-07
Anonymous
No

Originally created by: kumaakh

Summary

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.

Background: How git worktrees work

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.

What goes wrong

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.

Why it never occurred with regular clones

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.

Observed impact (this sprint)

  • Phase 3 agent rewrote .git to WSL path to unblock bash git
  • Phases 4 and 5 inherited the broken state
  • Phase 5 push failed with fatal: not a git repository
  • Recovery required: git worktree repair via Windows git.exe
  • Total disruption: 2 phases affected, manual PM intervention required

Root cause

Two compounding issues:

  1. 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.

  2. 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.

Proposed fix

Server-side (execute_command / execute_prompt):

  • Detect when work_folder is a worktree: stat .git — if it's a file, it's a worktree
  • On Windows members with a worktree: always invoke git via git.exe (Windows binary) for push/fetch/pull — never via bash git, which may resolve paths differently
  • Guard against agent writes to .git file: if a command attempts to overwrite .git, block it

Agent context (tpl-doer.md):

  • Add a rule: never modify .git files — if git fails in a worktree, report blocked, do not attempt to repair the path

Fleet onboarding / member registration:

  • Warn or reject work_folder values that point to git worktrees until this is fixed
  • Or: automatically run git worktree repair on registration to normalize the path

Workaround (until fixed)

If 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.

Related

Tickets: #105
Tickets: #73

Discussion

  • Anonymous

    Anonymous - 2026-04-23

    Originally posted by: kumaakh

    Technical direction: Two independent fixes that together eliminate the problem:

    1. 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.

    2. 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.

    3. 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.

     
  • Anonymous

    Anonymous - 2026-04-26

    Originally posted by: kumaakh

    Research: Git Worktree .git Corruption on Windows

    1. Where does the fleet server resolve/use work_folder?

    workFolder is defined on the Agent interface at src/types.ts:17 and used throughout:

    File Line(s) Usage
    src/tools/execute-command.ts 169 resolveTilde(input.run_from ?? agent.workFolder) — resolves folder for command execution
    src/tools/execute-prompt.ts 104 resolveTilde(agent.workFolder) — sets up prompt execution context
    src/services/strategy.ts 43 this.agent.workFolder in RemoteStrategy.deleteFiles
    src/services/strategy.ts 77 exec(wrapped, { cwd: this.agent.workFolder, ... in LocalStrategy.execCommand
    src/services/strategy.ts 101-102 Destination base in LocalStrategy.transferFiles
    src/services/strategy.ts 131, 147, 152-153 LocalStrategy.receiveFiles and testConnection

    2. Is there any existing worktree detection?

    NO. No .git file vs directory checks exist anywhere in the codebase.

    • src/version.ts:24-28 reads .git/HEAD for dev version detection, but only for the fleet server itself
    • Git operations use bare git command (not git.exe) everywhere:
    • src/os/windows.ts:259-260gitCurrentBranch() invokes git via PowerShell
    • src/os/linux.ts:230-231 — same with bash git
    • src/os/windows.ts:219-220, 226 — credential helper setup also uses bare git

    3. Best place to add worktree detection

    Recommended: Strategy Layer (src/services/strategy.ts)

    Add isWorktree() to the AgentStrategy interface (lines 15-23) and implement in both RemoteStrategy and LocalStrategy:

    • LocalStrategy: fs.statSync(path.join(workFolder, '.git')).isFile() — if .git is a file (not directory), it's a worktree
    • RemoteStrategy: Execute shell check over SSH: test -f <workFolder>/.git && echo true || echo false
    • Cache the result on the strategy instance to avoid repeated checks

    Then in src/os/windows.ts:259-260 and credential helper methods, use git.exe explicitly when worktree is detected. This is the root fix — bash-bundled git on Windows mishandles the .git file reference in worktrees.

    Secondary option: Add detection in execute-command.ts at line ~216 (before command wrapping), but this is higher in the stack and would need to be duplicated for execute-prompt.ts.

    4. Guard against agent writes to .git file

    Available hook points:

    Hook 1: execute-command.ts:130-170 (after credential token resolution)

    • Add regex check for commands writing to .git paths
    • Pattern already exists: lines 38-39, 58-60 check for raw sec:// handles — same approach works
    • Check: /\.git[\\\/]/ combined with write indicators (echo, Set-Content, tee, printf, >)

    Hook 2: strategy.ts:28-30 (SSH exec entry point in RemoteStrategy)

    • Add isDangerous(command) guard before executing via SSH
    • Pro: catches all commands regardless of entry point
    • Con: regex-based filtering has false positive risk (e.g. git -C repo/.git-mirror)

    Limitation: Cannot reliably block all shell-level operations (e.g. sh -c "echo x > .git/HEAD"). The real fix is using git.exe on 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 .git file protection, and add a note to the "Git as transport" section (lines 78-82):

    "Doers and reviewers NEVER modify .git files directly — all git operations must use the git CLI, which is automatically routed to git.exe on Windows to prevent worktree corruption."

    6. Implementation complexity estimate

    MEDIUM (~9.5 hours)

    Component Effort Notes
    Worktree detection in strategy layer 2h Add isWorktree() + caching in both strategy classes
    Force git.exe on Windows 1.5h Update gitCurrentBranch() + credential helpers in windows.ts
    Command filtering in execute-command.ts 1h Regex guard at line ~132, following existing sec:// pattern
    Strategy-level isDangerous() guard 2h Careful regex to avoid false positives
    Testing on Windows with real worktree 3h Non-trivial environment setup, cross-platform verification
    Documentation (doer-reviewer.md) 0.5h Add safeguard row + git transport note

    Why 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, .git is a file (containing gitdir: /path/to/parent/.git/worktrees/name) not a directory. When bash-bundled git runs in this context, it can mishandle the .git file reference. The fix is: detect worktree status, then enforce native git.exe for all git operations on Windows worktree members.

     

Log in to post a comment.

MongoDB Logo MongoDB