summaryrefslogtreecommitdiff
path: root/internal/executor/executor.go
diff options
context:
space:
mode:
authorPeter Stone <thepeterstone@gmail.com>2026-03-08 22:24:12 +0000
committerPeter Stone <thepeterstone@gmail.com>2026-03-08 22:24:12 +0000
commitf135ab89ce6710a4f20049e6d0d8e914d8e2e402 (patch)
tree67b492274a239a1e0d4fc579a2daf3a70cb9be65 /internal/executor/executor.go
parentfab59cf8e669fe9ec34b30586f07b7478e897c31 (diff)
executor: fix sandbox git fetch + inject prior failure history
Fix: use file:// prefix in git fetch during sandbox teardown to force pack-protocol transfer. The local optimization uses hard links which fail across devices and with mixed-owner object stores. Feature: before running a task, query prior failed/timed-out executions and prepend their error messages to the agent's --append-system-prompt. This tells the agent what went wrong in previous attempts so it doesn't repeat the same mistakes. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat (limited to 'internal/executor/executor.go')
-rw-r--r--internal/executor/executor.go42
1 files changed, 42 insertions, 0 deletions
diff --git a/internal/executor/executor.go b/internal/executor/executor.go
index d1c8e72..df222f8 100644
--- a/internal/executor/executor.go
+++ b/internal/executor/executor.go
@@ -6,6 +6,7 @@ import (
"fmt"
"log/slog"
"path/filepath"
+ "strings"
"sync"
"time"
@@ -416,6 +417,10 @@ func (p *Pool) execute(ctx context.Context, t *task.Task) {
p.mu.Unlock()
}()
+ // Inject prior failure history so the agent knows what went wrong before.
+ priorExecs, priorErr := p.store.ListExecutions(t.ID)
+ t = withFailureHistory(t, priorExecs, priorErr)
+
// Run the task.
err = runner.Run(ctx, t, exec)
exec.EndTime = time.Now().UTC()
@@ -478,6 +483,43 @@ var terminalFailureStates = map[task.State]bool{
task.StateBudgetExceeded: true,
}
+// withFailureHistory returns a shallow copy of t with prior failed execution
+// error messages prepended to SystemPromptAppend so the agent knows what went
+// wrong in previous attempts.
+func withFailureHistory(t *task.Task, execs []*storage.Execution, err error) *task.Task {
+ if err != nil || len(execs) == 0 {
+ return t
+ }
+
+ var failures []storage.Execution
+ for _, e := range execs {
+ if (e.Status == "FAILED" || e.Status == "TIMED_OUT") && e.ErrorMsg != "" {
+ failures = append(failures, *e)
+ }
+ }
+ if len(failures) == 0 {
+ return t
+ }
+
+ var sb strings.Builder
+ sb.WriteString("## Prior Attempt History\n\n")
+ sb.WriteString("This task has failed before. Do not repeat the same mistakes.\n\n")
+ for i, f := range failures {
+ fmt.Fprintf(&sb, "**Attempt %d** (%s) — %s:\n%s\n\n",
+ i+1, f.StartTime.Format("2006-01-02 15:04 UTC"), f.Status, f.ErrorMsg)
+ }
+ sb.WriteString("---\n\n")
+
+ copy := *t
+ copy.Agent = t.Agent
+ if copy.Agent.SystemPromptAppend != "" {
+ copy.Agent.SystemPromptAppend = sb.String() + copy.Agent.SystemPromptAppend
+ } else {
+ copy.Agent.SystemPromptAppend = sb.String()
+ }
+ return &copy
+}
+
// waitForDependencies polls storage until all tasks in t.DependsOn reach COMPLETED,
// or until a dependency enters a terminal failure state or the context is cancelled.
func (p *Pool) waitForDependencies(ctx context.Context, t *task.Task) error {