summaryrefslogtreecommitdiff
path: root/Omni/Jr.hs
diff options
context:
space:
mode:
Diffstat (limited to 'Omni/Jr.hs')
-rw-r--r--Omni/Jr.hs125
1 files changed, 106 insertions, 19 deletions
diff --git a/Omni/Jr.hs b/Omni/Jr.hs
index bae5588..0cf22f6 100644
--- a/Omni/Jr.hs
+++ b/Omni/Jr.hs
@@ -4,6 +4,7 @@
-- : out jr
-- : dep sqlite-simple
+-- : dep sqids
module Omni.Jr where
import Alpha
@@ -143,26 +144,112 @@ reviewTask tid = do
(x : _) -> x
[] -> ""
- putText "\n=== Diff for this task ===\n"
- _ <- Process.rawSystem "git" ["show", commitSha]
-
- putText "\n[a]ccept / [r]eject / [s]kip? "
- IO.hFlush IO.stdout
- choice <- getLine
-
- case Text.toLower choice of
- c
- | "a" `Text.isPrefixOf` c -> do
- TaskCore.updateTaskStatus tid TaskCore.Done []
- putText ("Task " <> tid <> " marked as Done.")
- | "r" `Text.isPrefixOf` c -> do
- putText "Enter rejection reason: "
- IO.hFlush IO.stdout
- reason <- getLine
+ -- Check for merge conflicts before showing diff
+ conflictResult <- checkMergeConflict commitSha
+ case conflictResult of
+ Just conflictFiles -> do
+ putText "\n=== MERGE CONFLICT DETECTED ==="
+ putText "This commit cannot be cleanly applied to live."
+ putText "Conflicting files:"
+ traverse_ (\f -> putText (" - " <> f)) conflictFiles
+ putText ""
+ putText "Kicking back to coder with context..."
+
+ -- Get current retry count
+ maybeCtx <- TaskCore.getRetryContext tid
+ let attempt = maybe 1 (\c -> TaskCore.retryAttempt c + 1) maybeCtx
+
+ if attempt > 3
+ then do
+ putText "\nTask has failed 3 times. Marking as NeedsHuman."
+ -- For now, just mark as Open with a note (no NeedsHuman status yet)
+ TaskCore.updateTaskStatus tid TaskCore.Open []
+ putText ("Task " <> tid <> " needs human intervention (3 failed attempts).")
+ else do
+ -- Save retry context
+ TaskCore.setRetryContext
+ TaskCore.RetryContext
+ { TaskCore.retryTaskId = tid,
+ TaskCore.retryOriginalCommit = Text.pack commitSha,
+ TaskCore.retryConflictFiles = conflictFiles,
+ TaskCore.retryAttempt = attempt,
+ TaskCore.retryReason = "merge_conflict"
+ }
TaskCore.updateTaskStatus tid TaskCore.Open []
- putText ("Task " <> tid <> " reopened.")
- putText ("Reason: " <> reason)
- | otherwise -> putText "Skipped; no status change."
+ putText ("Task " <> tid <> " returned to queue (attempt " <> tshow attempt <> "/3).")
+ Nothing -> do
+ -- No conflict, proceed with normal review
+ putText "\n=== Diff for this task ===\n"
+ _ <- Process.rawSystem "git" ["show", commitSha]
+
+ putText "\n[a]ccept / [r]eject / [s]kip? "
+ IO.hFlush IO.stdout
+ choice <- getLine
+
+ case Text.toLower choice of
+ c
+ | "a" `Text.isPrefixOf` c -> do
+ TaskCore.clearRetryContext tid
+ TaskCore.updateTaskStatus tid TaskCore.Done []
+ putText ("Task " <> tid <> " marked as Done.")
+ | "r" `Text.isPrefixOf` c -> do
+ putText "Enter rejection reason: "
+ IO.hFlush IO.stdout
+ reason <- getLine
+ -- Save rejection as retry context
+ maybeCtx <- TaskCore.getRetryContext tid
+ let attempt = maybe 1 (\ctx -> TaskCore.retryAttempt ctx + 1) maybeCtx
+ TaskCore.setRetryContext
+ TaskCore.RetryContext
+ { TaskCore.retryTaskId = tid,
+ TaskCore.retryOriginalCommit = Text.pack commitSha,
+ TaskCore.retryConflictFiles = [],
+ TaskCore.retryAttempt = attempt,
+ TaskCore.retryReason = "rejected: " <> reason
+ }
+ TaskCore.updateTaskStatus tid TaskCore.Open []
+ putText ("Task " <> tid <> " reopened (attempt " <> tshow attempt <> "/3).")
+ | otherwise -> putText "Skipped; no status change."
+
+-- | Check if a commit can be cleanly cherry-picked onto live
+-- Returns Nothing if clean, Just [conflicting files] if conflict
+checkMergeConflict :: String -> IO (Maybe [Text])
+checkMergeConflict commitSha = do
+ -- Save current state
+ (_, _, _) <- Process.readProcessWithExitCode "git" ["branch", "--show-current"] ""
+ (_, origHead, _) <- Process.readProcessWithExitCode "git" ["rev-parse", "HEAD"] ""
+
+ -- Try cherry-pick
+ (cpCode, _, cpErr) <-
+ Process.readProcessWithExitCode
+ "git"
+ ["cherry-pick", "--no-commit", commitSha]
+ ""
+
+ -- Always abort/reset regardless of result
+ _ <- Process.readProcessWithExitCode "git" ["cherry-pick", "--abort"] ""
+ _ <- Process.readProcessWithExitCode "git" ["reset", "--hard", List.head (List.lines origHead)] ""
+
+ case cpCode of
+ Exit.ExitSuccess -> pure Nothing
+ Exit.ExitFailure _ -> do
+ -- Parse conflict files from error message
+ let errLines = Text.lines (Text.pack cpErr)
+ conflictLines = filter (Text.isPrefixOf "CONFLICT") errLines
+ -- Extract file names (rough parsing)
+ files = mapMaybe extractConflictFile conflictLines
+ pure (Just (if null files then ["(unknown files)"] else files))
+
+extractConflictFile :: Text -> Maybe Text
+extractConflictFile line =
+ -- CONFLICT (content): Merge conflict in path/to/file.hs
+ case Text.breakOn "Merge conflict in " line of
+ (_, rest)
+ | not (Text.null rest) -> Just (Text.strip (Text.drop 18 rest))
+ _ -> case Text.breakOn "in " line of
+ (_, rest)
+ | not (Text.null rest) -> Just (Text.strip (Text.drop 3 rest))
+ _ -> Nothing
test :: Test.Tree
test =