← Back to task

Commit bfe8be3a

commit bfe8be3a1a85aa2104bb8e9f2fb2c5147d9fbcaf
Author: Ben Sima <ben@bensima.com>
Date:   Thu Nov 27 22:21:02 2025

    Exclude tasks with retry >= 3 from ready queue
    
    Tasks that have failed 3 or more times need human intervention and
    should not appear in the ready queue. This prevents infinite retry
    loops.
    
    Task-Id: t-164 Amp-Thread-ID:
    https://ampcode.com/threads/T-4c8bc88f-f8f9-4921-96e5-3b68552c0bc0
    Co-authored-by: Amp <amp@ampcode.com>

diff --git a/Omni/Task.hs b/Omni/Task.hs
index b0dbe897..663cc023 100644
--- a/Omni/Task.hs
+++ b/Omni/Task.hs
@@ -483,6 +483,21 @@ unitTests =
         epic <- createTask "Epic task" Epic Nothing Nothing P2 [] Nothing
         ready <- getReadyTasks
         (taskId epic `notElem` map taskId ready) Test.@?= True,
+      Test.unit "ready tasks exclude tasks needing intervention (retry >= 3)" <| do
+        task <- createTask "Failing task" WorkTask Nothing Nothing P2 [] Nothing
+        ready1 <- getReadyTasks
+        (taskId task `elem` map taskId ready1) Test.@?= True
+        setRetryContext
+          RetryContext
+            { retryTaskId = taskId task,
+              retryOriginalCommit = "abc123",
+              retryConflictFiles = [],
+              retryAttempt = 3,
+              retryReason = "test_failure",
+              retryNotes = Nothing
+            }
+        ready2 <- getReadyTasks
+        (taskId task `notElem` map taskId ready2) Test.@?= True,
       Test.unit "child task gets sequential ID" <| do
         parent <- createTask "Parent" Epic Nothing Nothing P2 [] Nothing
         child1 <- createTask "Child 1" WorkTask (Just (taskId parent)) Nothing P2 [] Nothing
diff --git a/Omni/Task/Core.hs b/Omni/Task/Core.hs
index ca9345f2..4ff0f5fa 100644
--- a/Omni/Task/Core.hs
+++ b/Omni/Task/Core.hs
@@ -619,12 +619,16 @@ listTasks maybeType maybeParent maybeStatus maybeNamespace = do
 getReadyTasks :: IO [Task]
 getReadyTasks = do
   allTasks <- loadTasks
+  retryContexts <- getAllRetryContexts
   let openTasks = filter (\t -> taskStatus t == Open || taskStatus t == InProgress) allTasks
       doneIds = map taskId <| filter (\t -> taskStatus t == Done) allTasks
 
       parentIds = mapMaybe taskParent allTasks
       isParent tid = tid `elem` parentIds
 
+      -- Tasks with retry_attempt >= 3 need human intervention
+      needsInterventionIds = [retryTaskId ctx | ctx <- retryContexts, retryAttempt ctx >= 3]
+
       blockingDepIds task = [depId dep | dep <- taskDependencies task, depType dep `elem` [Blocks, ParentChild]]
       isReady task =
         taskType task
@@ -633,6 +637,8 @@ getReadyTasks = do
           && all (`elem` doneIds) (blockingDepIds task)
           && taskType task
           /= HumanTask
+          && taskId task
+          `notElem` needsInterventionIds
   pure <| filter isReady openTasks
 
 -- Get dependency tree