diff options
| author | Ben Sima <ben@bensima.com> | 2025-11-27 22:42:20 -0500 |
|---|---|---|
| committer | Ben Sima <ben@bensima.com> | 2025-11-27 22:42:20 -0500 |
| commit | c3f48a938af0b9a2f692fab2168cc549d629511c (patch) | |
| tree | 98b8ea55a03011b37aa33e236025a9b714ca2036 | |
| parent | ecbc8385d590cf8f52d437796ff91d6e55bfd55e (diff) | |
Add Draft status for tasks not ready for jr to pickup
All changes are in place. The build and tests pass. The implementation
i
1. **Added `Draft` to Status enum** - First in sequence: `Draft |
Open | 2. **Excluded Draft from getReadyTasks** - Uses `taskStatus
t `elem` [Op 3. **CLI support** - `task update <id> draft` works,
`task list --status 4. **Display coloring** - Draft shows in gray
with `[.]` indicator in tr 5. **TaskStats** - Added `draftTasks`
field and display in stats output 6. **Tests** - Added unit test for
draft exclusion from ready tasks and
Task-Id: t-166
| -rw-r--r-- | Omni/Task.hs | 33 | ||||
| -rw-r--r-- | Omni/Task/Core.hs | 13 |
2 files changed, 38 insertions, 8 deletions
diff --git a/Omni/Task.hs b/Omni/Task.hs index 663cc02..c078a3e 100644 --- a/Omni/Task.hs +++ b/Omni/Task.hs @@ -81,7 +81,7 @@ Options: --type=<type> Task type: epic, task, or human (default: task) --parent=<id> Parent epic ID --priority=<p> Priority: 0-4 (0=critical, 4=backlog, default: 2) - --status=<status> Filter by status: open, in-progress, review, approved, done + --status=<status> Filter by status: draft, open, in-progress, review, approved, done --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 @@ -99,7 +99,7 @@ Options: Arguments: <title> Task title <id> Task ID - <status> Task status (open, in-progress, review, approved, done) + <status> Task status (draft, open, in-progress, review, approved, done) <file> JSONL file to import |] @@ -208,11 +208,12 @@ move' args Just other -> panic <| "Invalid priority: " <> T.pack other <> ". Use: 0-4" maybeStatus <- case Cli.getArg args (Cli.longOption "status") of Nothing -> pure Nothing + Just "draft" -> pure <| Just Draft Just "open" -> pure <| Just Open Just "in-progress" -> pure <| Just InProgress Just "review" -> pure <| Just Review Just "done" -> pure <| Just Done - Just other -> panic <| "Invalid status: " <> T.pack other <> ". Use: open, in-progress, review, or done" + Just other -> panic <| "Invalid status: " <> T.pack other <> ". Use: draft, open, in-progress, review, or done" maybeNamespace <- case Cli.getArg args (Cli.longOption "namespace") of Nothing -> pure Nothing Just ns -> do @@ -270,12 +271,13 @@ move' args Just p -> pure <| Just (T.pack p) maybeStatus <- case Cli.getArg args (Cli.longOption "status") of Nothing -> pure Nothing + Just "draft" -> pure <| Just Draft Just "open" -> pure <| Just Open Just "in-progress" -> pure <| Just InProgress Just "review" -> pure <| Just Review Just "approved" -> pure <| Just Approved Just "done" -> pure <| Just Done - Just other -> panic <| "Invalid status: " <> T.pack other <> ". Use: open, in-progress, review, approved, or done" + Just other -> panic <| "Invalid status: " <> T.pack other <> ". Use: draft, open, in-progress, review, approved, or done" maybeNamespace <- case Cli.getArg args (Cli.longOption "namespace") of Nothing -> pure Nothing Just ns -> do @@ -323,12 +325,13 @@ move' args pure (map (\d -> Dependency {depId = d, depType = dtype}) ids) let newStatus = case statusStr of + "draft" -> Draft "open" -> Open "in-progress" -> InProgress "review" -> Review "approved" -> Approved "done" -> Done - _ -> panic "Invalid status. Use: open, in-progress, review, approved, or done" + _ -> panic "Invalid status. Use: draft, open, in-progress, review, approved, or done" -- Show verification checklist warning when marking Done without --verified when (newStatus == Done && not isVerified && not (isJsonMode args)) <| do @@ -449,6 +452,11 @@ unitTests = task <- createTask "Human Task" HumanTask Nothing Nothing P2 [] Nothing ready <- getReadyTasks (taskId task `notElem` map taskId ready) Test.@?= True, + Test.unit "ready tasks exclude draft tasks" <| do + task <- createTask "Draft Task" WorkTask Nothing Nothing P2 [] Nothing + updateTaskStatus (taskId task) Draft [] + ready <- getReadyTasks + (taskId task `notElem` map taskId ready) Test.@?= True, Test.unit "can create task with description" <| do task <- createTask "Test task" WorkTask Nothing Nothing P2 [] (Just "My description") taskDescription task Test.@?= Just "My description", @@ -772,6 +780,13 @@ cliTests = Right args -> do args `Cli.has` Cli.command "list" Test.@?= True Cli.getArg args (Cli.longOption "status") Test.@?= Just "approved", + Test.unit "list with --status=draft filter" <| do + let result = Docopt.parseArgs help ["list", "--status=draft"] + case result of + Left err -> Test.assertFailure <| "Failed to parse 'list --status=draft': " <> show err + Right args -> do + args `Cli.has` Cli.command "list" Test.@?= True + Cli.getArg args (Cli.longOption "status") Test.@?= Just "draft", Test.unit "ready command" <| do let result = Docopt.parseArgs help ["ready"] case result of @@ -800,6 +815,14 @@ cliTests = args `Cli.has` Cli.command "update" Test.@?= True Cli.getArg args (Cli.argument "id") Test.@?= Just "t-abc123" Cli.getArg args (Cli.argument "status") Test.@?= Just "approved", + Test.unit "update command with draft" <| do + let result = Docopt.parseArgs help ["update", "t-abc123", "draft"] + case result of + Left err -> Test.assertFailure <| "Failed to parse 'update ... draft': " <> show err + Right args -> do + args `Cli.has` Cli.command "update" Test.@?= True + Cli.getArg args (Cli.argument "id") Test.@?= Just "t-abc123" + Cli.getArg args (Cli.argument "status") Test.@?= Just "draft", Test.unit "update with --json flag" <| do let result = Docopt.parseArgs help ["update", "t-abc123", "done", "--json"] case result of diff --git a/Omni/Task/Core.hs b/Omni/Task/Core.hs index d15ed8e..61a2e83 100644 --- a/Omni/Task/Core.hs +++ b/Omni/Task/Core.hs @@ -44,7 +44,7 @@ data Task = Task data TaskType = Epic | WorkTask | HumanTask deriving (Show, Eq, Read, Generic) -data Status = Open | InProgress | Review | Approved | Done +data Status = Draft | Open | InProgress | Review | Approved | Done deriving (Show, Eq, Read, Generic) -- Priority levels (matching beads convention) @@ -692,7 +692,7 @@ getReadyTasks :: IO [Task] getReadyTasks = do allTasks <- loadTasks retryContexts <- getAllRetryContexts - let openTasks = filter (\t -> taskStatus t == Open || taskStatus t == InProgress) allTasks + let openTasks = filter (\t -> taskStatus t `elem` [Open, InProgress]) allTasks doneIds = map taskId <| filter (\t -> taskStatus t == Done) allTasks parentIds = mapMaybe taskParent allTasks @@ -815,6 +815,7 @@ showTaskTree maybeId = do completed = length <| filter (\t -> taskStatus t == Done) children in "[" <> T.pack (show completed) <> "/" <> T.pack (show total) <> "]" _ -> case taskStatus task of + Draft -> "[.]" Open -> "[ ]" InProgress -> "[~]" Review -> "[?]" @@ -824,6 +825,7 @@ showTaskTree maybeId = do coloredStatusStr = case taskType task of Epic -> magenta statusStr _ -> case taskStatus task of + Draft -> gray statusStr Open -> bold statusStr InProgress -> yellow statusStr Review -> magenta statusStr @@ -881,6 +883,7 @@ printTask t = do coloredStatus = let s = "[" <> T.pack (show (taskStatus t)) <> "]" in case taskStatus t of + Draft -> gray s Open -> bold s InProgress -> yellow s Review -> magenta s @@ -987,6 +990,7 @@ saveTaskToJsonl path task = do data TaskStats = TaskStats { totalTasks :: Int, + draftTasks :: Int, openTasks :: Int, inProgressTasks :: Int, reviewTasks :: Int, @@ -1021,6 +1025,7 @@ getTaskStats maybeEpicId = do tasks = targetTasks total = length tasks + draft = length <| filter (\t -> taskStatus t == Draft) tasks open = length <| filter (\t -> taskStatus t == Open) tasks inProg = length <| filter (\t -> taskStatus t == InProgress) tasks review = length <| filter (\t -> taskStatus t == Review) tasks @@ -1028,7 +1033,7 @@ getTaskStats maybeEpicId = do done = length <| filter (\t -> taskStatus t == Done) tasks epics = length <| filter (\t -> taskType t == Epic) tasks readyCount' = readyCount - blockedCount = total - readyCount' - done + blockedCount = total - readyCount' - done - draft byPriority = [ (P0, length <| filter (\t -> taskPriority t == P0) tasks), (P1, length <| filter (\t -> taskPriority t == P1) tasks), @@ -1042,6 +1047,7 @@ getTaskStats maybeEpicId = do pure TaskStats { totalTasks = total, + draftTasks = draft, openTasks = open, inProgressTasks = inProg, reviewTasks = review, @@ -1068,6 +1074,7 @@ showTaskStats maybeEpicId = do Just epicId -> putText <| "Task Statistics for Epic " <> epicId putText "" putText <| "Total tasks: " <> T.pack (show (totalTasks stats)) + putText <| " Draft: " <> T.pack (show (draftTasks stats)) putText <| " Open: " <> T.pack (show (openTasks stats)) putText <| " In Progress: " <> T.pack (show (inProgressTasks stats)) putText <| " Review: " <> T.pack (show (reviewTasks stats)) |
