The version check compared Version strings, but during development both
client and server report "devel" — so stale servers were never restarted.
BuildID is now derived from the executable's modification time, which
changes on every recompilation (including go run). The server exposes it
via /v1/version and the client checks both Version and BuildID match.
Assisted-by: Crush:AWS Claude Opus 4.6
When two clients started up at the same time they would both try to
spawn a server and one would lose the bind race, leaving a confusing
log behind. Take an exclusive file lock around the spawn-and-wait
sequence, re-check health after acquiring the lock, and skip the spawn
entirely if a peer client has already brought the server up. The lock
is released as soon as the new server is ready.
Co-Authored-By: Charm Crush <crush@charm.land>
Two related gaps in the client startup path. After a version mismatch we
were shutting the old server down and then never starting a new one,
leaving the next caller to time out against a missing socket. And any
unexpected error from inspecting the socket path (something other than
"not found") was silently ignored, so we'd fall straight into the same
timeout. Both paths now converge on "needs (re)start" and we try to
clean up any stale file in the way before spawning.
Co-Authored-By: Charm Crush <crush@charm.land>
The parent process was creating the detached server via a context-bound
command, so when the parent exited or its context was cancelled the
runtime would also kill the brand new server we had just spawned. Drop
the context binding and rely on the existing platform-specific detach
to give the server a life of its own.
Co-Authored-By: Charm Crush <crush@charm.land>
Cold starts of CRUSH_CLIENT_SERVER=1 frequently failed with a stat error
because the spawned server creates the socket file as the last step of
initialization, well after the parent had given up. Replace the stat
polling with a real HTTP probe against /v1/health, treat any 2xx as
ready, and make the total budget overridable via CRUSH_SERVER_READY_TIMEOUT.
Drop the now-redundant CreateWorkspace retry loop.
Co-Authored-By: Charm Crush <crush@charm.land>
Having a `.crush/.gitignore` showing on `git diff` just because you
started Crush on a directory is annoying.
Having the file there is enough. We don't want to commit it.
* refactor(config): introduce ConfigStore and Scope for better config management
This makes config.Config immutable and introduces a ConfigStore that
manages the config and provides helper methods for accessing config
values with proper scoping (global, workspace). This allows us to avoid
passing around mutable config objects and ensures that all parts of the
code are accessing the most up-to-date config values. It also lays the
groundwork for future features like per-workspace config overrides.
* fixt: lint