summaryrefslogtreecommitdiff
path: root/internal
diff options
context:
space:
mode:
Diffstat (limited to 'internal')
-rw-r--r--internal/executor/claude.go11
-rw-r--r--internal/executor/claude_test.go36
2 files changed, 45 insertions, 2 deletions
diff --git a/internal/executor/claude.go b/internal/executor/claude.go
index db4d0fa..4839a90 100644
--- a/internal/executor/claude.go
+++ b/internal/executor/claude.go
@@ -85,9 +85,16 @@ func (r *ClaudeRunner) Run(ctx context.Context, t *task.Task, e *storage.Executi
}
// Pre-assign session ID so we can resume after a BLOCKED state.
- // If this is a resume execution the session ID is already set.
+ // For resume executions, the claude session continues under the original
+ // session ID (the one passed to --resume). Using the new exec's own UUID
+ // would cause a second block-and-resume cycle to pass the wrong --resume
+ // argument.
if e.SessionID == "" {
- e.SessionID = e.ID // reuse execution UUID as session UUID (both are UUIDs)
+ if e.ResumeSessionID != "" {
+ e.SessionID = e.ResumeSessionID
+ } else {
+ e.SessionID = e.ID // reuse execution UUID as session UUID (both are UUIDs)
+ }
}
// For new (non-resume) executions with a project_dir, clone into a sandbox.
diff --git a/internal/executor/claude_test.go b/internal/executor/claude_test.go
index 1f95b4a..79f294e 100644
--- a/internal/executor/claude_test.go
+++ b/internal/executor/claude_test.go
@@ -227,6 +227,42 @@ func TestClaudeRunner_BuildArgs_PreambleBashNotDuplicated(t *testing.T) {
}
}
+// TestClaudeRunner_Run_ResumeSetsSessionIDFromResumeSession verifies that when a
+// resume execution is itself blocked again, the stored SessionID is the original
+// resumed session, not the new execution's own UUID. Without this, a second
+// block-and-resume cycle passes the wrong --resume session ID and fails.
+func TestClaudeRunner_Run_ResumeSetsSessionIDFromResumeSession(t *testing.T) {
+ logDir := t.TempDir()
+ r := &ClaudeRunner{
+ BinaryPath: "true", // exits 0, no output
+ Logger: slog.New(slog.NewTextHandler(io.Discard, nil)),
+ LogDir: logDir,
+ }
+ tk := &task.Task{
+ Agent: task.AgentConfig{
+ Type: "claude",
+ Instructions: "continue",
+ SkipPlanning: true,
+ },
+ }
+ exec := &storage.Execution{
+ ID: "resume-exec-uuid",
+ TaskID: "task-1",
+ ResumeSessionID: "original-session-uuid",
+ ResumeAnswer: "yes",
+ }
+
+ // Run completes successfully (binary is "true").
+ _ = r.Run(context.Background(), tk, exec)
+
+ // SessionID must be the original session (ResumeSessionID), not the new
+ // exec's own ID. If it were exec.ID, a second blocked-then-resumed cycle
+ // would use the wrong --resume argument and fail.
+ if exec.SessionID != "original-session-uuid" {
+ t.Errorf("SessionID after resume Run: want %q, got %q", "original-session-uuid", exec.SessionID)
+ }
+}
+
func TestClaudeRunner_Run_InaccessibleWorkingDir_ReturnsError(t *testing.T) {
r := &ClaudeRunner{
BinaryPath: "true", // would succeed if it ran