JetBrains / JetBrains/thinkrail
The host's own git reads re-arm the repo-metadata watcher: a self-sustaining `fsChanged` loop spawning ~45 git processes/s until Bun crashes
- Dominant language
- TypeScript
- Stars
- 467
- Forks
- 37
- Avg merge
- 1d 56m
- Merged PRs (30d)
- 141
Description
## Description
The host's own read-only git calls can re-arm the repo-metadata watcher, and every arming makes every client re-read the workspace with a fan-out of git subprocesses. While something keeps `.git/index` racy (an agent editing files in a terminal, for example), the cycle sustains itself at the watcher's cadence: `git status` / `git diff` rewrite the index → the unfiltered git-dir watcher fires → 300 ms later a pathless `fsChanged` is published → clients re-read (Changes panel `git status` + diff, projects rail `workspace.list` = one `git diff --shortstat` **per workspace**, `refreshUserOwnedWorkspace`) → `git status` / `git diff` rewrite the index again.
In the desktop app this ended in a crash of the bundled Bun. The crash itself is a Bun 1.3.14 bug (a segfault inside the runtime), but what tripped it was the churn: Bun's own diagnostics counted **24,455 subprocess spawns in 539 s** (~45/s) before it died.
Every ingredient is on `main`:
- `packages/server/src/watch/watch.ts` — the git-dir watcher is non-recursive and deliberately unfiltered, so an `index` rewrite counts as a metadata change (the spec explains why paths cannot be matched reliably; the trade-off is that a status refresh looks like a commit).
- `packages/server/src/host/server.ts` — `setRepoMetaPublisher` runs `refreshUserOwnedWorkspace` and then publishes a pathless `fsChanged`, which makes every client re-read.
- `packages/server/src/workspaces/workspaces.ts` — `workspace.list` runs `git diff --shortstat` once per workspace on every re-read.
- `packages/server/src/git/gitExec.ts` — `nonInteractiveGitEnv` sets only `GIT_TERMINAL_PROMPT=0`; nothing stops `status` / `diff` from opportunistically rewriting `.git/index`.
## Steps to reproduce
1. Open a project with several workspaces (so the rail has several rows to compute diff stats for) and expand the Files tree and the Changes panel.
2. Keep the worktree busy for a few minutes — e.g. run Claude Code or any tool that edits files in a terminal tab.
3. Watch the host's subprocess rate (`ps` / `fs_usage`, or note `spawn(N)` in Bun's crash banner). On macOS 26 with the desktop build, the bundled Bun 1.3.14 eventually crashes.
## Expected behavior
A read-only git command issued by the host must not look like a repository change to the host, and one file-system change batch should cost a bounded, small number of subprocesses.
## Actual behavior
~45 git subprocesses per second sustained for the length of the session, then:
```
Bun v1.3.14 (0d9b296a) macOS Silicon
macOS v26.6.2
Features: ... spawn(24455) ... workers_spawned
Elapsed: 539272ms | User: 27580ms | Sys: 16725ms
RSS: 0.24GB | Peak: 0.49GB
panic: Segmentation fault at address 0x10
oh no: Bun has crashed. This indicates a bug in Bun, not your code.
https://bun.report/1.3.14/Ma10d9b296iSuhooC_25u4oB25u4oB2/6yfu5mlxB+tqwHmvinlB__A2AgB
Child process terminated by signal: 5
```
The macOS crash report (`bun-2026-09-07-075544.ips`) shows `EXC_BREAKPOINT` / `SIGTRAP` on the `Worker` thread — which, under Electrobun, is the thread that runs the whole host (`new Worker(appEntrypointPath)` in `electrobun/dist/main.js`).
Suggested fix, smallest first:
1. Add `GIT_OPTIONAL_LOCKS=0` to `nonInteractiveGitEnv` (git then skips the opportunistic index refresh for read-only commands), so the host's own reads can never re-arm the metadata nudge. One line plus a spec note.
2. Consider computing rail diff stats less eagerly than once per workspace per `fsChanged`.
3. Separately, this is a Bun 1.3.14 bug; an Electrobun release bundling a newer Bun would remove the crash even if the churn stays.
## Environment
- OS: macOS 26.6.2 (Apple Silicon)
- Bun version (`bun --version`): 1.4.0 on the machine; the desktop app bundles Electrobun 1.18.1's custom Bun 1.3.14, which is what crashed
- Node version (`node --version`): v26.8.1
- ThinkRail version / commit: `main` at a3b91d7e (2026-09-06); observed on a fork branch that adds a `git check-ignore` per directory listing, which only makes each turn of the cycle more expensive — the cycle itself is unchanged from `main`
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Contributor guide
Research direction
Start in packages/server/src/git/gitExec.ts and read nonInteractiveGitEnv, then review the watcher behavior in packages/server/src/watch/watch.ts and the related spec note. The smallest completion criterion is that host-issued read-only git commands no longer re-arm repository metadata watching; use the issue's reproduction to verify subprocess churn is bounded.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- bun, git, typescript
- Domain
- backend, devtools, performance
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 72/100