fix(cli): honor CLIDE_SOCK as an explicit target, fail loudly if dead (T-247)
The `clide` shell client only ever computed the socket path from the workspace's FNV-1a hash — it never read CLIDE_SOCK, so the var the app exports to spawned agents was a no-op and a bogus CLIDE_SOCK was silently ignored (it still hit the discovered instance). Now: when CLIDE_SOCK is set the client connects to it directly, beating discovery, and a dead socket aborts with EX_UNAVAILABLE rather than falling back to a different instance (the split-brain footgun). Unset → the deterministic per-workspace path (D-70) as before. The server keeps deterministic binding; this is a client-side targeting override only. Also fixes a pre-existing -Wstringop-truncation warning in find_workspace_root (strncpy+manual-null → snprintf). Tests: the e2e suite now clears the inherited CLIDE_SOCK so discovery tests stay hermetic (the suite may run inside a clide instance), plus two new tests — a valid CLIDE_SOCK pins from a non-git dir, a dead one fails loudly and returns no data. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+26
-11
@@ -183,8 +183,7 @@ static int find_workspace_root(const char *start, char *out, size_t out_size) {
|
||||
snprintf(probe, sizeof(probe), "%s/.git", cwd);
|
||||
struct stat st;
|
||||
if (lstat(probe, &st) == 0) {
|
||||
strncpy(out, cwd, out_size - 1);
|
||||
out[out_size - 1] = '\0';
|
||||
snprintf(out, out_size, "%s", cwd);
|
||||
return 0;
|
||||
}
|
||||
/* Climb one. /foo/bar -> /foo, / -> stop. */
|
||||
@@ -367,16 +366,32 @@ int main(int argc, char **argv) {
|
||||
return EX_USAGE;
|
||||
}
|
||||
|
||||
char ws_root[4096];
|
||||
if (find_workspace_root(NULL, ws_root, sizeof(ws_root)) != 0) {
|
||||
fprintf(stderr, "clide: not inside a git repository — no workspace to talk to\n");
|
||||
return EX_USAGE;
|
||||
}
|
||||
|
||||
/* CLIDE_SOCK is an explicit target that beats workspace discovery (T-247):
|
||||
* a spawned agent inherits the parent app's socket path here, and a human
|
||||
* can pin a specific instance. When it's set we connect to it and FAIL
|
||||
* LOUDLY if it's dead — never silently fall back to discovering a different
|
||||
* instance (that's the split-brain footgun this fixes). Unset → the
|
||||
* deterministic per-workspace path (D-70). */
|
||||
char sock_path[4096];
|
||||
if (socket_path_for(ws_root, sock_path, sizeof(sock_path)) >= (int)sizeof(sock_path)) {
|
||||
fprintf(stderr, "clide: socket path overflow\n");
|
||||
return EX_SOFTWARE;
|
||||
const char *env_sock = getenv("CLIDE_SOCK");
|
||||
if (env_sock && *env_sock) {
|
||||
if (strlen(env_sock) >= sizeof(sock_path)) {
|
||||
fprintf(stderr, "clide: CLIDE_SOCK path too long\n");
|
||||
return EX_USAGE;
|
||||
}
|
||||
strncpy(sock_path, env_sock, sizeof(sock_path) - 1);
|
||||
sock_path[sizeof(sock_path) - 1] = '\0';
|
||||
} else {
|
||||
char ws_root[4096];
|
||||
if (find_workspace_root(NULL, ws_root, sizeof(ws_root)) != 0) {
|
||||
fprintf(stderr, "clide: not inside a git repository — no workspace to talk to "
|
||||
"(set CLIDE_SOCK to target a specific instance)\n");
|
||||
return EX_USAGE;
|
||||
}
|
||||
if (socket_path_for(ws_root, sock_path, sizeof(sock_path)) >= (int)sizeof(sock_path)) {
|
||||
fprintf(stderr, "clide: socket path overflow\n");
|
||||
return EX_SOFTWARE;
|
||||
}
|
||||
}
|
||||
|
||||
sock_t fd = connect_unix(sock_path);
|
||||
|
||||
Reference in New Issue
Block a user