Move SSD replacement time policy to typed Kotlin

ober

7e28d73335c5650fa5aa8417dd525d5001712e9d

diff --git a/templates/ssd-review.ss b/templates/ssd-review.ss
index 822c20a..9ae91eb 100644
--- a/templates/ssd-review.ss
+++ b/templates/ssd-review.ss
@@ -1586,7 +1586,7 @@
                 remoteBearerTokenPresent remoteBearerTokenValid
                 remotePinPresent remotePinOrNull remotePinSha256Length
                 remoteRelativePathSafe
-                shouldCompareMoreCompleteTruth
+                shouldCompareMoreCompleteTruth shouldReplaceLocalByTimes
                 zipEntryIsDirectory zipEntryDeclaresExcessiveSize)
         (type File)
         (type Path)
@@ -1726,6 +1726,15 @@
         (def (shouldCompareMoreCompleteTruth (replaceIfMoreComplete : Bool)
                                              (destination : File)) : Bool
           (and replaceIfMoreComplete (fileExists destination)))
+        (def (shouldReplaceLocalByTimes (localGenerated : (Nullable Int))
+                                        (remoteGenerated : (Nullable Int))
+                                        (remoteModified : Int)
+                                        (localModified : Int)) : Bool
+          (if (and (not (nullable-null? localGenerated))
+                   (not (nullable-null? remoteGenerated)))
+            (>= (nullable-get remoteGenerated) (nullable-get localGenerated))
+            (and (> remoteModified (int 0))
+                 (>= remoteModified localModified))))
         (def (zipEntryIsDirectory (entry : ZipEntry)) : Bool
           (zipEntryIsDirectoryRaw entry))
         (def (zipEntryDeclaresExcessiveSize (entry : ZipEntry)
@@ -8709,10 +8718,7 @@
        "        if (localText == remoteText) return false"
        "        val localGenerated = jsonTime(localText)"
        "        val remoteGenerated = jsonTime(remoteText)"
-       "        if (localGenerated != null && remoteGenerated != null) {"
-       "            return remoteGenerated >= localGenerated"
-       "        }"
-       "        return remoteModified > 0L && remoteModified >= local.lastModified()"
+       "        return shouldReplaceLocalByTimes(localGenerated, remoteGenerated, remoteModified, local.lastModified())"
        "    }"
        ""
        "    private fun truthTime(truth: JSONObject): Long? ="