diff options
| author | Ben Sima <ben@bensima.com> | 2025-11-22 13:21:59 -0500 |
|---|---|---|
| committer | Ben Sima <ben@bensima.com> | 2025-11-22 13:21:59 -0500 |
| commit | 37931a412eb300edc21188678dee984a4f5d1b3a (patch) | |
| tree | 5e016af37ac60db6a77a8343b467365d3ec0b78f /Omni/Task/Core.hs | |
| parent | 6f4b2c97a24967508f3970b46999052fd1f44e67 (diff) | |
feat: implement t-rWcpygi7d
The task was to verify that lowercase task IDs are accepted and do
not clash with old tasks.
I have verified this by: 1. **Analyzing the codebase**: I explored
`Omni/Task.hs` and `Omni/Task/Core.hs` to understand how task
IDs are generated, stored, and compared. I found that `matchesId`
implements case-insensitive comparison, which means `t-abc` and
`t-ABC` are effectively the same task ID in lookup operations. 2.
**Updating Core Logic**: I modified `Omni/Task/Core.hs` (via a
sub-agent operation that I verified) to:
* Implement `toBase36` for ID generation, ensuring new
IDs use only lowercase characters (0-9, a-z). * Implement
`generateUniqueId` which checks for collisions against existing
tasks (case-insensitively) before assigning a new ID. * Update
`createTask` to use `generateUniqueId` instead of `generateId`.
3. **Adding Tests**: I modified `Omni/Task.hs` to add unit tests
that explicitly verify:
* Lowercase task IDs can be created and found. * `generateId`
(now unique and lowercase) produces valid IDs that can be found.
4. **Verifying**: I ran `bild --test Omni/Task.hs` and confirmed
that all tests pass, including the new ones.
The system now generates lowercase IDs by default and actively
prevents collisions with any existing mixed-case IDs, satisfying
the requirements.
Task Status: Done (Ready to be closed by system/user) Changes are
committed to local files.
Diffstat (limited to 'Omni/Task/Core.hs')
| -rw-r--r-- | Omni/Task/Core.hs | 30 |
1 files changed, 21 insertions, 9 deletions
diff --git a/Omni/Task/Core.hs b/Omni/Task/Core.hs index bab1912..a2e76b6 100644 --- a/Omni/Task/Core.hs +++ b/Omni/Task/Core.hs @@ -176,7 +176,7 @@ withTaskReadLock action = action ) --- Generate a short ID using base62 encoding of timestamp +-- Generate a short ID using base36 encoding of timestamp (lowercase) generateId :: IO Text generateId = do now <- getCurrentTime @@ -188,7 +188,7 @@ generateId = do -- 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 + encoded = toBase36 totalMicros pure <| "t-" <> T.pack encoded -- Generate a child ID based on parent ID (e.g. "t-abc.1", "t-abc.1.2") @@ -220,15 +220,15 @@ getSuffix parent childId = else Nothing else Nothing --- Convert number to base62 (0-9, a-z, A-Z) -toBase62 :: Integer -> String -toBase62 0 = "0" -toBase62 n = reverse <| go n +-- Convert number to base36 (0-9, a-z) +toBase36 :: Integer -> String +toBase36 0 = "0" +toBase36 n = reverse <| go n where - alphabet = ['0' .. '9'] ++ ['a' .. 'z'] ++ ['A' .. 'Z'] + alphabet = ['0' .. '9'] ++ ['a' .. 'z'] go 0 = [] go x = - let (q, r) = x `divMod` 62 + let (q, r) = x `divMod` 36 idx = fromIntegral r char = case drop idx alphabet of (c : _) -> c @@ -320,7 +320,7 @@ createTask :: Text -> TaskType -> Maybe Text -> Maybe Text -> Priority -> [Depen createTask title taskType parent namespace priority deps description = withTaskWriteLock <| do tid <- case parent of - Nothing -> generateId + Nothing -> generateUniqueId Just pid -> do tasks <- loadTasksInternal pure <| computeNextChildId tasks pid @@ -342,6 +342,18 @@ createTask title taskType parent namespace priority deps description = saveTaskInternal task pure task +-- Generate a unique ID (checking against existing tasks) +generateUniqueId :: IO Text +generateUniqueId = do + tasks <- loadTasksInternal + go tasks + where + go tasks = do + tid <- generateId + case findTask tid tasks of + Nothing -> pure tid + Just _ -> go tasks -- Retry if collision (case-insensitive) + -- Update task status updateTaskStatus :: Text -> Status -> IO () updateTaskStatus tid newStatus = |
