anthropics / anthropics/claude-code
Windows/Git Bash: 85 per-function base64 evals make every Bash call pay ~5-9s; snapshot-creation 10s timeout silently forks sessions into fast/slow regimes
- Langage dominant
- Python
- Étoiles
- 145k
- Forks
- 23.1k
- Métriques de merge des PR
- Métriques de PR en attente
Description
## Summary
On Windows + Git for Windows (MSYS bash), the Bash tool's shell-snapshot mechanism adds **~5–9 seconds to every Bash tool call**, and the snapshot-*creation* path has a timeout asymmetry that silently splits sessions into two performance regimes. Two coupled problems, both measured:
1. **Per-call cost:** the snapshot stores every captured shell function as its own `eval "$(echo '' | base64 -d)" > /dev/null 2>&1` line. On a machine whose login shell defines 145 functions (stock Git-for-Windows `git-completion.bash` — 144 of the 145), that is **85 base64-eval lines**, each paying an MSYS subshell fork (~16 ms) plus an external `base64` exec (~65 ms with real-time AV on). Re-sourced on **every** Bash tool call.
2. **Creation-timeout roulette:** snapshot creation runs the login shell under a hard **10,000 ms** timeout, while creation itself takes **~6 s idle** on this class of machine. Under load it times out → the session permanently falls back to per-call `bash -c -l` (the `-l` is added only when no snapshot was captured) — which is *faster* (~1.3 s/call) than snapshot mode (~6.5 s/call). Sessions randomly land in one regime at their first Bash call and stay there. A timed-out creation can still leave the snapshot file on disk, which makes forensics confusing (file present, never sourced).
## Environment
- Windows 11 Pro, Git for Windows (stock `/etc/profile.d/git-prompt.sh` → git-completion), Windows Defender real-time on
- Claude Code 2.1.237 (desktop app and CLI; behavior identical), snapshot files `~/.claude/shell-snapshots/snapshot-bash-*.sh`, 88,199 bytes / 85 evals each
## Measurements
Sourcing cost isolated (same snapshot, same machine, idle):
```
source full snapshot (85 evals) : 5,703 – 6,510 ms
same file, eval lines stripped : 88 ms <- 98.5% of cost is the eval lines
85 × $(:) fork-only control : 1,393 ms <- pure MSYS fork floor, AV-independent
85 × $(echo|base64 -d) : 6,853 ms (~81 ms/eval)
```
Corpus evidence (stream-parsed 9,473 clean serial Bash tool_use→tool_result deltas across 110 session transcripts / 35 days, one machine):
- Sessions in snapshot mode: floors 6.2–8.4 s, medians 8–15 s under load
- Sessions in `-l` fallback mode: floors 972–1,300 ms
- 39 of 83 sessions (n≥5) sat under a 2 s floor — i.e. **the busier the machine, the more sessions accidentally escape the tax**, which makes the cost look nondeterministic and very hard for users to attribute
- First call of a session ≈ 2× median (creation) — consistent with the 10 s deadline being marginal, not comfortable
## Suggested fixes (either helps; both are small)
1. **Batch the decode**: store all function bodies in one base64 blob (or plain `declare -f` text — which #50733 already requests for auditability) and decode once. Turns 85 forks+execs into ~1. Expected: ~6 s → well under 200 ms per call on this machine class.
2. **Rationalize the creation timeout**: either raise/scale it, or make the outcome consistent — right now timing out yields a *faster* session than succeeding, which is exactly backwards; and the partially-written snapshot file should be removed on timeout so file-presence matches behavior.
## Workaround for affected users (validated)
Git for Windows' `/etc/profile.d/git-prompt.sh` sources `~/.config/git/git-prompt.sh` *instead of* its completion-loading block when that file exists. A user-level override that replicates the stock prompt but skips `git-completion.bash` when `CLAUDECODE=1` is set (Claude Code sets it on the snapshot-creation shell) drops the snapshot to ~1 eval / 4.9 KB with the interactive prompt byte-identical. After deploying it: per-call floors converge to ~1.0–1.3 s in every new session. Happy to share the override file in a comment if useful.
Guide de contribution
Aucun guide de contribution indexé pour ce dépôt
Piste de recherche
Start by locating the Bash shell-snapshot creation and per-call sourcing paths that produce ~/.claude/shell-snapshots/snapshot-bash-*.sh, then reproduce the timing behavior on Windows with Git Bash. Compare batched decoding and timeout handling, including cleanup of partially written snapshots; done means the per-call cost is reduced and timeout and successful-creation sessions behave consistently.
Rédigé par le modèle d'indexation à partir du texte de l'issue.
Évaluation
- Stack technique
- bash, git, python
- Domaine
- cli, operating-systems, performance
- Type d'issue
- Bug
- Difficulté
- 4/5
- Temps estimé
- 3-5 jours
- Activité
- Active
- Clarté
- Plutôt claire
- Accessibilité débutants
- 48/100