← Back to task

Commit 483114d4

commit 483114d458d0828ab24b81fa1f932947cff198c8
Author: Ben Sima <ben@bensima.com>
Date:   Fri Jan 2 00:05:02 2026

    Omni/Agent/Telegram: Rename Orchestrator.hs to Developer.hs
    
    'Orchestrator' was too vague and overloaded. 'Developer' better
    describes its role as the Telegram bot that helps with development tasks.
    
    Task-Id: t-340

diff --git a/Omni/Agent/Telegram.hs b/Omni/Agent/Telegram.hs
index 6525e3fd..b205e62f 100644
--- a/Omni/Agent/Telegram.hs
+++ b/Omni/Agent/Telegram.hs
@@ -99,10 +99,10 @@ import qualified Omni.Agent.Skills as Skills
 import qualified Omni.Agent.Subagent as Subagent
 import qualified Omni.Agent.Subagent.Jobs as Jobs
 import qualified Omni.Agent.Telegram.Actions as Actions
+import qualified Omni.Agent.Telegram.Developer as Developer
 import qualified Omni.Agent.Telegram.IncomingQueue as IncomingQueue
 import qualified Omni.Agent.Telegram.Media as Media
 import qualified Omni.Agent.Telegram.Messages as Messages
-import qualified Omni.Agent.Telegram.Orchestrator as Orchestrator
 import qualified Omni.Agent.Telegram.Reminders as Reminders
 import qualified Omni.Agent.Telegram.Types as Types
 import qualified Omni.Agent.Tools as Tools
@@ -117,11 +117,11 @@ import qualified Omni.Agent.Tools.Outreach as Outreach
 import qualified Omni.Agent.Tools.Pdf as Pdf
 import qualified Omni.Agent.Tools.Python as Python
 import qualified Omni.Agent.Tools.Tasks as Tasks
-import qualified Omni.Task.Core as Task
 import qualified Omni.Agent.Tools.Todos as Todos
 import qualified Omni.Agent.Tools.WebReader as WebReader
 import qualified Omni.Agent.Tools.WebSearch as WebSearch
 import qualified Omni.Ava.Trace as Trace
+import qualified Omni.Task.Core as Task
 import qualified Omni.Test as Test
 import System.Environment (lookupEnv)
 import Text.Printf (printf)
@@ -1772,9 +1772,10 @@ handleReadyCommand :: Int -> Maybe Int -> Text -> IO Bool
 handleReadyCommand chatId mThreadId cmd
   | cmd == "/ready" || cmd == "/ready " || cmd == "ready" || cmd == "show ready" || cmd == "what's ready" = do
       readyTasks <- Task.getReadyTasks
-      let msg = if null readyTasks
-            then "No tasks ready right now."
-            else formatReadyTasks (take 10 readyTasks)
+      let msg =
+            if null readyTasks
+              then "No tasks ready right now."
+              else formatReadyTasks (take 10 readyTasks)
       _ <- Messages.enqueueImmediate Nothing chatId mThreadId msg (Just "system") Nothing
       pure True
   | otherwise = pure False
@@ -1784,10 +1785,14 @@ handleReadyCommand chatId mThreadId cmd
       "📋 *Ready tasks:*\n\n"
         <> Text.intercalate "\n" (map formatTask tasks)
         <> "\n\n_Reply with a task ID to start work._"
-    
+
     formatTask :: Task.Task -> Text
     formatTask t =
-      "• `" <> Task.taskId t <> "` [" <> tshow (Task.taskPriority t) <> "] "
+      "• `"
+        <> Task.taskId t
+        <> "` ["
+        <> tshow (Task.taskPriority t)
+        <> "] "
         <> Text.take 50 (Task.taskTitle t)
         <> if Text.length (Task.taskTitle t) > 50 then "..." else ""
 
@@ -1795,7 +1800,7 @@ handleReadyCommand chatId mThreadId cmd
 handleStatusCommand :: Int -> Maybe Int -> Text -> IO Bool
 handleStatusCommand chatId mThreadId cmd
   | cmd == "/status" || cmd == "/status " || cmd == "status" || cmd == "what are you working on" = do
-      mStatus <- Orchestrator.getOrchestratorStatus chatId
+      mStatus <- Developer.getOrchestratorStatus chatId
       let msg = case mStatus of
             Nothing -> "I'm not working on anything right now. Send me a task ID to start work."
             Just status -> formatOrchestratorStatus status
@@ -1803,38 +1808,46 @@ handleStatusCommand chatId mThreadId cmd
       pure True
   | otherwise = pure False
   where
-    formatOrchestratorStatus :: Orchestrator.OrchestratorStatus -> Text
+    formatOrchestratorStatus :: Developer.OrchestratorStatus -> Text
     formatOrchestratorStatus status =
-      "🔧 *Working on " <> Orchestrator.osTaskId status <> "*\n"
-        <> Orchestrator.osTaskTitle status <> "\n\n"
-        <> "Phase: " <> formatPhaseSimple (Orchestrator.osPhase status) <> "\n"
-        <> "Iteration: " <> tshow (Orchestrator.osIteration status) <> "/" <> tshow (Orchestrator.osMaxIterations status)
-    
-    formatPhaseSimple :: Orchestrator.OrchestratorPhase -> Text
+      "🔧 *Working on "
+        <> Developer.osTaskId status
+        <> "*\n"
+        <> Developer.osTaskTitle status
+        <> "\n\n"
+        <> "Phase: "
+        <> formatPhaseSimple (Developer.osPhase status)
+        <> "\n"
+        <> "Iteration: "
+        <> tshow (Developer.osIteration status)
+        <> "/"
+        <> tshow (Developer.osMaxIterations status)
+
+    formatPhaseSimple :: Developer.OrchestratorPhase -> Text
     formatPhaseSimple = \case
-      Orchestrator.PhaseStarting -> "Starting"
-      Orchestrator.PhaseCoder _ -> "Coder running"
-      Orchestrator.PhaseCoderDone _ -> "Coder done"
-      Orchestrator.PhaseVerifyBuild _ -> "Verifying build"
-      Orchestrator.PhaseBuildPassed _ -> "Build passed"
-      Orchestrator.PhaseBuildFailed _ _ -> "Build failed"
-      Orchestrator.PhaseReviewer _ -> "Reviewer running"
-      Orchestrator.PhaseReviewerApproved -> "Reviewer approved"
-      Orchestrator.PhaseReviewerRejected _ _ -> "Reviewer rejected"
-      Orchestrator.PhaseCommitting -> "Committing"
-      Orchestrator.PhaseComplete _ -> "Complete"
-      Orchestrator.PhaseFailed _ -> "Failed"
+      Developer.PhaseStarting -> "Starting"
+      Developer.PhaseCoder _ -> "Coder running"
+      Developer.PhaseCoderDone _ -> "Coder done"
+      Developer.PhaseVerifyBuild _ -> "Verifying build"
+      Developer.PhaseBuildPassed _ -> "Build passed"
+      Developer.PhaseBuildFailed _ _ -> "Build failed"
+      Developer.PhaseReviewer _ -> "Reviewer running"
+      Developer.PhaseReviewerApproved -> "Reviewer approved"
+      Developer.PhaseReviewerRejected _ _ -> "Reviewer rejected"
+      Developer.PhaseCommitting -> "Committing"
+      Developer.PhaseComplete _ -> "Complete"
+      Developer.PhaseFailed _ -> "Failed"
 
 -- | Handle stop/cancel command - stop current orchestrator work
 handleStopCommand :: Types.TelegramConfig -> Int -> Maybe Int -> Text -> IO Bool
 handleStopCommand tgConfig chatId mThreadId cmd
   | cmd == "stop" || cmd == "/stop" || cmd == "cancel" || cmd == "/cancel" || cmd == "stop work" = do
-      result <- Orchestrator.stopOrchestrator chatId
+      result <- Developer.stopOrchestrator chatId
       case result of
-        Orchestrator.StopNotRunning -> do
+        Developer.StopNotRunning -> do
           _ <- Messages.enqueueImmediate Nothing chatId mThreadId "I'm not working on anything right now." (Just "system") Nothing
           pure True
-        Orchestrator.StopSuccess taskId -> do
+        Developer.StopSuccess taskId -> do
           let msg = "⛔ Stopped work on " <> taskId <> ". Changes are uncommitted."
               keyboard =
                 Types.InlineKeyboardMarkup
diff --git a/Omni/Agent/Telegram/Orchestrator.hs b/Omni/Agent/Telegram/Developer.hs
similarity index 79%
rename from Omni/Agent/Telegram/Orchestrator.hs
rename to Omni/Agent/Telegram/Developer.hs
index 3b651239..7acc1935 100644
--- a/Omni/Agent/Telegram/Orchestrator.hs
+++ b/Omni/Agent/Telegram/Developer.hs
@@ -18,7 +18,7 @@
 -- : dep aeson
 -- : dep async
 -- : dep http-conduit
-module Omni.Agent.Telegram.Orchestrator
+module Omni.Agent.Telegram.Developer
   ( -- * Configuration
     OrchestratorConfig (..),
     defaultConfig,
@@ -35,7 +35,7 @@ module Omni.Agent.Telegram.Orchestrator
     OrchestratorStatus (..),
     getActiveOrchestrators,
     getOrchestratorStatus,
-    
+
     -- * Stopping
     stopOrchestrator,
     StopResult (..),
@@ -48,23 +48,23 @@ where
 
 import Alpha
 import qualified Control.Concurrent.Async as Async
+import qualified Control.Concurrent.STM as STM
+import qualified Control.Exception as Exception
+import Data.Aeson ((.:), (.=))
 import qualified Data.Aeson as Aeson
-import Data.Aeson ((.=), (.:))
+import qualified Data.Map.Strict as Map
 import qualified Data.Text as Text
 import qualified Data.Text.IO as TextIO
+import qualified Data.Time as Time
 import qualified Network.HTTP.Simple as HTTP
 import qualified Omni.Agent.Telegram.Types as Types
 import qualified Omni.Task.Core as Task
 import qualified Omni.Test as Test
 import qualified System.Exit as Exit
-import qualified System.Process as Process
-import qualified System.Timeout as Timeout
 import System.IO (hFlush)
-import qualified Control.Concurrent.STM as STM
-import qualified Data.Map.Strict as Map
-import qualified Data.Time as Time
 import System.IO.Unsafe (unsafePerformIO)
-import qualified Control.Exception as Exception
+import qualified System.Process as Process
+import qualified System.Timeout as Timeout
 
 -- | Configuration for an orchestrator run
 data OrchestratorConfig = OrchestratorConfig
@@ -72,7 +72,7 @@ data OrchestratorConfig = OrchestratorConfig
     orchChatId :: Int,
     orchThreadId :: Maybe Int,
     orchMaxIterations :: Int,
-    orchCoderTimeout :: Int,    -- seconds
+    orchCoderTimeout :: Int, -- seconds
     orchReviewerTimeout :: Int, -- seconds
     orchWorkDir :: FilePath
   }
@@ -106,8 +106,8 @@ data OrchestratorStatus = OrchestratorStatus
 
 -- | Result of stopping an orchestrator
 data StopResult
-  = StopSuccess Text  -- Task ID that was stopped
-  | StopNotRunning    -- No orchestrator was running
+  = StopSuccess Text -- Task ID that was stopped
+  | StopNotRunning -- No orchestrator was running
   deriving (Show, Eq)
 
 -- | Global registry of active orchestrators (keyed by chatId)
@@ -117,23 +117,27 @@ activeOrchestrators = unsafePerformIO <| STM.newTVarIO Map.empty
 
 -- | Register an orchestrator as active
 registerOrchestrator :: OrchestratorStatus -> IO ()
-registerOrchestrator status = STM.atomically <|
-  STM.modifyTVar' activeOrchestrators (Map.insert (osChatId status) status)
+registerOrchestrator status =
+  STM.atomically
+    <| STM.modifyTVar' activeOrchestrators (Map.insert (osChatId status) status)
 
 -- | Update the phase of an active orchestrator
 updateOrchestratorPhase :: Int -> OrchestratorPhase -> Int -> IO ()
-updateOrchestratorPhase chatId newPhase iteration = STM.atomically <|
-  STM.modifyTVar' activeOrchestrators (Map.adjust (\s -> s { osPhase = newPhase, osIteration = iteration }) chatId)
+updateOrchestratorPhase chatId newPhase iteration =
+  STM.atomically
+    <| STM.modifyTVar' activeOrchestrators (Map.adjust (\s -> s {osPhase = newPhase, osIteration = iteration}) chatId)
 
 -- | Update the current process handle for an orchestrator
 updateOrchestratorProcess :: Int -> Maybe Process.ProcessHandle -> IO ()
-updateOrchestratorProcess chatId mHandle = STM.atomically <|
-  STM.modifyTVar' activeOrchestrators (Map.adjust (\s -> s { osCurrentProcess = mHandle }) chatId)
+updateOrchestratorProcess chatId mHandle =
+  STM.atomically
+    <| STM.modifyTVar' activeOrchestrators (Map.adjust (\s -> s {osCurrentProcess = mHandle}) chatId)
 
 -- | Remove an orchestrator from active registry
 unregisterOrchestrator :: Int -> IO ()
-unregisterOrchestrator chatId = STM.atomically <|
-  STM.modifyTVar' activeOrchestrators (Map.delete chatId)
+unregisterOrchestrator chatId =
+  STM.atomically
+    <| STM.modifyTVar' activeOrchestrators (Map.delete chatId)
 
 -- | Get all active orchestrators
 getActiveOrchestrators :: IO [OrchestratorStatus]
@@ -157,7 +161,7 @@ stopOrchestrator chatId = do
           -- Use interruptProcessGroupOf to also kill child processes
           Process.interruptProcessGroupOf ph
           -- Give it a moment, then force terminate
-          threadDelay 100000  -- 100ms
+          threadDelay 100000 -- 100ms
           Process.terminateProcess ph
         Nothing -> pure ()
       -- Unregister the orchestrator
@@ -167,7 +171,7 @@ stopOrchestrator chatId = do
 -- | Current phase of the orchestrator
 data OrchestratorPhase
   = PhaseStarting
-  | PhaseCoder Int         -- iteration
+  | PhaseCoder Int -- iteration
   | PhaseCoderDone Int
   | PhaseVerifyBuild Int
   | PhaseBuildPassed Int
@@ -176,16 +180,16 @@ data OrchestratorPhase
   | PhaseReviewerApproved
   | PhaseReviewerRejected Int Text
   | PhaseCommitting
-  | PhaseComplete Text     -- commit hash
+  | PhaseComplete Text -- commit hash
   | PhaseFailed Text
   deriving (Show, Eq, Generic)
 
 -- | Format a phase as user-friendly status text
 formatPhase :: Text -> Int -> OrchestratorPhase -> Text
 formatPhase tid maxIter = \case
-  PhaseStarting -> 
+  PhaseStarting ->
     "🔧 " <> tid <> ": Starting orchestrator..."
-  PhaseCoder n -> 
+  PhaseCoder n ->
     "🔧 " <> tid <> ": Running coder... (iteration " <> tshow n <> "/" <> tshow maxIter <> ")"
   PhaseCoderDone n ->
     "🔧 " <> tid <> ": Coder complete (iteration " <> tshow n <> "/" <> tshow maxIter <> ")"
@@ -218,21 +222,21 @@ spawnOrchestrator :: Types.TelegramConfig -> OrchestratorConfig -> IO (Async.Asy
 spawnOrchestrator tgCfg cfg = do
   putText <| "spawnOrchestrator: About to spawn async for " <> orchTaskId cfg
   hFlush stdout
-  
+
   -- Look up task title for status display (do this BEFORE spawning)
   allTasks <- Task.loadTasks
   let mTask = Task.findTask (orchTaskId cfg) allTasks
       taskTitle = maybe "Unknown task" Task.taskTitle mTask
       chatId = orchChatId cfg
       tid = orchTaskId cfg
-  
+
   Async.async <| do
     -- Wrap ENTIRE body in exception handler + finally for cleanup
     let cleanup = do
           putText <| "Orchestrator cleanup running for " <> tid
           hFlush stdout
           unregisterOrchestrator chatId
-    
+
     Exception.finally (runOrchestratorThread tgCfg cfg taskTitle) cleanup
 
 -- | Inner orchestrator thread logic, separated for cleaner exception handling
@@ -241,50 +245,65 @@ runOrchestratorThread tgCfg cfg taskTitle = do
   let chatId = orchChatId cfg
       tid = orchTaskId cfg
       maxIter = orchMaxIterations cfg
-  
+
   -- Catch ANY exception and report it
-  result <- try @SomeException <| do
-    putText <| "Orchestrator async thread started for " <> tid
-    hFlush stdout
-    
-    -- Register this orchestrator
-    now <- Time.getCurrentTime
-    let status = OrchestratorStatus
-          { osTaskId = tid,
-            osTaskTitle = taskTitle,
-            osChatId = chatId,
-            osPhase = PhaseStarting,
-            osIteration = 0,
-            osMaxIterations = maxIter,
-            osStartedAt = now,
-            osCurrentProcess = Nothing
-          }
-    registerOrchestrator status
-    
-    -- Create initial status message
-    mMsgId <- createStatusMessage tgCfg chatId (orchThreadId cfg)
-      (formatPhase tid maxIter PhaseStarting)
-    putText <| "Orchestrator: createStatusMessage returned " <> tshow mMsgId
-    hFlush stdout
-    
-    case mMsgId of
-      Nothing -> do
-        -- Failed to create status message, send error
-        _ <- sendMessage tgCfg chatId (orchThreadId cfg)
-          ("❌ Failed to start orchestrator for " <> tid <> ": couldn't create status message")
-        pure ()
-      Just msgId -> do
-        -- Run the main orchestrator loop
-        runOrchestrator tgCfg cfg msgId
-  
+  result <-
+    try @SomeException <| do
+      putText <| "Orchestrator async thread started for " <> tid
+      hFlush stdout
+
+      -- Register this orchestrator
+      now <- Time.getCurrentTime
+      let status =
+            OrchestratorStatus
+              { osTaskId = tid,
+                osTaskTitle = taskTitle,
+                osChatId = chatId,
+                osPhase = PhaseStarting,
+                osIteration = 0,
+                osMaxIterations = maxIter,
+                osStartedAt = now,
+                osCurrentProcess = Nothing
+              }
+      registerOrchestrator status
+
+      -- Create initial status message
+      mMsgId <-
+        createStatusMessage
+          tgCfg
+          chatId
+          (orchThreadId cfg)
+          (formatPhase tid maxIter PhaseStarting)
+      putText <| "Orchestrator: createStatusMessage returned " <> tshow mMsgId
+      hFlush stdout
+
+      case mMsgId of
+        Nothing -> do
+          -- Failed to create status message, send error
+          _ <-
+            sendMessage
+              tgCfg
+              chatId
+              (orchThreadId cfg)
+              ("❌ Failed to start orchestrator for " <> tid <> ": couldn't create status message")
+          pure ()
+        Just msgId -> do
+          -- Run the main orchestrator loop
+          runOrchestrator tgCfg cfg msgId
+
   -- Handle any exception that occurred
   case result of
     Left err -> do
       putText <| "Orchestrator " <> tid <> " crashed with exception: " <> tshow err
       hFlush stdout
       -- Try to notify user (this might also fail, but at least we try)
-      _ <- try @SomeException <| sendMessage tgCfg chatId (orchThreadId cfg)
-        ("❌ " <> tid <> " orchestrator crashed: " <> Text.take 200 (tshow err))
+      _ <-
+        try @SomeException
+          <| sendMessage
+            tgCfg
+            chatId
+            (orchThreadId cfg)
+            ("❌ " <> tid <> " orchestrator crashed: " <> Text.take 200 (tshow err))
       pure ()
     Right () -> do
       putText <| "Orchestrator " <> tid <> " completed normally"
@@ -300,13 +319,13 @@ runOrchestrator tgCfg cfg statusMsgId = do
         updateStatusMessage tgCfg chatId statusMsgId (formatPhase tid maxIter p)
         updateOrchestratorPhase chatId p iter
       sendResult msg = sendMessage tgCfg chatId (orchThreadId cfg) msg
-  
+
   -- Run iterations
   result <- runLoop 1
-  
+
   -- Unregister this orchestrator
   unregisterOrchestrator chatId
-  
+
   -- Send final result as new message
   case result of
     Left err -> do
@@ -322,28 +341,31 @@ runOrchestrator tgCfg cfg statusMsgId = do
   where
     runLoop :: Int -> IO (Either Text Text)
     runLoop iteration
-      | iteration > orchMaxIterations cfg = 
+      | iteration > orchMaxIterations cfg =
           pure (Left "Max iterations exceeded")
       | otherwise = do
           let chatId = orchChatId cfg
               updatePhase' p = do
-                updateStatusMessage tgCfg chatId statusMsgId
+                updateStatusMessage
+                  tgCfg
+                  chatId
+                  statusMsgId
                   (formatPhase (orchTaskId cfg) (orchMaxIterations cfg) p)
                 updateOrchestratorPhase chatId p iteration
-          
+
           -- Phase: Coder
           updatePhase' (PhaseCoder iteration)
           coderResult <- runCoder cfg
-          
+
           case coderResult of
             Left err -> pure (Left ("Coder failed: " <> err))
             Right () -> do
               updatePhase' (PhaseCoderDone iteration)
-              
+
               -- Phase: Verify build
               updatePhase' (PhaseVerifyBuild iteration)
               buildResult <- runBuild cfg
-              
+
               case buildResult of
                 Left err -> do
                   updatePhase' (PhaseBuildFailed iteration err)
@@ -351,11 +373,11 @@ runOrchestrator tgCfg cfg statusMsgId = do
                   runLoop (iteration + 1)
                 Right () -> do
                   updatePhase' (PhaseBuildPassed iteration)
-                  
+
                   -- Phase: Reviewer
                   updatePhase' (PhaseReviewer iteration)
                   reviewResult <- runReviewer cfg
-                  
+
                   case reviewResult of
                     Left err -> do
                       updatePhase' (PhaseReviewerRejected iteration err)
@@ -368,35 +390,36 @@ runOrchestrator tgCfg cfg statusMsgId = do
 -- | Run the coder subprocess (pi-code.sh)
 runCoder :: OrchestratorConfig -> IO (Either Text ())
 runCoder cfg = do
-  let proc = (Process.proc "./Omni/Ide/pi-code.sh" [Text.unpack (orchTaskId cfg)])
-        { Process.cwd = Just (orchWorkDir cfg),
-          -- Inherit stdout/stderr so output goes to parent (visible in logs)
-          -- and we don't block on full pipe buffers
-          Process.std_out = Process.Inherit,
-          Process.std_err = Process.Inherit,
-          -- Create new process group so we can kill children
-          Process.create_group = True
-        }
-  
+  let proc =
+        (Process.proc "./Omni/Ide/pi-code.sh" [Text.unpack (orchTaskId cfg)])
+          { Process.cwd = Just (orchWorkDir cfg),
+            -- Inherit stdout/stderr so output goes to parent (visible in logs)
+            -- and we don't block on full pipe buffers
+            Process.std_out = Process.Inherit,
+            Process.std_err = Process.Inherit,
+            -- Create new process group so we can kill children
+            Process.create_group = True
+          }
+
   (_, _, _, ph) <- Process.createProcess proc
-  
+
   -- Register the process handle so it can be stopped
   updateOrchestratorProcess (orchChatId cfg) (Just ph)
-  
+
   -- Wait with timeout
   let timeoutMicros = orchCoderTimeout cfg * 1000000
   mExit <- Timeout.timeout timeoutMicros (Process.waitForProcess ph)
-  
+
   -- Clear the process handle
   updateOrchestratorProcess (orchChatId cfg) Nothing
-  
+
   case mExit of
     Nothing -> do
       -- Timeout - kill process
       Process.terminateProcess ph
       pure (Left "Coder timeout")
     Just Exit.ExitSuccess -> pure (Right ())
-    Just (Exit.ExitFailure code) -> 
+    Just (Exit.ExitFailure code) ->
       pure (Left ("Exit code " <> tshow code))
 
 -- | Run build verification (bild)
@@ -408,23 +431,24 @@ runBuild cfg = do
     Nothing -> pure (Left "Task not found")
     Just task -> do
       let namespace = fromMaybe "Omni/Ava.hs" (Task.taskNamespace task)
-          proc = (Process.proc "bild" [Text.unpack namespace])
-            { Process.cwd = Just (orchWorkDir cfg),
-              Process.std_out = Process.Inherit,
-              Process.std_err = Process.Inherit,
-              Process.create_group = True
-            }
-      
+          proc =
+            (Process.proc "bild" [Text.unpack namespace])
+              { Process.cwd = Just (orchWorkDir cfg),
+                Process.std_out = Process.Inherit,
+                Process.std_err = Process.Inherit,
+                Process.create_group = True
+              }
+
       (_, _, _, ph) <- Process.createProcess proc
-      
+
       -- Register the process handle
       updateOrchestratorProcess (orchChatId cfg) (Just ph)
-      
+
       exitCode <- Process.waitForProcess ph
-      
+
       -- Clear the process handle
       updateOrchestratorProcess (orchChatId cfg) Nothing
-      
+
       case exitCode of
         Exit.ExitSuccess -> pure (Right ())
         Exit.ExitFailure code ->
@@ -434,36 +458,38 @@ runBuild cfg = do
 -- Returns Right commitHash on approval, Left reason on rejection
 runReviewer :: OrchestratorConfig -> IO (Either Text Text)
 runReviewer cfg = do
-  let proc = (Process.proc "./Omni/Ide/pi-review.sh" [Text.unpack (orchTaskId cfg)])
-        { Process.cwd = Just (orchWorkDir cfg),
-          Process.std_out = Process.Inherit,
-          Process.std_err = Process.Inherit,
-          Process.create_group = True
-        }
-  
+  let proc =
+        (Process.proc "./Omni/Ide/pi-review.sh" [Text.unpack (orchTaskId cfg)])
+          { Process.cwd = Just (orchWorkDir cfg),
+            Process.std_out = Process.Inherit,
+            Process.std_err = Process.Inherit,
+            Process.create_group = True
+          }
+
   (_, _, _, ph) <- Process.createProcess proc
-  
+
   -- Register the process handle
   updateOrchestratorProcess (orchChatId cfg) (Just ph)
-  
+
   -- Wait with timeout
   let timeoutMicros = orchReviewerTimeout cfg * 1000000
   mExit <- Timeout.timeout timeoutMicros (Process.waitForProcess ph)
-  
+
   -- Clear the process handle
   updateOrchestratorProcess (orchChatId cfg) Nothing
-  
+
   case mExit of
     Nothing -> do
       Process.terminateProcess ph
       pure (Left "Reviewer timeout")
     Just Exit.ExitSuccess -> do
       -- Reviewer approved - get commit hash from git
-      (_, Just hOut, _, ph') <- Process.createProcess 
-        (Process.proc "git" ["rev-parse", "--short", "HEAD"])
-          { Process.cwd = Just (orchWorkDir cfg),
-            Process.std_out = Process.CreatePipe
-          }
+      (_, Just hOut, _, ph') <-
+        Process.createProcess
+          (Process.proc "git" ["rev-parse", "--short", "HEAD"])
+            { Process.cwd = Just (orchWorkDir cfg),
+              Process.std_out = Process.CreatePipe
+            }
       _ <- Process.waitForProcess ph'
       commit <- Text.strip <$> TextIO.hGetContents hOut
       pure (Right commit)
@@ -483,19 +509,19 @@ createStatusMessage cfg chatId mThreadId text = do
           <> Text.unpack (Types.tgBotToken cfg)
           <> "/sendMessage"
       body =
-        Aeson.object <|
-          [ "chat_id" .= chatId,
-            "text" .= text
-          ]
+        Aeson.object
+          <| [ "chat_id" .= chatId,
+               "text" .= text
+             ]
           ++ maybe [] (\tid -> ["message_thread_id" .= tid]) mThreadId
-  
+
   req0 <- HTTP.parseRequest url
   let req =
         HTTP.setRequestMethod "POST"
           <| HTTP.setRequestHeader "Content-Type" ["application/json"]
           <| HTTP.setRequestBodyLBS (Aeson.encode body)
           <| req0
-  
+
   putText <| "Orchestrator: Creating status message for chat " <> tshow chatId
   result <- try @SomeException (HTTP.httpLBS req)
   case result of
@@ -528,13 +554,14 @@ data TelegramResponse = TelegramResponse
   deriving (Show, Generic)
 
 instance Aeson.FromJSON TelegramResponse where
-  parseJSON = Aeson.withObject "TelegramResponse" <| \v -> do
-    ok <- v .: "ok"
-    mResult <- v Aeson..:? "result"
-    msgId <- case mResult of
-      Just res -> res Aeson..:? "message_id"
-      Nothing -> pure Nothing
-    pure TelegramResponse {tgRespOk = ok, tgRespMessageId = msgId}
+  parseJSON =
+    Aeson.withObject "TelegramResponse" <| \v -> do
+      ok <- v .: "ok"
+      mResult <- v Aeson..:? "result"
+      msgId <- case mResult of
+        Just res -> res Aeson..:? "message_id"
+        Nothing -> pure Nothing
+      pure TelegramResponse {tgRespOk = ok, tgRespMessageId = msgId}
 
 -- | Update an existing status message
 updateStatusMessage :: Types.TelegramConfig -> Int -> Int -> Text -> IO ()
@@ -550,14 +577,14 @@ updateStatusMessage cfg chatId messageId text = do
             "message_id" .= messageId,
             "text" .= text
           ]
-  
+
   req0 <- HTTP.parseRequest url
   let req =
         HTTP.setRequestMethod "POST"
           <| HTTP.setRequestHeader "Content-Type" ["application/json"]
           <| HTTP.setRequestBodyLBS (Aeson.encode body)
           <| req0
-  
+
   result <- try @SomeException (HTTP.httpLBS req)
   case result of
     Left err -> putText <| "Edit message failed: " <> tshow err
@@ -568,7 +595,7 @@ updateStatusMessage cfg chatId messageId text = do
 
 -- | Send a new message
 sendMessage :: Types.TelegramConfig -> Int -> Maybe Int -> Text -> IO (Maybe Int)
-sendMessage = createStatusMessage  -- Same implementation
+sendMessage = createStatusMessage -- Same implementation
 
 -- | Main for testing
 main :: IO ()
@@ -578,7 +605,7 @@ main = Test.run test
 test :: Test.Tree
 test =
   Test.group
-    "Omni.Agent.Telegram.Orchestrator"
+    "Omni.Agent.Telegram.Developer"
     [ Test.unit "formatPhase Starting" <| do
         formatPhase "t-123" 3 PhaseStarting
           Test.@?= "🔧 t-123: Starting orchestrator...",
diff --git a/Omni/Agent/Telegram/Telegram.hs b/Omni/Agent/Telegram/Telegram.hs
index 9c75b307..616b903d 100644
--- a/Omni/Agent/Telegram/Telegram.hs
+++ b/Omni/Agent/Telegram/Telegram.hs
@@ -99,10 +99,10 @@ import qualified Omni.Agent.Skills as Skills
 import qualified Omni.Agent.Subagent as Subagent
 import qualified Omni.Agent.Subagent.Jobs as Jobs
 import qualified Omni.Agent.Telegram.Actions as Actions
+import qualified Omni.Agent.Telegram.Developer as Developer
 import qualified Omni.Agent.Telegram.IncomingQueue as IncomingQueue
 import qualified Omni.Agent.Telegram.Media as Media
 import qualified Omni.Agent.Telegram.Messages as Messages
-import qualified Omni.Agent.Telegram.Orchestrator as Orchestrator
 import qualified Omni.Agent.Telegram.Reminders as Reminders
 import qualified Omni.Agent.Telegram.Types as Types
 import qualified Omni.Agent.Tools as Tools
@@ -117,11 +117,11 @@ import qualified Omni.Agent.Tools.Outreach as Outreach
 import qualified Omni.Agent.Tools.Pdf as Pdf
 import qualified Omni.Agent.Tools.Python as Python
 import qualified Omni.Agent.Tools.Tasks as Tasks
-import qualified Omni.Task.Core as Task
 import qualified Omni.Agent.Tools.Todos as Todos
 import qualified Omni.Agent.Tools.WebReader as WebReader
 import qualified Omni.Agent.Tools.WebSearch as WebSearch
 import qualified Omni.Ava.Trace as Trace
+import qualified Omni.Task.Core as Task
 import qualified Omni.Test as Test
 import System.Environment (lookupEnv)
 import Text.Printf (printf)
@@ -1766,9 +1766,10 @@ handleReadyCommand :: Int -> Maybe Int -> Text -> IO Bool
 handleReadyCommand chatId mThreadId cmd
   | cmd == "/ready" || cmd == "/ready " || cmd == "ready" || cmd == "show ready" || cmd == "what's ready" = do
       readyTasks <- Task.getReadyTasks
-      let msg = if null readyTasks
-            then "No tasks ready right now."
-            else formatReadyTasks (take 10 readyTasks)
+      let msg =
+            if null readyTasks
+              then "No tasks ready right now."
+              else formatReadyTasks (take 10 readyTasks)
       _ <- Messages.enqueueImmediate Nothing chatId mThreadId msg (Just "system") Nothing
       pure True
   | otherwise = pure False
@@ -1778,10 +1779,14 @@ handleReadyCommand chatId mThreadId cmd
       "📋 *Ready tasks:*\n\n"
         <> Text.intercalate "\n" (map formatTask tasks)
         <> "\n\n_Reply with a task ID to start work._"
-    
+
     formatTask :: Task.Task -> Text
     formatTask t =
-      "• `" <> Task.taskId t <> "` [" <> tshow (Task.taskPriority t) <> "] "
+      "• `"
+        <> Task.taskId t
+        <> "` ["
+        <> tshow (Task.taskPriority t)
+        <> "] "
         <> Text.take 50 (Task.taskTitle t)
         <> if Text.length (Task.taskTitle t) > 50 then "..." else ""
 
@@ -1789,7 +1794,7 @@ handleReadyCommand chatId mThreadId cmd
 handleStatusCommand :: Int -> Maybe Int -> Text -> IO Bool
 handleStatusCommand chatId mThreadId cmd
   | cmd == "/status" || cmd == "/status " || cmd == "status" || cmd == "what are you working on" = do
-      mStatus <- Orchestrator.getOrchestratorStatus chatId
+      mStatus <- Developer.getOrchestratorStatus chatId
       let msg = case mStatus of
             Nothing -> "I'm not working on anything right now. Send me a task ID to start work."
             Just status -> formatOrchestratorStatus status
@@ -1797,38 +1802,46 @@ handleStatusCommand chatId mThreadId cmd
       pure True
   | otherwise = pure False
   where
-    formatOrchestratorStatus :: Orchestrator.OrchestratorStatus -> Text
+    formatOrchestratorStatus :: Developer.OrchestratorStatus -> Text
     formatOrchestratorStatus status =
-      "🔧 *Working on " <> Orchestrator.osTaskId status <> "*\n"
-        <> Orchestrator.osTaskTitle status <> "\n\n"
-        <> "Phase: " <> formatPhaseSimple (Orchestrator.osPhase status) <> "\n"
-        <> "Iteration: " <> tshow (Orchestrator.osIteration status) <> "/" <> tshow (Orchestrator.osMaxIterations status)
-    
-    formatPhaseSimple :: Orchestrator.OrchestratorPhase -> Text
+      "🔧 *Working on "
+        <> Developer.osTaskId status
+        <> "*\n"
+        <> Developer.osTaskTitle status
+        <> "\n\n"
+        <> "Phase: "
+        <> formatPhaseSimple (Developer.osPhase status)
+        <> "\n"
+        <> "Iteration: "
+        <> tshow (Developer.osIteration status)
+        <> "/"
+        <> tshow (Developer.osMaxIterations status)
+
+    formatPhaseSimple :: Developer.OrchestratorPhase -> Text
     formatPhaseSimple = \case
-      Orchestrator.PhaseStarting -> "Starting"
-      Orchestrator.PhaseCoder _ -> "Coder running"
-      Orchestrator.PhaseCoderDone _ -> "Coder done"
-      Orchestrator.PhaseVerifyBuild _ -> "Verifying build"
-      Orchestrator.PhaseBuildPassed _ -> "Build passed"
-      Orchestrator.PhaseBuildFailed _ _ -> "Build failed"
-      Orchestrator.PhaseReviewer _ -> "Reviewer running"
-      Orchestrator.PhaseReviewerApproved -> "Reviewer approved"
-      Orchestrator.PhaseReviewerRejected _ _ -> "Reviewer rejected"
-      Orchestrator.PhaseCommitting -> "Committing"
-      Orchestrator.PhaseComplete _ -> "Complete"
-      Orchestrator.PhaseFailed _ -> "Failed"
+      Developer.PhaseStarting -> "Starting"
+      Developer.PhaseCoder _ -> "Coder running"
+      Developer.PhaseCoderDone _ -> "Coder done"
+      Developer.PhaseVerifyBuild _ -> "Verifying build"
+      Developer.PhaseBuildPassed _ -> "Build passed"
+      Developer.PhaseBuildFailed _ _ -> "Build failed"
+      Developer.PhaseReviewer _ -> "Reviewer running"
+      Developer.PhaseReviewerApproved -> "Reviewer approved"
+      Developer.PhaseReviewerRejected _ _ -> "Reviewer rejected"
+      Developer.PhaseCommitting -> "Committing"
+      Developer.PhaseComplete _ -> "Complete"
+      Developer.PhaseFailed _ -> "Failed"
 
 -- | Handle stop/cancel command - stop current orchestrator work
 handleStopCommand :: Types.TelegramConfig -> Int -> Maybe Int -> Text -> IO Bool
 handleStopCommand tgConfig chatId mThreadId cmd
   | cmd == "stop" || cmd == "/stop" || cmd == "cancel" || cmd == "/cancel" || cmd == "stop work" = do
-      result <- Orchestrator.stopOrchestrator chatId
+      result <- Developer.stopOrchestrator chatId
       case result of
-        Orchestrator.StopNotRunning -> do
+        Developer.StopNotRunning -> do
           _ <- Messages.enqueueImmediate Nothing chatId mThreadId "I'm not working on anything right now." (Just "system") Nothing
           pure True
-        Orchestrator.StopSuccess taskId -> do
+        Developer.StopSuccess taskId -> do
           let msg = "⛔ Stopped work on " <> taskId <> ". Changes are uncommitted."
               keyboard =
                 Types.InlineKeyboardMarkup
diff --git a/Omni/Agent/Tools/Tasks.hs b/Omni/Agent/Tools/Tasks.hs
index 591167c6..06a65cdb 100644
--- a/Omni/Agent/Tools/Tasks.hs
+++ b/Omni/Agent/Tools/Tasks.hs
@@ -39,7 +39,7 @@ import qualified Data.Aeson.KeyMap as KeyMap
 import qualified Data.Text as Text
 -- import Data.Time (getCurrentTime)
 import qualified Omni.Agent.Engine as Engine
-import qualified Omni.Agent.Telegram.Orchestrator as Orchestrator
+import qualified Omni.Agent.Telegram.Developer as Developer
 import qualified Omni.Agent.Telegram.Types as TgTypes
 import qualified Omni.Task.Core as Task
 import qualified Omni.Test as Test
@@ -126,10 +126,10 @@ executeWorkOnTask ctx v = do
                   -- Spawn orchestrator with Telegram updates
                   putText <| "work_on_task: Spawning orchestrator for " <> tid <> " in chat " <> tshow (ttcChatId ctx)
                   let orchConfig =
-                        (Orchestrator.defaultConfig tid (ttcChatId ctx))
-                          { Orchestrator.orchThreadId = ttcThreadId ctx
+                        (Developer.defaultConfig tid (ttcChatId ctx))
+                          { Developer.orchThreadId = ttcThreadId ctx
                           }
-                  _asyncHandle <- Orchestrator.spawnOrchestrator (ttcTelegramConfig ctx) orchConfig
+                  _asyncHandle <- Developer.spawnOrchestrator (ttcTelegramConfig ctx) orchConfig
                   putText <| "work_on_task: Orchestrator spawned, async handle created"
                   -- Return immediately - orchestrator runs async
                   pure
@@ -368,15 +368,17 @@ executeCreateTask v = do
           deps = case ctaDiscoveredFrom args of
             Just dfid -> [Task.Dependency dfid Task.DiscoveredFrom]
             Nothing -> []
-      result <- try @SomeException <| Task.createTask
-        (ctaTitle args)
-        Task.WorkTask
-        (ctaParentId args)
-        (ctaNamespace args)
-        priority
-        Nothing  -- complexity
-        deps
-        description
+      result <-
+        try @SomeException
+          <| Task.createTask
+            (ctaTitle args)
+            Task.WorkTask
+            (ctaParentId args)
+            (ctaNamespace args)
+            priority
+            Nothing -- complexity
+            deps
+            description
       case result of
         Left err ->
           pure <| Aeson.object ["error" .= tshow err]