[BUG] Cowork sandbox FUSE mount serves stale inode for entire session — silently corrupts bash git commit; now also corrupts .git/config on every Windows-side gh pr merge

Resolved 💬 2 comments Opened May 27, 2026 by casey451 Closed May 31, 2026

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report (please file separate reports for different bugs)
  • [x] I am using the latest version of Claude Code

What's Wrong?

The FUSE bridge that mounts a Windows host folder into the Cowork Linux sandbox at /sessions/<id>/mnt/<folder>/ caches inode metadata indefinitely. Files modified on Windows after the mount is first read appear, to the sandbox bash environment, with their original size, mtime, and content for the entire session — no invalidation event arrives.

The wedge is selective: file content + stat are stale, directory listings can come back empty for files that exist, working-tree git walks return garbage, but ref-walking git commands stay clean. Cowork's first-class file tools (Read/Edit/Write) bypass FUSE and are authoritative, which is the only reason this bug hasn't caused data loss yet. Bash git add / git commit against the working tree silently commits the stale content.

Repro reliability: 100% on user casey.l.solomon@gmail.com's project C:\Users\casey\projects\havasu-chat across 10+ sessions over 3+ days (sessions v32, v33, v35, v36, v37, v42, v43 — every session boot).

Severity: Critical (P1, escalated from High). As of the v43 session (2026-05-27), every Windows-side gh pr merge --delete-branch corrupts .git/config from the sandbox side: first the file appears truncated on disk at session open, then re-corrupted (NUL-padded to the old size) after every subsequent Windows-side merge. Each occurrence requires a full sandbox-side rewrite of .git/config via cat > .git/config before any further git op (including a simple git fetch) will run. The bug now blocks the routine "merge a PR, then keep working" loop on every iteration, not just on session boot. Three config-rewrite cycles were required in a single session (one per merged PR). Full evidence in Appendix C of the Additional Information section.

Files affected: the FUSE mount that bridges the Windows host working directory into the Linux guest sandbox at /sessions/<session>/mnt/<folder>/. Both content reads and attribute (stat) reads return stale data; directory entries can also fail to appear; small parser-sensitive files like .git/config and .git/HEAD get truncated or NUL-padded on Windows-side shrinks.

What Should Happen?

Expected: Files modified on the Windows host should become visible to the sandbox bash environment within a short bounded window (or immediately, on the next syscall) — the same way ordinary native FS mounts behave. Files shrunk on the Windows host should appear shrunk to the sandbox, with no trailing NUL-padded bytes from the prior longer state.

Observed: Once the guest has read a file once, the FUSE view of that file's stat tuple (st_size, st_mtime, st_ctime) and its content stays frozen for the lifetime of the session, even though the same file on the Windows host is being modified normally and the Cowork in-process file tools (Read / Edit / Write) see the post-modification content correctly. When the host-side file is shrunk (e.g. gh pr merge --delete-branch removing a [branch "..."] section from .git/config), the sandbox-side view serves the valid prefix followed by stale page-cache trailing NULs padded to the old, larger file size — making the file unusable to any parser that errors on NUL bytes.

Error Messages/Logs

fatal: bad config line 71 in file .git/config

fatal: ambiguous argument 'HEAD': unknown revision or path not in the working tree.
Use '--' to separate paths from revisions, like this:
'git <command> [<revision>...] -- [<path>...]'

# Bash git status, in-sandbox, when FUSE is wedged (totally wrong — repo has hundreds of commits, working tree is clean on Windows):
On branch main
No commits yet
Changes to be committed:
  (use "git rm --cached <file>..." to unstage)
        new file:   .gitignore
        new file:   README.md
        ... ~1286 phantom A entries ...

# pip install attempt in mounted venv leaves undeletable .tmp dirs:
$ rm -rf .venv/Lib/site-packages/.pkg-abc123.tmp
rm: cannot remove '.venv/Lib/site-packages/.pkg-abc123.tmp/...': Operation not permitted
$ chmod -R u+w .venv/Lib/site-packages/.pkg-abc123.tmp
chmod: cannot access '.venv/Lib/site-packages/.pkg-abc123.tmp': No such file or directory

# Contradictory ls output (single invocation, dentry cache vs negative-dentry cache disagree):
$ ls -la .git/index.lock .git/index.stash.3.lock
ls: .git/index.lock: No such file or directory
ls: .git/index.stash.3.lock: No such file or directory
-rwx------ 1 pensive-gifted-rubin pensive-gifted-rubin 0 May 27 13:23 .git/index.lock
-rwx------ 1 pensive-gifted-rubin pensive-gifted-rubin 0 May 27 13:23 .git/index.stash.3.lock

Steps to Reproduce

Primary repro (stale stat / content):

On a Cowork Windows host with a folder mounted into a sandbox session:

  1. From the sandbox bash shell: stat <mounted-file> and record size + mtime.
  2. From Windows (Notepad, Cursor, Set-Content, or any native editor): modify the file and save. Confirm size + mtime have changed via Windows Get-Item / Explorer.
  3. From the sandbox bash shell: stat <mounted-file> again. Observed: size + mtime + cat content are unchanged from step 1, even though the file on the Windows host now matches step 2.
  4. Try every standard cache-invalidation move from the guest: ls -la on the parent dir, full cat, dd if=... bs=1, find -printf, sync. None refresh the FUSE view. The wedge persists for the lifetime of the session.
  5. From within the same session, the Cowork file tool Read on the same path returns the correct (post-step-2) content — confirming the file tools bypass FUSE entirely. This is the single fact that distinguishes "FUSE cache wedge" from "broken host filesystem."

Smallest possible one-liner repro:

# Guest (sandbox bash):
echo guest-before; stat -c '%n size=%s mtime=%y' /sessions/<id>/mnt/<folder>/probe.txt
# Host (Windows PowerShell, AFTER the guest stat above):
Set-Content C:\path\to\<folder>\probe.txt "host-updated-$(Get-Date -Format o)"
Get-Item C:\path\to\<folder>\probe.txt | Select-Object Length, LastWriteTimeUtc
# Guest again (same shell, no reconnect):
stat -c '%n size=%s mtime=%y' /sessions/<id>/mnt/<folder>/probe.txt
cat /sessions/<id>/mnt/<folder>/probe.txt

Expected: second guest stat reflects the host-side Set-Content.
Observed: second guest stat is byte-for-byte identical to the first; cat prints the pre-Set-Content body.

Secondary repro (NUL-padded shrink — the v43 escalation, single-test version for triage):

# 1. From sandbox bash, in a session mounted on a real git repo, confirm .git/config parses:
git -C /sessions/<id>/mnt/<folder> rev-parse HEAD

# 2. From Windows on the same folder, perform any operation that *shrinks* .git/config.
#    Easiest: `gh pr merge <num> --delete-branch` for any open PR.
#    Or: `git config --unset branch.<deleted-branch>.merge` after a manual delete.

# 3. From sandbox bash, in the SAME session, re-run:
git -C /sessions/<id>/mnt/<folder> rev-parse HEAD

Expected at step 3: same SHA as step 1.
Observed: fatal: bad config line N in file .git/config. xxd .git/config shows the valid shorter content followed by NUL bytes padded to the OLD file's length.

If step 3 reproduces the error, the same page-cache-tail bug described in the appendices is present and the report applies.

Claude Model

None

Is this a regression?

Yes, this worked in a previous version

Last Working Version

_No response_

Claude Code Version

N/A — this is a Cowork desktop sandbox issue, not Claude Code CLI. Cowork build approximately 1.8555.2.0 (no Cowork-specific issue form exists, so filing here per Lane B research showing recent Cowork issues #61682, #55879, #45883 used this same form).

Platform

Anthropic API

Operating System

Windows

Terminal/Shell

Other

Additional Information

Repro signature

Concrete data from session 2026-05-26 (one of many — full per-session evidence in the appendices):

mount entry:
  /mnt/.virtiofs-root/shared/c/Users/casey/projects/havasu-chat
    on /sessions/zealous-vigilant-thompson/mnt/havasu-chat
    type fuse (rw,nosuid,nodev,relatime,user_id=0,group_id=0,
               default_permissions,allow_other)

guest kernel: Linux 6.8.0-106-generic #106~22.04.1-Ubuntu
guest uid:    1077 (NOT root; /proc/sys/vm/drop_caches not writable)

For three files in the project (app/home/queries_c.py, app/home/router.py, app/static/styles/home_c.css):

| file | bash size | Windows size | bash mtime UTC | Windows mtime UTC |
|---|---:|---:|---|---|
| queries_c.py | 5,759 | 20,423 | 2026-05-25 17:13:13 | 2026-05-26 14:26:48 |
| router.py | 5,282 | 5,666 | 2026-05-25 21:22:15 | 2026-05-26 14:27:16 |
| home_c.css | 7,485 | 17,687 | 2026-05-25 03:30:44 | 2026-05-26 14:27:41 |

Bash sees size + mtime + content from ~21 hours ago. Windows is correct. The bash side has never been re-synced.

A separate file (.git/HEAD) was also stale: bash saw a 38-byte truncated text ...c-d4-services while Windows had a 44-byte ...c-d4-services-grid\n. This means partial-file staleness is also possible (likely the page-cache holding an earlier in-place overwrite truncation point).

Additional failure modes observed (sessions v33–v37)

The stale-inode cache produces a cluster of secondary failures whose common cause is the same. They're worth listing because they're the symptoms a user is most likely to notice before they realize FUSE is the culprit.

Working-tree git walks are poisoned, but ref-walks are clean. This asymmetry is the most important diagnostic signal.

  • git status (bash, in-sandbox) regularly claims "No commits yet" and reports every file in the working tree as A (staged-as-new), even when the branch is sitting at a real commit and the index is consistent on the Windows side.
  • git rev-parse HEAD (bash) fails with fatal: ambiguous argument 'HEAD', while git rev-parse refs/heads/<branch> in the same shell returns the correct SHA. Symbolic HEAD resolution reads .git/HEAD via FUSE and hits stale content; the ref-walk path reads packed-refs / loose refs and is fine.
  • git fetch, git push, git log, git show <object>, git for-each-ref, and git rev-parse refs/... all work correctly from bash.
  • git add / git commit / git diff <file> read working-tree content through FUSE → silently use stale bytes. This is the data-corruption path.

pip install in a FUSE-mounted venv leaves undeletable wheel extraction directories. When pip extracts a wheel into a site-packages/.<pkg>-<hash>.tmp/ staging directory on the FUSE mount, the rename-into-place step fails partway through and leaves the .tmp dir behind. Subsequent rm -rf from bash returns Operation not permitted even though the running user owns the files. Cleanup has to happen from the Windows side.

Directory listings appear empty for files that exist. A fresh session's first ls outputs/ (or any tool relying on a directory walk — Glob, find, shell completion) frequently returns nothing for a directory the Windows host shows as populated. Absolute-path Read on a known filename in the same directory returns the file's content correctly.

.git/HEAD truncation across detach/reattach. When the symbolic ref in .git/HEAD is rewritten on the Windows side to a longer branch name, the FUSE-side view sometimes serves the new content truncated to the old file's length. This implies the wedge is not just stat-cached but page-cached at a stale size, and that in-place overwrites that grow the file expose uninitialized or pre-existing tail bytes.

Why this is unsafe

Git tools that read the working tree go through the stale FUSE view:

  • git add reads file content via FUSE → stages stale content
  • git commit then commits that stale content silently
  • git diff <file> shows diff against stale content
  • git status -M flags files as "modified" based on stale checksums

This produces commits that are wrong, not commits that fail.

Git metadata ops are safe because they only touch .git/refs/*, .git/objects/*, and .git/HEAD-type files where staleness either (a) gets refreshed on directory listing in our experience, or (b) is self-evident (e.g. ref not found → fatal error rather than silent wrong commit). The v43 escalation breaks part of this safety: .git/config is also a small parser-sensitive file and now reliably gets NUL-padded on every Windows-side shrink.

Methodology

  1. Captured bash-side stat --printf on 3 known-stale files via the FUSE mount.
  2. Reporter ran a probe script (_phase4_stat_probe.cmd_phase4_stat_probe.ps1, using Get-Item) on Windows, native filesystem.
  3. Cross-compared size, mtime, line count, content prefix.
  4. Re-stat from bash after Windows-side reads completed — to check whether host-side opens trigger any FUSE invalidation. They did not.
  5. Tried bash-side invalidation: ls -la on parent dir, cat (full open + read), dd (direct byte read), find -printf (different stat syscall path). All returned the same stale tuple.
  6. Tried writing to /proc/sys/vm/drop_caches — not writable (uid 1077, not root). Even if it were, this is host-side userspace FUSE daemon caching, not guest page cache, so it wouldn't help.

Hypothesis

The mount line shows no explicit cache-control flags (attr_timeout, entry_timeout, noac, auto_cache, direct_io). Most FUSE setups default to infinite attribute caching, expecting the host-side daemon to actively emit fuse_lowlevel_notify_inval_* on host-side modifications.

The host-side daemon (bridging Windows ↔ guest Linux) appears not to be emitting those notifications. Once a file is cached, the guest serves it forever. Windows-side writes by Cursor, Notepad, Set-Content, or any other native Windows path do not propagate into the guest.

Best guesses where to look:

  • The virtiofs-over-FUSE daemon (host process, name unknown from guest) and its watcher integration on the Windows side. Does it use ReadDirectoryChangesW on the host folder and feed fuse_lowlevel_notify_inval_inode into the daemon? If not, this bug is expected.
  • Mount options. Adding attr_timeout=1,entry_timeout=1,auto_cache on the FUSE mount would at minimum bound the staleness to ~1 second per file, instead of unbounded.
  • noac would disable attribute caching entirely. Big perf hit but would eliminate the bug class.
  • The NUL-padded shrink case looks like an in-place page-cache overwrite where the host-side daemon updates content but doesn't communicate the new shorter length to the guest, leaving the trailing pages stale (and those pages happen to be NUL because the prior content's last few bytes were probably small ASCII that doesn't show up at the boundary, or because the daemon zero-fills).

Workarounds (current)

The pattern that emerges from 3+ days of use is "FUSE poisons working-tree walks; ref-walks are clean; file tools are authoritative."

  • File-content reads: use the Cowork file tools (Read / Write / Edit). These bypass FUSE entirely and reflect Windows reality. This is the single workaround that has held up every time.
  • Working-tree-content git ops (git add, git commit, git diff <file>, git rev-parse HEAD, anything that reads .git/HEAD or walks the working tree): do not use from bash. Run from Windows Cursor / cmd instead.
  • Git ref-walking ops (git fetch, git push, git rev-parse refs/heads/<branch>, git log, git branch, git show <object>, git for-each-ref): safe from bash.
  • git status: unsafe — frequently fabricates "No commits yet" with the entire tree staged-as-new. Treat its output as garbage until verified from Windows.
  • pip install in the mounted venv: do from Windows side, or expect to manually clean up .tmp directories from Windows after.
  • Directory listings (ls, find, Glob): if a directory looks empty, don't trust it — try absolute-path Read on a known filename before concluding the files aren't there.
  • .git/config after every Windows merge (v43-new): rewrite via cat > .git/config <<'EOF' ... EOF to force the sandbox to re-read. Keep the file minimal (only [core], [remote "origin"], [branch "main"], [user]) to reduce the surface for future shrinks.

Recommended fixes (ranked)

  1. Fix the host-side FUSE daemon to emit fuse_lowlevel_notify_inval_inode and fuse_lowlevel_notify_inval_entry when host-side writes / renames are detected. This is the right fix.
  2. Add bounded cache TTLs to the mount options (attr_timeout=1,entry_timeout=1,auto_cache) as a band-aid that bounds staleness to ~1s.
  3. Truncate-on-shrink correctness: whatever the host-side daemon does on a Windows-side file shrink, it has to invalidate (or zero-out-and-resize) trailing pages so the guest doesn't serve NUL-padded content with the old length. This is the v43-escalation fix.
  4. Document the issue in Cowork user docs at minimum, so users don't git commit stale content unaware.
  5. Add a sandbox-side health check at session boot: stat a well-known agent-test file from bash, compare to a value the desktop side knows, log a warning if they disagree. Bonus: also check wc -c .git/config from bash vs from Read and warn on mismatch.

Questions for maintainers

  1. Is the FUSE bridge expected to invalidate guest inode/page caches on host-side writes via fuse_lowlevel_notify_inval_* (or equivalent virtiofsd FUSE_NOTIFY_*)? If yes — is there a known condition under which those notifications stop being emitted (e.g. Windows-side anti-malware filter driver intercepting ReadDirectoryChangesW)?
  2. Are the current FUSE mount options (default_permissions, allow_other, no attr_timeout / entry_timeout) intentional, or should we expect auto_cache / a bounded TTL?
  3. Is there a supported way for the in-sandbox bash to force a cache invalidation that doesn't require root? drop_caches is not writable as uid 1077 and would be guest-side only anyway.
  4. Is there a known difference in this path between Hyper-V and WSL2 backends? We'd like to know whether switching backends is a reasonable user-side workaround.
  5. Should the Cowork Edit tool stop auto-creating .bak files for paths inside .git/? Three .git/config.bak* files accumulated in one session and are easy to miss.

Reporter / environment context

Confirmed from inside the sandbox:

  • Guest OS: Linux 6.8.0-106-generic #106~22.04.1-Ubuntu
  • Mount type: fuse over virtiofs host root (/mnt/.virtiofs-root/shared/c/Users/...)
  • Mount options: rw,nosuid,nodev,relatime,user_id=0,group_id=0, default_permissions,allow_other. No attr_timeout, entry_timeout, noac, auto_cache, or direct_io.
  • Guest uid: 1077 (non-root)
  • Host OS: Windows 11 Pro 10.0.26200 (Windows 11 25H2 generation)
  • Project on Windows host: C:\Users\casey\projects\havasu-chat
  • Repro reliability: 100% on this project across 10+ sessions (v32 → v43, span ~5 days, every session boot)
  • Example session IDs: zealous-vigilant-thompson, affectionate-dazzling-meitner, pensive-gifted-rubin, kind-ecstatic-planck

Reporter environment (additional):

  • Cowork / Claude desktop client version: approximately 1.8555.2.0 (recent build).
  • Sandbox runtime backend: mount line strongly suggests virtiofs in a Linux VM; reporter cannot confirm Hyper-V vs WSL2 vs custom virtiofsd from inside the sandbox.
  • Other projects exhibit the same wedge? untested. All evidence is from the havasu-chat folder. (Note: Defender-as-cause hypothesis was ruled out.)
  • Wedge persists across desktop-app restart? Strong circumstantial evidence yes (e.g. .git/index.lock from a session that ended 7 hours ago was still serving stale-existence cache to a fresh session) but not explicitly verified.

---

Appendix A — 2026-05-26 (sessions v32–v37): initial discovery, methodology, magnitude

(Material above; included here as the historical anchor for the pattern.)

Appendix B — 2026-05-27 session "pensive-gifted-rubin" (v42 intermediate): truncation generalizes beyond .git/HEAD

B.1 Contradictory ls output in a single syscall batch

ls -la .git/index.lock .git/index.stash.3.lock simultaneously reported:

ls: .git/index.lock: No such file or directory
ls: .git/index.stash.3.lock: No such file or directory
-rwx------ 1 pensive-gifted-rubin pensive-gifted-rubin 0 May 27 13:23 .git/index.lock
-rwx------ 1 pensive-gifted-rubin pensive-gifted-rubin 0 May 27 13:23 .git/index.stash.3.lock

Both arguments produced an ENOENT error message and a successful stat line in the same ls invocation. The dentry cache for the parent directory showed the entries as present (readdir cache hit) while per-path lookup reported them missing (lookup cache miss).

B.2 Selective syscall-path cache invalidation

After Windows-side del /F .git\index.lock .git\index.stash.3.lock, the following sandbox-side probes produced inconsistent answers within milliseconds of each other:

| syscall path | result |
|---|---|
| python3 os.listdir('.git') | locks NOT in returned list (correct — Windows deleted them) |
| python3 os.stat('.git/index.lock') | returns size=0 (stale — claims file exists) |
| python3 os.open(path, O_RDONLY) | ENOENT (correct — file is gone) |
| python3 os.unlink(path) | EPERM "Operation not permitted" (file appears present but unmodifiable) |
| chmod u+w .git/index.lock | "No such file or directory" |
| rm -f .git/index.lock | "Operation not permitted" |
| git open(O_CREAT \| O_EXCL) (via git reset) | EEXIST "File exists" |

Five different "is this file there?" answers from the same FUSE mount within a single second.

B.3 .git/config extension-with-null-bytes corruption (first sighting)

.git/config was rewritten by Cowork file tools during the session. After the write, subsequent reads showed the intended new content followed by 115 trailing null bytes on a fresh line, breaking git config parsing (fatal: bad config line N). The trailing nulls appeared to be page-cache tail bytes from a previous longer version of the file that the in-place overwrite didn't truncate.

B.4 .git/HEAD repeated re-corruption mid-session

.git/HEAD was corrupted three separate times during this session, each time during read-only investigation work where no part of the session was issuing branch switches or commits:

  1. Truncated to ref: refs/heads/main + 30 trailing nulls
  2. Truncated to ref: refs/heads/fix/t (mid-string truncation of fix/tier2-formatter-cache-raw, no trailing newline)
  3. Same truncation pattern reappeared after manual repair

B.5 Lock-file removal denial despite confirmed Windows deletion

.git/index.lock was 7 hours stale (a crash artifact from the prior session's git operation). The sandbox refused to remove it through every available mechanism with conflicting error messages (EPERM vs ENOENT). Windows-side del /F removed both lock files instantly without error. The sandbox-side dentry cache then continued reporting them present for the rest of the session. Strong circumstantial evidence the wedge survives session boundaries within a single desktop-app lifetime.

B.6 Mid-session staged revert of an already-merged PR

While the session was performing read-only investigation, the git index silently acquired a staged change set equivalent to a complete revert of a previously-merged PR. Both index and working tree had been mutated. No part of this session issued git revert or git checkout <commit> -- <file>. Likely source: another concurrent Cowork session on the same Windows folder, or an IDE-side rebase/revert. Implication: any commit performed from inside a Cowork session on this folder must verify the staged delta before committing.

Appendix C — 2026-05-27 v43 session: .git/config truncated on disk, re-corrupted on every Windows-side gh pr merge (severity-escalating evidence)

C.1 .git/config truncated on disk at session open (byte 2101, mid-line)

At session open, .git/config was 2101 bytes ending mid-line with [branch "fix/tier2 — no closing ", no closing ], no newline, no key lines for that section. Verified via wc -c, tail -c 30 | xxd, and dd if=.git/config bs=1 skip=2090 count=20. This is not a FUSE view artifact — the Cowork in-process Read tool (which bypasses FUSE) showed the full prior content, suggesting Cowork's Read path serves from a snapshot or a separate mirror, not the same on-disk bytes the bash side now sees. Git's response: fatal: bad config line 71 in file .git/config. No git operation in the sandbox would run.

C.2 printf >> .git/config produced wrong content

First repair attempt was a printf '...' >> .git/config append. Result: the previous section's header [branch "fix/tier2 was lost entirely from the post-append file — the append appears to have started writing before the truncation point, overwriting the last good section header. Workaround: rewrite the whole file via cat > .git/config << 'EOF'.

C.3 Re-corrupted after every Windows-side merge in the same session

After config was repaired, the session merged two PRs. Each merge re-corrupted the file the same way:

| step | command (Windows-side) | result (sandbox-side, next bash) |
|---|---|---|
| Initial | — | .git/config truncated on disk at byte 2101 (C.1) |
| Repair 1 | cat > .git/config <<EOF ... EOF | valid 4-section config restored |
| Merge | gh pr merge 18 --squash --delete-branch from Windows | next sandbox git fetchfatal: bad config line N. Sandbox view: valid prefix + NUL padding to OLD file size. Windows-side Get-Content showed the correct shorter content. |
| Repair 2 | rewrite via cat > .git/config | valid config, no stale sections |
| Merge | gh pr merge 17 --squash --delete-branch from Windows | identical pattern: NUL-padded view, fatal: bad config line N |
| Repair 3 | rewrite | valid 4-section minimal config |

The merge-poisons-config loop. Each Windows-side gh pr merge --delete-branch shrinks .git/config (it removes the deleted branch's [branch "..."] section), and the FUSE sandbox view serves the valid prefix followed by stale page-cache tail bytes (which happen to be NULs) padded to the old, longer file size. The git config parser sees the NULs as an invalid line and aborts.

Pattern generalizes: any FUSE-mounted file overwritten with shorter content from Windows exposes stale-page-cache trailing NULs through the sandbox view, and when the file is a parser-sensitive config (.git/config, .git/HEAD, likely any .ini / .toml / JSON), the file becomes unusable until a sandbox-side rewrite forces the cache to re-read.

C.4 .git/config.bak* files left in .git/ (Cowork Edit tool fallback)

When the Cowork Edit tool was used to repair .git/config (rather than bash cat >), each invocation left a .git/config.bak-<date> file behind. The session ended with three such files in .git/. Suggests the Edit tool auto-backups any edit it makes; for .git/-internal files this is probably undesired.

C.5 Minimal .git/config as a workaround that survives merges better

The v43 session ended with .git/config reduced to only four sections: [core], [remote "origin"], [branch "main"], [user]. This survives Windows-side merges better — because there's nothing for gh pr merge --delete-branch to remove, the file size doesn't shrink, and the NUL-padding window in C.3 doesn't open. Not a permanent fix: any new tracked branch will trigger another section append from Windows-side git.

C.6 Implications for severity

Pre-v43, the workaround "use Cowork file tools for content, bash only for ref-walk" was sufficient. v43 demonstrates that's no longer sufficient: .git/config is required for any git operation, and it's now being corrupted reliably on every merge. Escalation path:

  1. v32–v37: stale-inode wedge → silent wrong commit (data corruption, rare in practice with file-tool workaround).
  2. v42 (pensive-gifted-rubin): truncation generalizes beyond .git/HEAD to .git/config; six new failure modes (Appendix B).
  3. v43: every Windows-side merge re-corrupts .git/config. Sandbox-side git is unusable without a per-merge rewrite ritual. This is the workflow-blocking step.

C.7 Most useful single test for a maintainer

(Already included as the secondary repro in the Steps to Reproduce section above.)

---

Artifacts

Available for investigation handoff in the reporter's outputs/ folder:

  • _phase4_stat_probe.cmd / _phase4_stat_probe.ps1 / _phase4_stat_probe_result.txt — the probe launcher, PowerShell logic, and Windows-side stat output.
  • phase4_rca_findings_2026-05-26.md — full RCA + matrix + per-attempted-mitigation result.
  • session_handoff_2026-05-26_v33.md §4 — prior-session reproduction data and the original Defender hypothesis ruled out.
  • session_handoff_2026-05-27_v42_entity_matched_fixed.md — v42 intermediate session, source material for Appendix B.
  • session_handoff_2026-05-27_v43_pr17_pr18_landed.md §2.C — v43 session, source material for Appendix C.
  • boot_prompt_v44.md — the verbatim cat > .git/config recovery script that subsequent sandbox sessions need to run as a defensive boot step.

Happy to share any of these directly if a maintainer would find them useful.

View original on GitHub ↗

This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗