Move SSD source key fallback to typed Kotlin

ober

272550c4ee631d58e4256dfd1e3b69b10f872d2b

diff --git a/templates/ssd-review.ss b/templates/ssd-review.ss
index c6141f1..9318ba2 100644
--- a/templates/ssd-review.ss
+++ b/templates/ssd-review.ss
@@ -1725,7 +1725,7 @@
         (export emptyJsonObject jsonObjectFromText
                 truthJsonArrayOrEmpty truthJsonObjectOrEmpty
                 truthGroupCount nullableTruthGroupCount truthSsdAreaRect
-                truthSourceKey truthSessionIdOr
+                truthSourceKey truthSourceKeyOrUnknown truthSessionIdOr
                 imageWidth imageHeight)
         (type JSONArray)
         (type JSONObject)
@@ -1759,6 +1759,10 @@
           (jsonRect (json-object-opt-json-array truth "ssd_area")))
         (def (truthSourceKey (truth : JSONObject)) : String
           (json-object-opt-string truth "source_key"))
+        (def (truthSourceKeyOrUnknown (sourceKey : String)) : String
+          (if (string-blank? sourceKey)
+            "unknown"
+            sourceKey))
         (def (truthSessionIdOr (truth : JSONObject) (fallback : String)) : String
           (json-object-opt-string-default truth "session_id" fallback))
         (def (imageWidth (truth : JSONObject)) : Int32
@@ -7138,7 +7142,7 @@
        "        val truthFile = truthPath(sourceKey)"
        "        atomicWriteText(truthFile, truth.toString(2))"
        "        invalidateTruthIndex()"
-       "        val sessionId = truthSessionIdOr(truth, sourceKey.ifBlank { \"unknown\" })"
+       "        val sessionId = truthSessionIdOr(truth, truthSourceKeyOrUnknown(sourceKey))"
        "        val reviewFile = containedLeaf(reviewsDir, \"review-${sha256Hex(sessionId.toByteArray())}.review.json\")"
        "        atomicWriteText(reviewFile, truth.toString(2))"
        "        val event = snapshotEventJson(sessionId, sourceKey, truthFile.name)"