summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBen Sima <ben@bensima.com>2025-11-27 13:44:47 -0500
committerBen Sima <ben@bensima.com>2025-11-27 13:44:47 -0500
commite47da20db9dd5f5d0a09ffcf2b17ad4e1d78115b (patch)
tree887257df85237985f07a799f8f6001a06e371e3c
parent1d8553c5ffd7f2a063078840886acb24153e9854 (diff)
Implement proper schema migrations for ALTER TABLE
All tests pass. The implementation adds: 1. **`runMigrations`** - Called at the end of `initTaskDb` to run migrat 2. **`migrateTable`** - Compares expected columns against existing colum 3. **`getTableColumns`** - Uses `PRAGMA table_info` to get existing colu 4. **`addColumn`** - Runs `ALTER TABLE ADD COLUMN` for missing columns 5. **Column definitions** - Lists of expected columns for `task_activity When new columns are added to the schema in the future, you just add the Task-Id: t-152.3
-rw-r--r--Omni/Task/Core.hs69
1 files changed, 69 insertions, 0 deletions
diff --git a/Omni/Task/Core.hs b/Omni/Task/Core.hs
index ffecd60..5af4ce4 100644
--- a/Omni/Task/Core.hs
+++ b/Omni/Task/Core.hs
@@ -374,6 +374,75 @@ initTaskDb = do
\ tokens_used INTEGER, \
\ FOREIGN KEY (task_id) REFERENCES tasks(id) \
\)"
+ runMigrations conn
+
+-- | Run schema migrations to add missing columns to existing tables
+runMigrations :: SQL.Connection -> IO ()
+runMigrations conn = do
+ migrateTable conn "task_activity" taskActivityColumns
+ migrateTable conn "tasks" tasksColumns
+ migrateTable conn "retry_context" retryContextColumns
+
+-- | Expected columns for task_activity table (name, type, nullable)
+taskActivityColumns :: [(Text, Text)]
+taskActivityColumns =
+ [ ("id", "INTEGER"),
+ ("task_id", "TEXT"),
+ ("timestamp", "DATETIME"),
+ ("stage", "TEXT"),
+ ("message", "TEXT"),
+ ("metadata", "TEXT"),
+ ("amp_thread_url", "TEXT"),
+ ("started_at", "DATETIME"),
+ ("completed_at", "DATETIME"),
+ ("cost_cents", "INTEGER"),
+ ("tokens_used", "INTEGER")
+ ]
+
+-- | Expected columns for tasks table
+tasksColumns :: [(Text, Text)]
+tasksColumns =
+ [ ("id", "TEXT"),
+ ("title", "TEXT"),
+ ("type", "TEXT"),
+ ("parent", "TEXT"),
+ ("namespace", "TEXT"),
+ ("status", "TEXT"),
+ ("priority", "TEXT"),
+ ("dependencies", "TEXT"),
+ ("description", "TEXT"),
+ ("created_at", "TIMESTAMP"),
+ ("updated_at", "TIMESTAMP")
+ ]
+
+-- | Expected columns for retry_context table
+retryContextColumns :: [(Text, Text)]
+retryContextColumns =
+ [ ("task_id", "TEXT"),
+ ("original_commit", "TEXT"),
+ ("conflict_files", "TEXT"),
+ ("attempt", "INTEGER"),
+ ("reason", "TEXT")
+ ]
+
+-- | Migrate a table by adding any missing columns
+migrateTable :: SQL.Connection -> Text -> [(Text, Text)] -> IO ()
+migrateTable conn tableName expectedCols = do
+ existingCols <- getTableColumns conn tableName
+ let missingCols = filter (\(name, _) -> name `notElem` existingCols) expectedCols
+ traverse_ (addColumn conn tableName) missingCols
+
+-- | Get list of column names for a table using PRAGMA table_info
+getTableColumns :: SQL.Connection -> Text -> IO [Text]
+getTableColumns conn tableName = do
+ rows <- SQL.query conn "PRAGMA table_info(?)" (SQL.Only tableName) :: IO [(Int, Text, Text, Int, Maybe Text, Int)]
+ pure [colName | (_, colName, _, _, _, _) <- rows]
+
+-- | Add a column to a table
+addColumn :: SQL.Connection -> Text -> (Text, Text) -> IO ()
+addColumn conn tableName (colName, colType) = do
+ let sql = "ALTER TABLE " <> tableName <> " ADD COLUMN " <> colName <> " " <> colType
+ SQL.execute_ conn (SQL.Query sql)
-- Generate a sequential task ID (t-1, t-2, t-3, ...)
generateId :: IO Text