From 7e03d8f323d44b972aeceb009928c977ee64fed2 Mon Sep 17 00:00:00 2001 From: Ben Sima Date: Mon, 1 Dec 2025 13:51:21 -0500 Subject: Replace HumanTask type with NeedsHelp status - Remove HumanTask from TaskType enum (now Epic | WorkTask only) - Add NeedsHelp to Status enum for tasks requiring human guidance - Update getReadyTasks to filter NeedsHelp instead of HumanTask - Rename humanTasks to tasksNeedingHelp in HumanActionItems - Add CLI parsing for needs-help status in list/update commands - Add badge styling for NeedsHelp (amber/yellow theme) - Update all status pattern matches in tree view and print functions - Update tests to verify NeedsHelp exclusion from ready queue Task-Id: t-210 --- Omni/Jr/Web.hs | 16 ++++++++++------ Omni/Jr/Web/Style.hs | 6 ++++++ Omni/Task.hs | 24 +++++++++++------------- Omni/Task/Core.hs | 17 ++++++++++------- 4 files changed, 37 insertions(+), 26 deletions(-) (limited to 'Omni') diff --git a/Omni/Jr/Web.hs b/Omni/Jr/Web.hs index 2e4ee66..8508560 100644 --- a/Omni/Jr/Web.hs +++ b/Omni/Jr/Web.hs @@ -680,6 +680,7 @@ statusBadge status = TaskCore.Review -> ("badge badge-review", "Review") TaskCore.Approved -> ("badge badge-approved", "Approved") TaskCore.Done -> ("badge badge-done", "Done") + TaskCore.NeedsHelp -> ("badge badge-needshelp", "Needs Help") in Lucid.span_ [Lucid.class_ cls] label complexityBadge :: (Monad m) => Int -> Lucid.HtmlT m () @@ -770,6 +771,7 @@ clickableBadge status _tid = TaskCore.Review -> ("badge badge-review status-badge-clickable", "Review") TaskCore.Approved -> ("badge badge-approved status-badge-clickable", "Approved") TaskCore.Done -> ("badge badge-done status-badge-clickable", "Done") + TaskCore.NeedsHelp -> ("badge badge-needshelp status-badge-clickable", "Needs Help") in Lucid.span_ [ Lucid.class_ cls, Lucid.tabindex_ "0", @@ -797,6 +799,7 @@ statusDropdownOptions currentStatus tid = statusOption TaskCore.Review currentStatus tid statusOption TaskCore.Approved currentStatus tid statusOption TaskCore.Done currentStatus tid + statusOption TaskCore.NeedsHelp currentStatus tid statusOption :: (Monad m) => TaskCore.Status -> TaskCore.Status -> Text -> Lucid.HtmlT m () statusOption opt currentStatus tid = @@ -807,6 +810,7 @@ statusOption opt currentStatus tid = TaskCore.Review -> ("badge badge-review", "Review") TaskCore.Approved -> ("badge badge-approved", "Approved") TaskCore.Done -> ("badge badge-done", "Done") + TaskCore.NeedsHelp -> ("badge badge-needshelp", "Needs Help") isSelected = opt == currentStatus optClass = cls <> " status-dropdown-option" <> if isSelected then " selected" else "" in Lucid.form_ @@ -1083,8 +1087,8 @@ instance Lucid.ToHtml InterventionPage where let crumbs = [Breadcrumb "Jr" (Just "/"), Breadcrumb "Needs Human Action" Nothing] failed = TaskCore.failedTasks actionItems epicsReady = TaskCore.epicsInReview actionItems - human = TaskCore.humanTasks actionItems - totalCount = length failed + length epicsReady + length human + needsHelp = TaskCore.tasksNeedingHelp actionItems + totalCount = length failed + length epicsReady + length needsHelp in Lucid.doctypehtml_ <| do pageHead "Needs Human Action - Jr" pageBodyWithCrumbs crumbs <| do @@ -1103,10 +1107,10 @@ instance Lucid.ToHtml InterventionPage where Lucid.h2_ [Lucid.class_ "section-header"] <| Lucid.toHtml ("Epics Ready for Review (" <> tshow (length epicsReady) <> ")") Lucid.p_ [Lucid.class_ "info-msg"] "Epics with all children completed. Verify before closing." Lucid.div_ [Lucid.class_ "task-list"] <| traverse_ renderEpicReviewCard epicsReady - unless (null human) <| do - Lucid.h2_ [Lucid.class_ "section-header"] <| Lucid.toHtml ("Human Tasks (" <> tshow (length human) <> ")") - Lucid.p_ [Lucid.class_ "info-msg"] "Tasks explicitly marked as needing human work." - Lucid.div_ [Lucid.class_ "task-list"] <| traverse_ renderTaskCard (sortTasks currentSort human) + unless (null needsHelp) <| do + Lucid.h2_ [Lucid.class_ "section-header"] <| Lucid.toHtml ("Needs Help (" <> tshow (length needsHelp) <> ")") + Lucid.p_ [Lucid.class_ "info-msg"] "Tasks where Jr needs human guidance or decisions." + Lucid.div_ [Lucid.class_ "task-list"] <| traverse_ renderTaskCard (sortTasks currentSort needsHelp) renderEpicReviewCard :: (Monad m) => TaskCore.EpicForReview -> Lucid.HtmlT m () renderEpicReviewCard epicReview = do diff --git a/Omni/Jr/Web/Style.hs b/Omni/Jr/Web/Style.hs index 08fda5d..9a0c12d 100644 --- a/Omni/Jr/Web/Style.hs +++ b/Omni/Jr/Web/Style.hs @@ -584,6 +584,9 @@ statusBadges = do ".badge-done" ? do backgroundColor "#d1fae5" color "#065f46" + ".badge-needshelp" ? do + backgroundColor "#fef3c7" + color "#92400e" ".status-badge-dropdown" ? do position relative display inlineBlock @@ -1864,6 +1867,9 @@ darkModeStyles = ".badge-done" ? do backgroundColor "#064e3b" color "#6ee7b7" + ".badge-needshelp" ? do + backgroundColor "#78350f" + color "#fcd34d" ".badge-p0" ? do backgroundColor "#7f1d1d" color "#fca5a5" diff --git a/Omni/Task.hs b/Omni/Task.hs index ce26f41..1385a4b 100644 --- a/Omni/Task.hs +++ b/Omni/Task.hs @@ -85,11 +85,11 @@ Commands: Options: -h --help Show this help --title= Task title - --type=<type> Task type: epic, task, or human (default: task) + --type=<type> Task type: epic or task (default: task) --parent=<id> Parent epic ID --priority=<p> Priority: 0-4 (0=critical, 4=backlog, default: 2) --complexity=<c> Complexity: 1-5 for model selection (1=trivial, 5=expert) - --status=<status> Filter by status: draft, open, in-progress, review, approved, done + --status=<status> Filter by status: draft, open, in-progress, review, approved, done, needs-help --epic=<id> Filter stats by epic (recursive) --deps=<ids> Comma-separated list of dependency IDs --dep-type=<type> Dependency type: blocks, discovered-from, parent-child, related @@ -109,7 +109,7 @@ Options: Arguments: <title> Task title <id> Task ID - <status> Task status (draft, open, in-progress, review, approved, done) + <status> Task status (draft, open, in-progress, review, approved, done, needs-help) <message> Comment message <file> JSONL file to import |] @@ -147,8 +147,7 @@ move' args Nothing -> pure WorkTask Just "epic" -> pure Epic Just "task" -> pure WorkTask - Just "human" -> pure HumanTask - Just other -> panic <| "Invalid task type: " <> T.pack other <> ". Use: epic, task, or human" + Just other -> panic <| "Invalid task type: " <> T.pack other <> ". Use: epic or task" parent <- case Cli.getArg args (Cli.longOption "parent") of Nothing -> pure Nothing Just p -> pure <| Just (T.pack p) @@ -295,7 +294,6 @@ move' args Nothing -> pure Nothing Just "epic" -> pure <| Just Epic Just "task" -> pure <| Just WorkTask - Just "human" -> pure <| Just HumanTask Just other -> panic <| "Invalid task type: " <> T.pack other maybeParent <- case Cli.getArg args (Cli.longOption "parent") of Nothing -> pure Nothing @@ -308,7 +306,8 @@ move' args Just "review" -> pure <| Just Review Just "approved" -> pure <| Just Approved Just "done" -> pure <| Just Done - Just other -> panic <| "Invalid status: " <> T.pack other <> ". Use: draft, open, in-progress, review, approved, or done" + Just "needs-help" -> pure <| Just NeedsHelp + Just other -> panic <| "Invalid status: " <> T.pack other <> ". Use: draft, open, in-progress, review, approved, done, or needs-help" maybeNamespace <- case Cli.getArg args (Cli.longOption "namespace") of Nothing -> pure Nothing Just ns -> do @@ -362,7 +361,8 @@ move' args "review" -> Review "approved" -> Approved "done" -> Done - _ -> panic "Invalid status. Use: draft, open, in-progress, review, approved, or done" + "needs-help" -> NeedsHelp + _ -> panic "Invalid status. Use: draft, open, in-progress, review, approved, done, or needs-help" -- Show verification checklist warning when marking Done without --verified when (newStatus == Done && not isVerified && not (isJsonMode args)) <| do @@ -620,11 +620,9 @@ unitTests = taskStatus task Test.@?= Open taskPriority task Test.@?= P2 null (taskDependencies task) Test.@?= True, - Test.unit "can create human task" <| do - task <- createTask "Human Task" HumanTask Nothing Nothing P2 Nothing [] "Human task description" - taskType task Test.@?= HumanTask, - Test.unit "ready tasks exclude human tasks" <| do - task <- createTask "Human Task" HumanTask Nothing Nothing P2 Nothing [] "Human task" + Test.unit "ready tasks exclude NeedsHelp tasks" <| do + task <- createTask "Needs Help Task" WorkTask Nothing Nothing P2 Nothing [] "Task needing help" + updateTaskStatus (taskId task) NeedsHelp [] ready <- getReadyTasks (taskId task `notElem` map taskId ready) Test.@?= True, Test.unit "ready tasks exclude draft tasks" <| do diff --git a/Omni/Task/Core.hs b/Omni/Task/Core.hs index 35f3ea7..8badf6b 100644 --- a/Omni/Task/Core.hs +++ b/Omni/Task/Core.hs @@ -45,10 +45,10 @@ data Task = Task } deriving (Show, Eq, Generic) -data TaskType = Epic | WorkTask | HumanTask +data TaskType = Epic | WorkTask deriving (Show, Eq, Read, Generic) -data Status = Draft | Open | InProgress | Review | Approved | Done +data Status = Draft | Open | InProgress | Review | Approved | Done | NeedsHelp deriving (Show, Eq, Read, Generic) -- Priority levels (matching beads convention) @@ -86,7 +86,7 @@ data EpicForReview = EpicForReview data HumanActionItems = HumanActionItems { failedTasks :: [Task], epicsInReview :: [EpicForReview], - humanTasks :: [Task] + tasksNeedingHelp :: [Task] } deriving (Show, Eq, Generic) @@ -806,8 +806,8 @@ getReadyTasks = do /= Epic && not (isParent (taskId task)) && all (`elem` doneIds) (blockingDepIds task) - && taskType task - /= HumanTask + && taskStatus task + /= NeedsHelp && taskId task `notElem` needsInterventionIds pure <| filter isReady openTasks @@ -920,6 +920,7 @@ showTaskTree maybeId = do Review -> "[?]" Approved -> "[+]" Done -> "[✓]" + NeedsHelp -> "[!]" coloredStatusStr = case taskType task of Epic -> magenta statusStr @@ -930,6 +931,7 @@ showTaskTree maybeId = do Review -> magenta statusStr Approved -> green statusStr Done -> green statusStr + NeedsHelp -> yellow statusStr nsStr = case taskNamespace task of Nothing -> "" @@ -988,6 +990,7 @@ printTask t = do Review -> magenta s Approved -> green s Done -> green s + NeedsHelp -> yellow s coloredTitle = if taskType t == Epic then bold (taskTitle t) else taskTitle t coloredProgress = if taskType t == Epic then magenta progressInfo else progressInfo @@ -1556,12 +1559,12 @@ getHumanActionItems = do let completed = length [c | c <- children, taskStatus c == Done], completed == total ] - human = [t | t <- allTasks, taskType t == HumanTask, taskStatus t == Open] + needingHelp = [t | t <- allTasks, taskStatus t == NeedsHelp] pure HumanActionItems { failedTasks = failed, epicsInReview = epicsReady, - humanTasks = human + tasksNeedingHelp = needingHelp } -- | Get all retry contexts from the database -- cgit v1.2.3