Move SSD JSONL merge formatting to typed Kotlin

ober

558e5d3458fc70a588079d20ef25f39685e95344

diff --git a/templates/ssd-review.ss b/templates/ssd-review.ss
index 7cef390..1cee625 100644
--- a/templates/ssd-review.ss
+++ b/templates/ssd-review.ss
@@ -1268,7 +1268,8 @@
                 make-StorageStats StorageStats?
                 StorageStats-files StorageStats-files-set!
                 StorageStats-bytes StorageStats-bytes-set!
-                storageSummaryText compressionRatioBudget boundedTreeReadLimit)
+                storageSummaryText compressionRatioBudget boundedTreeReadLimit
+                mergedJsonLinesText)
         (type Int32)
         (record ImportBudget
           ((mut entries : Int32)
@@ -1276,6 +1277,13 @@
         (record StorageStats
           ((mut files : Int32)
            (mut bytes : Int)))
+        (extern (stringSetEmpty? (items : (Set String))) : Bool
+          (kotlin-member-call isEmpty))
+        (extern (joinStringSet (items : (Set String))
+                               (separator : String)
+                               (prefix : String)
+                               (postfix : String)) : String
+          (kotlin-member-call joinToString))
         (def (maxIntValue (a : Int) (b : Int)) : Int
           (if (> a b) a b))
         (def (minIntValue (a : Int) (b : Int)) : Int
@@ -1286,6 +1294,12 @@
           (maxIntValue floorBytes (* compressedBytes ratio)))
         (def (boundedTreeReadLimit (entryLimit : Int) (remaining : Int)) : Int
           (minIntValue entryLimit remaining))
+        (def (mergedJsonLinesText (lines : (Set String))) : String
+          (joinStringSet
+            lines
+            "\n"
+            ""
+            (if (stringSetEmpty? lines) "" "\n")))
         (def (storageSummaryText (truthCount : Int32)
                                  (eventCount : Int32)) : String
           (string-append
@@ -7831,7 +7845,7 @@
        "            if (lines.size >= MAX_JSONL_LINES) throw IllegalStateException(\"JSONL line-count limit exceeded\")"
        "            lines.add(it)"
        "        }"
-       "        val merged = lines.joinToString(separator = \"\\n\", postfix = if (lines.isEmpty()) \"\" else \"\\n\")"
+       "        val merged = mergedJsonLinesText(lines)"
        "        require(merged.toByteArray(Charsets.UTF_8).size.toLong() <= MAX_ZIP_ENTRY_BYTES)"
        "        atomicWriteText(file, merged)"
        "    }"