diff --git a/backend/internal/adapters/reviewer/claudecode/claudecode.go b/backend/internal/adapters/reviewer/claudecode/claudecode.go index 159d67a673..e8db5ca28c 100644 --- a/backend/internal/adapters/reviewer/claudecode/claudecode.go +++ b/backend/internal/adapters/reviewer/claudecode/claudecode.go @@ -103,5 +103,12 @@ func (r *Reviewer) PreLaunch(ctx context.Context, inv ports.ReviewInvocation) er // ReviewMessage is the text injected into an already-running reviewer pane to // review a new commit — AO's central review prompt. func (r *Reviewer) ReviewMessage(_ context.Context, inv ports.ReviewInvocation) (string, error) { - return inv.Prompt, nil + // Do not return the prompt here. ReviewMessage is called when re-notifying an + // already-running reviewer pane about a new commit in the queue. + // The reviewer already has the full prompt (including task instructions and run IDs) + // from the initial launch via ReviewCommand -> GetLaunchCommand. + // Returning it again would just duplicate the instructions in the terminal. + // Instead, return empty so the terminal stays clean, the agent has all the context + // it needs from the original prompt at launch time. + return "", nil } diff --git a/backend/internal/adapters/reviewer/claudecode/claudecode_test.go b/backend/internal/adapters/reviewer/claudecode/claudecode_test.go index 50a3948371..24c17b975c 100644 --- a/backend/internal/adapters/reviewer/claudecode/claudecode_test.go +++ b/backend/internal/adapters/reviewer/claudecode/claudecode_test.go @@ -139,3 +139,13 @@ func contains(values []string, needle string) bool { } return false } + +func TestReviewMessageReturnsEmpty(t *testing.T) { + got, err := (&Reviewer{}).ReviewMessage(context.Background(), ports.ReviewInvocation{Prompt: "next review"}) + if err != nil { + t.Fatalf("ReviewMessage: %v", err) + } + if got != "" { + t.Fatalf("ReviewMessage should return empty; got %q", got) + } +} diff --git a/backend/internal/adapters/reviewer/codex/codex.go b/backend/internal/adapters/reviewer/codex/codex.go index a166b78c7d..d08141ac95 100644 --- a/backend/internal/adapters/reviewer/codex/codex.go +++ b/backend/internal/adapters/reviewer/codex/codex.go @@ -62,7 +62,14 @@ func (r *Reviewer) ReviewCommand(ctx context.Context, inv ports.ReviewInvocation // ReviewMessage returns the centrally-authored task for an existing pane. func (r *Reviewer) ReviewMessage(_ context.Context, inv ports.ReviewInvocation) (string, error) { - return inv.Prompt, nil + // Do not return the prompt here. ReviewMessage is called when re-notifying an + // already-running reviewer pane about a new commit in the queue. + // The reviewer already has the full prompt (including task instructions and run IDs) + // from the initial launch via ReviewCommand -> GetLaunchCommand. + // Returning it again would just duplicate the instructions in the terminal. + // Instead, return empty so the terminal stays clean, the agent has all the context + // it needs from the original prompt at launch time. + return "", nil } func insertBeforePrompt(argv []string, extra ...string) []string { diff --git a/backend/internal/adapters/reviewer/codex/codex_test.go b/backend/internal/adapters/reviewer/codex/codex_test.go index 9912c6c244..de9dc31a64 100644 --- a/backend/internal/adapters/reviewer/codex/codex_test.go +++ b/backend/internal/adapters/reviewer/codex/codex_test.go @@ -66,12 +66,12 @@ func TestReviewCommandUsesReadOnlySandbox(t *testing.T) { } } -func TestReviewMessageReturnsTaskPrompt(t *testing.T) { +func TestReviewMessageReturnsEmpty(t *testing.T) { got, err := (&Reviewer{}).ReviewMessage(context.Background(), ports.ReviewInvocation{Prompt: "next review"}) if err != nil { t.Fatalf("ReviewMessage: %v", err) } - if got != "next review" { - t.Fatalf("message = %q", got) + if got != "" { + t.Fatalf("ReviewMessage should return empty; got %q", got) } } diff --git a/backend/internal/adapters/reviewer/opencode/opencode.go b/backend/internal/adapters/reviewer/opencode/opencode.go index 2ffd3b5a31..d78975190f 100644 --- a/backend/internal/adapters/reviewer/opencode/opencode.go +++ b/backend/internal/adapters/reviewer/opencode/opencode.go @@ -52,5 +52,12 @@ func (r *Reviewer) ReviewCommand(ctx context.Context, inv ports.ReviewInvocation // ReviewMessage returns the centrally-authored task for an existing pane. func (r *Reviewer) ReviewMessage(_ context.Context, inv ports.ReviewInvocation) (string, error) { - return inv.Prompt, nil + // Do not return the prompt here. ReviewMessage is called when re-notifying an + // already-running reviewer pane about a new commit in the queue. + // The reviewer already has the full prompt (including task instructions and run IDs) + // from the initial launch via ReviewCommand -> GetLaunchCommand. + // Returning it again would just duplicate the instructions in the terminal. + // Instead, return empty so the terminal stays clean, the agent has all the context + // it needs from the original prompt at launch time. + return "", nil } diff --git a/backend/internal/adapters/reviewer/opencode/opencode_test.go b/backend/internal/adapters/reviewer/opencode/opencode_test.go index 9086ed58ac..d56bf32a85 100644 --- a/backend/internal/adapters/reviewer/opencode/opencode_test.go +++ b/backend/internal/adapters/reviewer/opencode/opencode_test.go @@ -100,13 +100,16 @@ func TestBashAllowlistCoversPromptRequiredCommands(t *testing.T) { } } -func TestReviewMessageReturnsTaskPrompt(t *testing.T) { +func TestReviewMessageReturnsEmpty(t *testing.T) { got, err := (&Reviewer{}).ReviewMessage(context.Background(), ports.ReviewInvocation{Prompt: "next review"}) if err != nil { t.Fatalf("ReviewMessage: %v", err) } - if got != "next review" { - t.Fatalf("message = %q", got) + // ReviewMessage should return empty so the prompt + // doesn't clutter the terminal. The reviewer already received the full + // prompt at launch time. + if got != "" { + t.Fatalf("ReviewMessage should return empty; got %q", got) } }