From 16110bec8b6ece5837560fb626ab30d8cdec0e81 Mon Sep 17 00:00:00 2001 From: Peter Stone Date: Wed, 3 Jun 2026 23:04:40 +0000 Subject: fix: embed execution log in checker instructions and add noopChannel to tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The checker task previously had no context about what the agent did — it tried to reach the API (potentially unavailable), search /home for artifacts, and clone the repo, all of which fail for LocalRunner tasks. Now spawnCheckerTask reads the tail of the execution's stdout.log and embeds it directly in the checker's instructions so the checker can verify output without needing Docker, API access, or repo cloning. Also fixes container_test: adds noopChannel to satisfy the AgentChannel parameter added by the OSS Runner interface. Co-Authored-By: Claude Sonnet 4.6 --- internal/executor/executor.go | 27 +++++++++++++++++++-------- internal/executor/summary.go | 1 + 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/internal/executor/executor.go b/internal/executor/executor.go index 8614481..2f01b93 100644 --- a/internal/executor/executor.go +++ b/internal/executor/executor.go @@ -505,7 +505,7 @@ func (p *Pool) handleRunResult(ctx context.Context, t *task.Task, exec *storage. if err := p.store.UpdateTaskState(t.ID, task.StateReady); err != nil { p.logger.Error("failed to update task state", "taskID", t.ID, "state", task.StateReady, "error", err) } - go p.spawnCheckerTask(context.Background(), t) + go p.spawnCheckerTask(context.Background(), t, exec) } } else { exec.Status = "COMPLETED" @@ -629,7 +629,7 @@ func (p *Pool) ShipStory(ctx context.Context, storyID string) error { // spawnCheckerTask creates and submits a checker task for the given completed task. // Guards: not called for subtasks, checker tasks, tasks without a repository URL, // or tasks that already have a checker. -func (p *Pool) spawnCheckerTask(ctx context.Context, checked *task.Task) { +func (p *Pool) spawnCheckerTask(ctx context.Context, checked *task.Task, exec *storage.Execution) { // Never spawn a checker for subtasks, checker tasks, or tasks without a repository. if checked.ParentTaskID != "" || checked.CheckerForTaskID != "" || checked.RepositoryURL == "" { return @@ -649,6 +649,15 @@ func (p *Pool) spawnCheckerTask(ctx context.Context, checked *task.Task) { criteria = checked.Agent.Instructions } + // Embed the execution log so the checker can verify output without needing + // Docker, API access, or repository cloning. + logSnippet := "" + if exec != nil && exec.StdoutPath != "" { + logSnippet = "\n\nExecution log (last 200 lines):\n\n" + + tailFile(exec.StdoutPath, 200) + + "\n" + } + instructions := fmt.Sprintf(`You are validating a completed task. Do not make any changes to the code or repository. Task: %s @@ -656,15 +665,17 @@ Instructions given to the implementor: %s Acceptance criteria: -%s +%s%s Steps: -1. Clone the repository and review the changes made. -2. Verify each acceptance criterion is met. Run tests or make HTTP requests as needed. -3. If all criteria are satisfied, exit normally (success). -4. If any criterion is not met, use the Bash tool to exit with a non-zero code: +1. Review the execution log above to determine what the agent did. +2. If the task involved code changes, use Read/Grep/Glob to inspect the repository at %s. +3. Verify each acceptance criterion is met. Run tests or make HTTP requests as needed. +4. If all criteria are satisfied, exit normally (success). +5. If any criterion is not met, use the Bash tool to exit with a non-zero code: bash -c "exit 1" - Before exiting, write a brief summary of what failed.`, checked.Name, checked.Agent.Instructions, criteria) + Before exiting, write a brief summary of what failed.`, + checked.Name, checked.Agent.Instructions, criteria, logSnippet, checked.RepositoryURL) now := time.Now().UTC() checker := &task.Task{ diff --git a/internal/executor/summary.go b/internal/executor/summary.go index bcf5cfd..e5a1772 100644 --- a/internal/executor/summary.go +++ b/internal/executor/summary.go @@ -12,6 +12,7 @@ import ( "github.com/thepeterstone/claudomator/internal/llm" ) + // synthesizeSummaryMaxBytes caps how much of the stdout log we send to the // LLM. Larger values cost more tokens with diminishing returns for a 2-4 // sentence summary. -- cgit v1.2.3