Move SSD truth cell remap to typed Kotlin

ober

bc8ac4d554d140b6610ef0b950609e3952dc83aa

diff --git a/.build.yml b/.build.yml
index 429b202..30a6b9f 100644
--- a/.build.yml
+++ b/.build.yml
@@ -5,7 +5,7 @@ packages:
   - make=4.4.1-r4
 sources:
   # Build dependency: full immutable commit, mirrored in dependencies.lock.json.
-  - "https://git.sr.ht/~lisp/jerboa#6fd51087d62a40f8d59ee556da023971a99792b0"
+  - "https://git.sr.ht/~lisp/jerboa#9638f2f8238c8ee30d2be26002eb2fb91898a84b"
   # The second source is the build subject selected by the SourceHut submitter.
   - https://git.sr.ht/~lisp/jerboa-android
 tasks:
@@ -14,6 +14,6 @@ tasks:
       test "$(apk info -v chez-scheme)" = chez-scheme-10.3.0-r2
       test "$(apk info -v git)" = git-2.54.0-r0
       test "$(apk info -v make)" = make-4.4.1-r4
-      test "$(git -C ../jerboa rev-parse HEAD)" = 6fd51087d62a40f8d59ee556da023971a99792b0
-      test "$(git -C ../jerboa rev-parse 'HEAD^{tree}')" = 832ff5f75d3f1ae470789de02f5fd7fbd357e90c
+      test "$(git -C ../jerboa rev-parse HEAD)" = 9638f2f8238c8ee30d2be26002eb2fb91898a84b
+      test "$(git -C ../jerboa rev-parse 'HEAD^{tree}')" = 532ac5bf0da6a25b66733936493e312f10524c98
       JERBOA="chez --libdirs .:../jerboa/lib --script" make test
diff --git a/dependencies.lock.json b/dependencies.lock.json
index 8674b8e..f6859e3 100644
--- a/dependencies.lock.json
+++ b/dependencies.lock.json
@@ -11,8 +11,8 @@
   "generator_runtime": {
     "name": "jerboa",
     "repository": "https://git.sr.ht/~lisp/jerboa",
-    "commit": "6fd51087d62a40f8d59ee556da023971a99792b0",
-    "tree": "832ff5f75d3f1ae470789de02f5fd7fbd357e90c"
+    "commit": "9638f2f8238c8ee30d2be26002eb2fb91898a84b",
+    "tree": "532ac5bf0da6a25b66733936493e312f10524c98"
   },
   "assurance_tools": {
     "osv_scanner": {
diff --git a/scripts/verify-supply-chain.sh b/scripts/verify-supply-chain.sh
index 1dc7d6e..057ac2f 100755
--- a/scripts/verify-supply-chain.sh
+++ b/scripts/verify-supply-chain.sh
@@ -3,8 +3,8 @@ set -eu
 
 repo=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd -P)
 lock="$repo/dependencies.lock.json"
-jerboa_commit=6fd51087d62a40f8d59ee556da023971a99792b0 # gitsafe:ignore
-jerboa_tree=832ff5f75d3f1ae470789de02f5fd7fbd357e90c # gitsafe:ignore
+jerboa_commit=9638f2f8238c8ee30d2be26002eb2fb91898a84b # gitsafe:ignore
+jerboa_tree=532ac5bf0da6a25b66733936493e312f10524c98 # gitsafe:ignore
 gradle_sha=20f1b1176237254a6fc204d8434196fa11a4cfb387567519c61556e8710aed78
 jdk_macos_sha=8fa1eff40bb637a33613b2ccb8b12c70dc3661cc22cf8e784943715769a05336
 jdk_linux_sha=d8afc263758141a66e0e3aafc321e783f7016696f4eaea067d340a269037d331
diff --git a/templates/ssd-review.ss b/templates/ssd-review.ss
index 135cf40..fa067a9 100644
--- a/templates/ssd-review.ss
+++ b/templates/ssd-review.ss
@@ -2560,6 +2560,45 @@
             usedIds
             (int32 0)))))
 
+    (typed-kotlin-file "com/sfb/ssdreview/TruthCellRemap.kt"
+      (typed-library (com sfb ssdreview)
+        (export remapAndAddTruthCells)
+        (type Int32)
+        (def (remappedTruthCell (group : SsdGroup)
+                                (cell : SsdCell)
+                                (usedIds : (MutableSet String))) : SsdCell
+          (if (set-contains? usedIds (SsdCell-id cell))
+              (make-SsdCell
+                (uniqueTruthCellId (SsdGroup-id group) (SsdCell-id cell) usedIds)
+                (SsdCell-x cell)
+                (SsdCell-y cell)
+                (SsdCell-w cell)
+                (SsdCell-h cell)
+                (SsdCell-detector cell))
+              cell))
+        (def (remapAndAddTruthCells (session : SsdSession)
+                                    (group : SsdGroup)
+                                    (truthCells : (List SsdCell))
+                                    (usedIds : (MutableSet String))) : Unit
+          (if (= (list-size truthCells) (int32 0))
+              (begin)
+              (let ((remappedIds (mutable-list-empty String)))
+                (begin
+                  (for/fold ((ignored (int32 0)))
+                            ((i (in-range (int32 0) (list-size truthCells))))
+                    (let ((resolved
+                            (remappedTruthCell
+                              group
+                              (list-ref truthCells i)
+                              usedIds)))
+                      (begin
+                        (mutable-list-add! (SsdSession-cells session) resolved)
+                        (mutable-set-add! usedIds (SsdCell-id resolved))
+                        (mutable-list-add! remappedIds (SsdCell-id resolved))
+                        ignored)))
+                  (mutable-list-clear! (SsdGroup-cellIds group))
+                  (mutable-list-add-all! (SsdGroup-cellIds group) remappedIds)))))))
+
     (typed-kotlin-file "com/sfb/ssdreview/PathSafety.kt"
       (typed-library (com sfb ssdreview)
         (export safePathComponent safeLeaf safeZipEntryName validSourceKey validatedSourceKey)
@@ -5720,28 +5759,6 @@
        "        return input.use { readBoundedBytes(it, MAX_HTTP_TEXT_BYTES).decodeToString() }"
        "    }"
        ""
-       "    private fun remapAndAddTruthCells("
-       "        session: SsdSession,"
-       "        group: SsdGroup,"
-       "        truthCells: List<SsdCell>,"
-       "        usedIds: MutableSet<String>"
-       "    ) {"
-       "        if (truthCells.isEmpty()) return"
-       "        val remappedIds = mutableListOf<String>()"
-       "        truthCells.forEach { cell ->"
-       "            val resolved = if (usedIds.contains(cell.id)) {"
-       "                cell.copy(id = uniqueTruthCellId(group.id, cell.id, usedIds))"
-       "            } else {"
-       "                cell"
-       "            }"
-       "            session.cells.add(resolved)"
-       "            usedIds.add(resolved.id)"
-       "            remappedIds.add(resolved.id)"
-       "        }"
-       "        group.cellIds.clear()"
-       "        group.cellIds.addAll(remappedIds)"
-       "    }"
-       ""
        "    private fun writeRemoteTruth("
        "        truth: JSONObject,"
        "        modified: Long,"