Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 21 additions & 6 deletions internal/daemon/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -1901,8 +1901,8 @@ func (s *Server) handleFixJob(w http.ResponseWriter, r *http.Request) {
}

// Build the fix prompt
fixPrompt := req.Prompt
if fixPrompt == "" && req.StaleJobID > 0 {
fixPrompt := ""
if req.StaleJobID > 0 {
// Server-side rebase: look up stale patch from DB and build rebase prompt
staleJob, err := s.db.GetJobByID(req.StaleJobID)
if err != nil {
Expand Down Expand Up @@ -1943,7 +1943,11 @@ func (s *Server) handleFixJob(w http.ResponseWriter, r *http.Request) {
writeError(w, http.StatusBadRequest, "parent job has no review to fix")
return
}
fixPrompt = buildFixPrompt(review.Output)
if req.Prompt != "" {
fixPrompt = buildFixPromptWithInstructions(review.Output, req.Prompt)
} else {
fixPrompt = buildFixPrompt(review.Output)
}
}

// Resolve agent for fix workflow
Expand Down Expand Up @@ -2154,18 +2158,29 @@ func (s *Server) handleActivity(w http.ResponseWriter, r *http.Request) {

// buildFixPrompt constructs a prompt for fixing review findings.
func buildFixPrompt(reviewOutput string) string {
return "# Fix Request\n\n" +
return buildFixPromptWithInstructions(reviewOutput, "")
}

// buildFixPromptWithInstructions constructs a fix prompt that includes the review
// findings and optional user-provided instructions.
func buildFixPromptWithInstructions(reviewOutput, userInstructions string) string {
prompt := "# Fix Request\n\n" +
"An analysis was performed and produced the following findings:\n\n" +
"## Analysis Findings\n\n" +
reviewOutput +
"\n\n## Instructions\n\n" +
reviewOutput + "\n\n"
if userInstructions != "" {
prompt += "## Additional Instructions\n\n" +
userInstructions + "\n\n"
}
prompt += "## Instructions\n\n" +
"Please apply the suggested changes from the analysis above. " +
"Make the necessary edits to address each finding. " +
"Focus on the highest priority items first.\n\n" +
"After making changes:\n" +
"1. Verify the code still compiles/passes linting\n" +
"2. Run any relevant tests to ensure nothing is broken\n" +
"3. Stage the changes with git add but do NOT commit — the changes will be captured as a patch\n"
return prompt
}

// buildRebasePrompt constructs a prompt for re-applying a stale patch to current HEAD.
Expand Down
41 changes: 41 additions & 0 deletions internal/daemon/server_ops_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,47 @@ func TestHandleFixJobStaleValidation(t *testing.T) {
}
})

t.Run("custom prompt includes review context", func(t *testing.T) {
req := testutil.MakeJSONRequest(
t, http.MethodPost, "/api/job/fix",
fixJobRequest{
ParentJobID: reviewJob.ID,
Prompt: "Ignore the security concern, it's only a testing binary.",
},
)
w := httptest.NewRecorder()
server.handleFixJob(w, req)

if w.Code != http.StatusCreated {
t.Fatalf(
"Expected 201 for fix enqueue with custom prompt, got %d: %s",
w.Code, w.Body.String(),
)
}

var fixJob storage.ReviewJob
testutil.DecodeJSON(t, w, &fixJob)

stored, err := db.GetJobByID(fixJob.ID)
if err != nil {
t.Fatalf("GetJobByID(%d): %v", fixJob.ID, err)
}

// The stored prompt must contain both the review output AND the custom instructions
if !strings.Contains(stored.Prompt, "FAIL: issues found") {
t.Fatalf(
"Expected fix prompt to contain review output, got:\n%s",
stored.Prompt,
)
}
if !strings.Contains(stored.Prompt, "Ignore the security concern") {
t.Fatalf(
"Expected fix prompt to contain custom instructions, got:\n%s",
stored.Prompt,
)
}
})

t.Run("fix job as parent is rejected", func(t *testing.T) {
// Create a fix job and try to use it as a parent
fixJob, _ := db.EnqueueJob(storage.EnqueueOpts{
Expand Down
Loading