Description
When two /browse CLI invocations start at the same time for the same project and no browse server is already running, they can race during bootstrap and one startup fails.
I hit this while using the compiled browse binary and launching two commands in parallel. One command recovered; the other failed with:
[browse] Server connection lost. Restarting...
[browse] Server failed to start:
[browse] Failed to start: ENOENT: no such file or directory, rename '/Users/test/.gstack/browse.json.tmp' -> '/Users/test/.gstack/browse.json'
Steps to reproduce
- Ensure there is no active browse server and no existing state file for the current project.
- From the same working directory, launch two browse commands concurrently before the server is up.
- Example shape:
- one command:
"$B" snapshot -i
- second command:
"$B" text
- Observe that both clients try to bootstrap the same browse server.
Observed behavior
One startup path can fail with ENOENT while renaming the shared temp file to the final state file.
Expected behavior
Concurrent bootstrap should converge on a single healthy server for that project. The second client should wait, reuse the published state, or otherwise avoid failing during startup.
Suspected root cause
The CLI has no startup lock around ensureServer() / startServer(), so multiple clients can decide to start a server at once:
browse/src/cli.ts#L163-L240
The server writes state via a shared temp path and then renames it:
browse/src/server.ts#L347-L358
If two servers use the same .tmp path, one can rename it away before the other reaches renameSync.
Why this matters
The project explicitly emphasizes multi-session parallel work in the README (## 10-15 parallel sprints), which makes race-safe browse startup part of the core product promise rather than just an edge case.
Acceptance criteria
- Two concurrent browse invocations in the same project no longer fail during bootstrap.
- Startup uses either a lock or uniquely named temp files plus safe publish semantics.
- A regression test covers parallel bootstrap.
- Existing stale-state restart behavior still passes.
Additional context
I did not find an existing issue for this exact failure mode, and I also did not find an issue template in the repo.
Description
When two
/browseCLI invocations start at the same time for the same project and no browse server is already running, they can race during bootstrap and one startup fails.I hit this while using the compiled browse binary and launching two commands in parallel. One command recovered; the other failed with:
Steps to reproduce
"$B" snapshot -i"$B" textObserved behavior
One startup path can fail with
ENOENTwhile renaming the shared temp file to the final state file.Expected behavior
Concurrent bootstrap should converge on a single healthy server for that project. The second client should wait, reuse the published state, or otherwise avoid failing during startup.
Suspected root cause
The CLI has no startup lock around
ensureServer()/startServer(), so multiple clients can decide to start a server at once:browse/src/cli.ts#L163-L240The server writes state via a shared temp path and then renames it:
browse/src/server.ts#L347-L358If two servers use the same
.tmppath, one can rename it away before the other reachesrenameSync.Why this matters
The project explicitly emphasizes multi-session parallel work in the README (
## 10-15 parallel sprints), which makes race-safe browse startup part of the core product promise rather than just an edge case.Acceptance criteria
Additional context
I did not find an existing issue for this exact failure mode, and I also did not find an issue template in the repo.