← Back to task

Commit a1b335d7

commit a1b335d73202a2dc4e9ecf3f70b0efc74008e402
Author: Ben Sima <ben@bensima.com>
Date:   Wed Nov 26 05:56:59 2025

    Fix worker to run formatters before commit
    
    - Run ormolu --mode inplace on changed .hs files - Run hlint --refactor
    to auto-fix lint issues - Use tryCommit that returns Either instead
    of panicking - Prevents commit hook failures from hlint violations
    
    Task-Id: t-1o2g8gugkr1

diff --git a/Omni/Agent/Worker.hs b/Omni/Agent/Worker.hs
index 1cdeb6d4..0c15a572 100644
--- a/Omni/Agent/Worker.hs
+++ b/Omni/Agent/Worker.hs
@@ -7,7 +7,6 @@ import Alpha
 import qualified Data.Text as Text
 import qualified Data.Text.IO as TIO
 import qualified Omni.Agent.Core as Core
-import qualified Omni.Agent.Git as Git
 import qualified Omni.Agent.Log as AgentLog
 import qualified Omni.Task.Core as TaskCore
 import qualified System.Directory as Directory
@@ -65,23 +64,90 @@ processTask worker task = do
     Exit.ExitSuccess -> do
       AgentLog.log "Agent finished successfully"
 
-      -- Update status to Review
-      TaskCore.updateTaskStatus tid TaskCore.Review []
+      -- Run formatting and linting before commit
+      AgentLog.updateActivity "Running formatters..."
+      formatResult <- runFormatters repo
 
-      -- Commit changes using Amp output (Gerrit-style trailer)
-      let commitMsg = formatCommitMessage output tid
-      Git.commit repo commitMsg
+      case formatResult of
+        Left err -> do
+          AgentLog.log ("Formatting failed: " <> err)
+          AgentLog.updateActivity "Format failed, task incomplete"
+        -- Don't update status, leave as InProgress for retry
+        Right () -> do
+          -- Update status to Review
+          TaskCore.updateTaskStatus tid TaskCore.Review []
 
-      -- Submit for review
-      AgentLog.updateActivity "Task completed"
+          -- Commit changes using Amp output (Gerrit-style trailer)
+          let commitMsg = formatCommitMessage output tid
+          commitResult <- tryCommit repo commitMsg
 
-      AgentLog.log ("[✓] Task " <> tid <> " completed")
-      AgentLog.update (\s -> s {AgentLog.statusTask = Nothing})
+          case commitResult of
+            Left commitErr -> do
+              AgentLog.log ("Commit failed: " <> commitErr)
+              AgentLog.updateActivity "Commit failed"
+            Right () -> do
+              AgentLog.updateActivity "Task completed"
+              AgentLog.log ("[✓] Task " <> tid <> " completed")
+              AgentLog.update (\s -> s {AgentLog.statusTask = Nothing})
     Exit.ExitFailure code -> do
       AgentLog.log ("Agent failed with code " <> tshow code)
       AgentLog.updateActivity "Agent failed, retrying..."
       threadDelay (10 * 1000000) -- Sleep 10s
 
+-- | Run ormolu and hlint --refactor on changed files
+runFormatters :: FilePath -> IO (Either Text ())
+runFormatters repo = do
+  -- Get list of changed .hs files
+  let diffCmd = (Process.proc "git" ["diff", "--name-only", "--cached", "HEAD"]) {Process.cwd = Just repo}
+  (_, diffOut, _) <- Process.readCreateProcessWithExitCode diffCmd ""
+
+  -- Also get untracked files
+  let untrackedCmd = (Process.proc "git" ["ls-files", "--others", "--exclude-standard"]) {Process.cwd = Just repo}
+  (_, untrackedOut, _) <- Process.readCreateProcessWithExitCode untrackedCmd ""
+
+  let changedFiles = Text.lines (Text.pack diffOut) ++ Text.lines (Text.pack untrackedOut)
+      allFiles = filter (Text.isSuffixOf ".hs") changedFiles
+
+  if null allFiles
+    then pure (Right ())
+    else do
+      -- Run ormolu on each file
+      forM_ allFiles <| \f -> do
+        let ormoluCmd = (Process.proc "ormolu" ["--mode", "inplace", Text.unpack f]) {Process.cwd = Just repo}
+        _ <- Process.readCreateProcessWithExitCode ormoluCmd ""
+        pure ()
+
+      -- Run hlint --refactor on each file
+      forM_ allFiles <| \f -> do
+        let hlintCmd = (Process.proc "hlint" ["--refactor", "--refactor-options=-i", Text.unpack f]) {Process.cwd = Just repo}
+        _ <- Process.readCreateProcessWithExitCode hlintCmd ""
+        pure ()
+
+      pure (Right ())
+
+-- | Try to commit, returning error message on failure
+tryCommit :: FilePath -> Text -> IO (Either Text ())
+tryCommit repo msg = do
+  -- Stage all changes
+  let addCmd = (Process.proc "git" ["add", "."]) {Process.cwd = Just repo}
+  (addCode, _, addErr) <- Process.readCreateProcessWithExitCode addCmd ""
+  case addCode of
+    Exit.ExitFailure _ -> pure <| Left (Text.pack addErr)
+    Exit.ExitSuccess -> do
+      -- Check for changes
+      let checkCmd = (Process.proc "git" ["diff", "--cached", "--quiet"]) {Process.cwd = Just repo}
+      (checkCode, _, _) <- Process.readCreateProcessWithExitCode checkCmd ""
+      case checkCode of
+        Exit.ExitSuccess -> pure (Right ()) -- Nothing to commit
+        Exit.ExitFailure 1 -> do
+          -- There are changes, commit them
+          let commitCmd = (Process.proc "git" ["commit", "-m", Text.unpack msg]) {Process.cwd = Just repo}
+          (commitCode, _, commitErr) <- Process.readCreateProcessWithExitCode commitCmd ""
+          case commitCode of
+            Exit.ExitSuccess -> pure (Right ())
+            Exit.ExitFailure _ -> pure <| Left (Text.pack commitErr)
+        Exit.ExitFailure c -> pure <| Left ("git diff failed with code " <> tshow c)
+
 runAmp :: FilePath -> TaskCore.Task -> IO (Exit.ExitCode, Text)
 runAmp repo task = do
   -- Check for retry context