From 028bbda4282515b95a7555209d397aaf22d32244 Mon Sep 17 00:00:00 2001 From: Ben Sima Date: Thu, 20 Nov 2025 18:07:12 -0500 Subject: feat: implement t-PpYZt2 --- Omni/Task/Core.hs | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) (limited to 'Omni/Task') diff --git a/Omni/Task/Core.hs b/Omni/Task/Core.hs index f7b7915..798f8fe 100644 --- a/Omni/Task/Core.hs +++ b/Omni/Task/Core.hs @@ -111,6 +111,28 @@ generateId = do encoded = toBase62 (fromIntegral microseconds) pure <| "t-" <> T.pack encoded +-- Generate a child ID based on parent ID (e.g. "t-abc.1") +generateChildId :: Text -> IO Text +generateChildId parentId = do + tasks <- loadTasks + let children = filter (\t -> taskParent t == Just parentId) tasks + -- Find the max suffix + suffixes = mapMaybe (\t -> getSuffix parentId (taskId t)) children + nextSuffix = case suffixes of + [] -> 1 + s -> maximum s + 1 + pure <| parentId <> "." <> T.pack (show nextSuffix) + +getSuffix :: Text -> Text -> Maybe Int +getSuffix parent childId = + if parent `T.isPrefixOf` childId && T.length childId > T.length parent + then + let rest = T.drop (T.length parent) childId + in if T.head rest == '.' + then readMaybe (T.unpack (T.tail rest)) + else Nothing + else Nothing + -- Convert number to base62 (0-9, a-z, A-Z) toBase62 :: Integer -> String toBase62 0 = "0" @@ -192,7 +214,9 @@ saveTask task = do -- Create a new task createTask :: Text -> TaskType -> Maybe Text -> Maybe Text -> Priority -> [Dependency] -> IO Task createTask title taskType parent namespace priority deps = do - tid <- generateId + tid <- case parent of + Nothing -> generateId + Just pid -> generateChildId pid now <- getCurrentTime let task = Task -- cgit v1.2.3 From cb37c2632cf945c1993d8b338abb1ce35899d5de Mon Sep 17 00:00:00 2001 From: Ben Sima Date: Thu, 20 Nov 2025 18:15:16 -0500 Subject: feat: implement t-PpYZt2 --- Omni/Task/Core.hs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) (limited to 'Omni/Task') diff --git a/Omni/Task/Core.hs b/Omni/Task/Core.hs index 798f8fe..31c0981 100644 --- a/Omni/Task/Core.hs +++ b/Omni/Task/Core.hs @@ -117,7 +117,7 @@ generateChildId parentId = do tasks <- loadTasks let children = filter (\t -> taskParent t == Just parentId) tasks -- Find the max suffix - suffixes = mapMaybe (\t -> getSuffix parentId (taskId t)) children + suffixes = mapMaybe (getSuffix parentId <. taskId) children nextSuffix = case suffixes of [] -> 1 s -> maximum s + 1 @@ -127,10 +127,10 @@ getSuffix :: Text -> Text -> Maybe Int getSuffix parent childId = if parent `T.isPrefixOf` childId && T.length childId > T.length parent then - let rest = T.drop (T.length parent) childId + let rest = T.drop (T.length parent) childId in if T.head rest == '.' - then readMaybe (T.unpack (T.tail rest)) - else Nothing + then readMaybe (T.unpack (T.tail rest)) + else Nothing else Nothing -- Convert number to base62 (0-9, a-z, A-Z) @@ -214,9 +214,7 @@ saveTask task = do -- Create a new task createTask :: Text -> TaskType -> Maybe Text -> Maybe Text -> Priority -> [Dependency] -> IO Task createTask title taskType parent namespace priority deps = do - tid <- case parent of - Nothing -> generateId - Just pid -> generateChildId pid + tid <- maybe generateId generateChildId parent now <- getCurrentTime let task = Task -- cgit v1.2.3 From 0d8ba7ec8b7b06a490eb7f2d625e169b2ed0ad72 Mon Sep 17 00:00:00 2001 From: Ben Sima Date: Thu, 20 Nov 2025 18:20:09 -0500 Subject: feat: implement t-PpYZt2 --- Omni/Task/Core.hs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'Omni/Task') diff --git a/Omni/Task/Core.hs b/Omni/Task/Core.hs index 31c0981..54ed04d 100644 --- a/Omni/Task/Core.hs +++ b/Omni/Task/Core.hs @@ -111,7 +111,8 @@ generateId = do encoded = toBase62 (fromIntegral microseconds) pure <| "t-" <> T.pack encoded --- Generate a child ID based on parent ID (e.g. "t-abc.1") +-- Generate a child ID based on parent ID (e.g. "t-abc.1", "t-abc.1.2") +-- Finds the next available sequential suffix among existing children. generateChildId :: Text -> IO Text generateChildId parentId = do tasks <- loadTasks -- cgit v1.2.3 From feda8ac221d88650850a3eaac2fbe2f2b215beac Mon Sep 17 00:00:00 2001 From: Ben Sima Date: Thu, 20 Nov 2025 18:39:25 -0500 Subject: feat: implement t-PpYZt2 --- Omni/Task/Core.hs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) (limited to 'Omni/Task') diff --git a/Omni/Task/Core.hs b/Omni/Task/Core.hs index 54ed04d..525ceb4 100644 --- a/Omni/Task/Core.hs +++ b/Omni/Task/Core.hs @@ -13,7 +13,8 @@ import qualified Data.ByteString.Lazy.Char8 as BLC import qualified Data.List as List import qualified Data.Text as T import qualified Data.Text.IO as TIO -import Data.Time (UTCTime, diffTimeToPicoseconds, getCurrentTime, utctDayTime) +import Data.Time (UTCTime, diffTimeToPicoseconds, getCurrentTime, utctDayTime, utctDay) +import Data.Time.Calendar (toModifiedJulianDay) import GHC.Generics () import System.Directory (createDirectoryIfMissing, doesFileExist) import System.Environment (lookupEnv) @@ -104,11 +105,15 @@ initTaskDb = do generateId :: IO Text generateId = do now <- getCurrentTime - -- Convert current time to microseconds since midnight - let dayTime = utctDayTime now - microseconds = diffTimeToPicoseconds dayTime `div` 1000000 - -- Convert to base62 for shorter IDs - encoded = toBase62 (fromIntegral microseconds) + -- Convert current time to microseconds since epoch (using MJD) + let day = utctDay now + dayTime = utctDayTime now + mjd = toModifiedJulianDay day + micros = diffTimeToPicoseconds dayTime `div` 1000000 + -- Combine MJD and micros to ensure uniqueness across days. + -- Multiplier 10^11 (100,000 seconds) is safe for any day length. + totalMicros = (mjd * 100000000000) + micros + encoded = toBase62 totalMicros pure <| "t-" <> T.pack encoded -- Generate a child ID based on parent ID (e.g. "t-abc.1", "t-abc.1.2") -- cgit v1.2.3 From 809c0009da015588ffbf8c4221df04c13aacc215 Mon Sep 17 00:00:00 2001 From: Ben Sima Date: Thu, 20 Nov 2025 19:06:41 -0500 Subject: feat: implement t-PpYZt2 --- Omni/Task/Core.hs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'Omni/Task') diff --git a/Omni/Task/Core.hs b/Omni/Task/Core.hs index 525ceb4..98ef6f9 100644 --- a/Omni/Task/Core.hs +++ b/Omni/Task/Core.hs @@ -121,9 +121,10 @@ generateId = do generateChildId :: Text -> IO Text generateChildId parentId = do tasks <- loadTasks - let children = filter (\t -> taskParent t == Just parentId) tasks - -- Find the max suffix - suffixes = mapMaybe (getSuffix parentId <. taskId) children + -- Find the max suffix among ALL tasks that look like children (to avoid ID collisions) + -- We check all tasks, not just those with taskParent set, because we want to ensure + -- ID uniqueness even if the parent link is missing. + let suffixes = mapMaybe (getSuffix parentId <. taskId) tasks nextSuffix = case suffixes of [] -> 1 s -> maximum s + 1 -- cgit v1.2.3