From 3962597950421e422b6e1ce57764550f5600ded6 Mon Sep 17 00:00:00 2001 From: Peter Stone Date: Tue, 3 Mar 2026 21:22:30 +0000 Subject: Fix working_dir failures: validate path early, remove hardcoded /root executor/claude.go: stat working_dir before cmd.Start() so a missing or inaccessible directory surfaces as a clear error ("working_dir \"/bad/path\": no such file or directory") rather than an opaque chdir failure wrapped in "starting claude". api/elaborate.go: replace the hardcoded /root/workspace/claudomator path with buildElaboratePrompt(workDir) which injects the server's actual working directory (from os.Getwd() at startup). Empty workDir tells the model to leave working_dir blank. Co-Authored-By: Claude Sonnet 4.6 --- internal/executor/claude.go | 3 +++ internal/executor/claude_test.go | 28 ++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+) (limited to 'internal/executor') diff --git a/internal/executor/claude.go b/internal/executor/claude.go index 8486427..7b3884c 100644 --- a/internal/executor/claude.go +++ b/internal/executor/claude.go @@ -40,6 +40,9 @@ func (r *ClaudeRunner) Run(ctx context.Context, t *task.Task, e *storage.Executi "CLAUDOMATOR_TASK_ID="+t.ID, ) if t.Claude.WorkingDir != "" { + if _, err := os.Stat(t.Claude.WorkingDir); err != nil { + return fmt.Errorf("working_dir %q: %w", t.Claude.WorkingDir, err) + } cmd.Dir = t.Claude.WorkingDir } diff --git a/internal/executor/claude_test.go b/internal/executor/claude_test.go index fa81b09..ac6aabf 100644 --- a/internal/executor/claude_test.go +++ b/internal/executor/claude_test.go @@ -1,9 +1,13 @@ package executor import ( + "context" + "io" + "log/slog" "strings" "testing" + "github.com/thepeterstone/claudomator/internal/storage" "github.com/thepeterstone/claudomator/internal/task" ) @@ -166,6 +170,30 @@ func TestClaudeRunner_BuildArgs_PreambleBashNotDuplicated(t *testing.T) { } } +func TestClaudeRunner_Run_InaccessibleWorkingDir_ReturnsError(t *testing.T) { + r := &ClaudeRunner{ + BinaryPath: "true", // would succeed if it ran + Logger: slog.New(slog.NewTextHandler(io.Discard, nil)), + LogDir: t.TempDir(), + } + tk := &task.Task{ + Claude: task.ClaudeConfig{ + WorkingDir: "/nonexistent/path/does/not/exist", + SkipPlanning: true, + }, + } + exec := &storage.Execution{ID: "test-exec"} + + err := r.Run(context.Background(), tk, exec) + + if err == nil { + t.Fatal("expected error for inaccessible working_dir, got nil") + } + if !strings.Contains(err.Error(), "working_dir") { + t.Errorf("expected 'working_dir' in error, got: %v", err) + } +} + func TestClaudeRunner_BinaryPath_Default(t *testing.T) { r := &ClaudeRunner{} if r.binaryPath() != "claude" { -- cgit v1.2.3