diff options
| author | Ben Sima <ben@bensima.com> | 2025-11-26 13:55:42 -0500 |
|---|---|---|
| committer | Ben Sima <ben@bensima.com> | 2025-11-26 13:55:42 -0500 |
| commit | 6305179905d5428879aaf8c08fef1722c734c3d7 (patch) | |
| tree | 634e4c6ee7f102a510f11f8860c98617eb557657 /Omni/Jr.hs | |
| parent | 1080ecd359dbf36bf571bb8f56347174edccdc5c (diff) | |
Jr: Gerrit-style conflict handling - kick back to coder with context
All tests pass and lint is clean. The implementation adds Gerrit-style
c
1. **`gatherConflictContext`** - Creates rich context including:
- The commit info (SHA, subject, body) - Current HEAD state (what
branch moved to) - Per-file conflict details showing both your
changes and recent chan
2. **`getFileConflictInfo`** - For each conflicting file, shows:
- Your changes to that file (stat summary) - Recent changes by
others (last 3 commits touching the file)
3. The context is stored in `retryReason` and passed to the worker
via t
Task-Id: t-1o2g8gudqlx
Diffstat (limited to 'Omni/Jr.hs')
| -rw-r--r-- | Omni/Jr.hs | 92 |
1 files changed, 90 insertions, 2 deletions
@@ -208,7 +208,7 @@ runLoop delaySec = do Nothing -> do autoReview tid task commitSha --- | Handle merge conflict during review +-- | Handle merge conflict during review (Gerrit-style: provide rich context) handleConflict :: Text -> [Text] -> String -> IO () handleConflict tid conflictFiles commitSha = do maybeCtx <- TaskCore.getRetryContext tid @@ -219,17 +219,105 @@ handleConflict tid conflictFiles commitSha = do putText "[review] Task has failed 3 times. Needs human intervention." TaskCore.updateTaskStatus tid TaskCore.Open [] else do + conflictDetails <- gatherConflictContext commitSha conflictFiles TaskCore.setRetryContext TaskCore.RetryContext { TaskCore.retryTaskId = tid, TaskCore.retryOriginalCommit = Text.pack commitSha, TaskCore.retryConflictFiles = conflictFiles, TaskCore.retryAttempt = attempt, - TaskCore.retryReason = "merge_conflict" + TaskCore.retryReason = conflictDetails } TaskCore.updateTaskStatus tid TaskCore.Open [] putText ("[review] Task " <> tid <> " returned to queue (attempt " <> tshow attempt <> "/3).") +-- | Gather Gerrit-style conflict context for the coder +gatherConflictContext :: String -> [Text] -> IO Text +gatherConflictContext commitSha conflictFiles = do + commitInfo <- getCommitInfo commitSha + currentHeadInfo <- getCurrentHeadInfo + fileDiffs <- traverse (getFileConflictInfo commitSha <. Text.unpack) conflictFiles + + pure + <| Text.unlines + [ "MERGE CONFLICT - Your changes could not be cleanly applied", + "", + "== Your Commit ==", + commitInfo, + "", + "== Current HEAD ==", + currentHeadInfo, + "", + "== Conflicting Files ==", + Text.unlines fileDiffs, + "", + "== Resolution Instructions ==", + "1. The codebase has been updated since your work", + "2. Review the current state of conflicting files", + "3. Re-implement your changes on top of the current code", + "4. Ensure your changes still make sense given the updates" + ] + +-- | Get info about the commit that caused the conflict +getCommitInfo :: String -> IO Text +getCommitInfo sha = do + (_, out, _) <- + Process.readProcessWithExitCode + "git" + ["log", "-1", "--format=%h %s%n%b", sha] + "" + pure <| Text.pack out + +-- | Get info about current HEAD +getCurrentHeadInfo :: IO Text +getCurrentHeadInfo = do + (_, out, _) <- + Process.readProcessWithExitCode + "git" + ["log", "-1", "--format=%h %s (%cr)"] + "" + pure <| Text.pack out + +-- | Get file-level conflict context showing what changed in both branches +getFileConflictInfo :: String -> FilePath -> IO Text +getFileConflictInfo commitSha filePath = do + yourChanges <- getYourChangesToFile commitSha filePath + recentChanges <- getRecentChangesToFile filePath + pure + <| Text.unlines + [ "--- " <> Text.pack filePath <> " ---", + "", + "Your changes to this file:", + yourChanges, + "", + "Recent changes by others:", + recentChanges + ] + +-- | Get a summary of changes in a specific commit to a file +getYourChangesToFile :: String -> FilePath -> IO Text +getYourChangesToFile commitSha filePath = do + (code, out, _) <- + Process.readProcessWithExitCode + "git" + ["show", "--stat", commitSha, "--", filePath] + "" + case code of + Exit.ExitSuccess -> pure <| Text.pack (take 500 out) + Exit.ExitFailure _ -> pure "(unable to get diff)" + +-- | Get recent changes to a file (last few commits) +getRecentChangesToFile :: FilePath -> IO Text +getRecentChangesToFile filePath = do + (code, out, _) <- + Process.readProcessWithExitCode + "git" + ["log", "-3", "--oneline", "--", filePath] + "" + case code of + Exit.ExitSuccess -> pure <| Text.pack out + Exit.ExitFailure _ -> pure "(unable to get history)" + -- | Interactive review command (jr review <task-id>) reviewTask :: Text -> Bool -> IO () reviewTask tid autoMode = do |
