Move SSD guess replacement guard to typed Kotlin

ober

46cc61f30ef8243aa4163a5b3b6f5f129e08bfbb

diff --git a/templates/ssd-review.ss b/templates/ssd-review.ss
index 55a4954..50aebe4 100644
--- a/templates/ssd-review.ss
+++ b/templates/ssd-review.ss
@@ -1202,7 +1202,8 @@
 
     (typed-kotlin-file "com/sfb/ssdreview/UiTextHelpers.kt"
       (typed-library (com sfb ssdreview)
-        (export normalizeFiringArc normalizeLabel isGenericGuessLabel isSsdPdfName
+        (export normalizeFiringArc normalizeLabel shouldReplaceGuessLabel
+                isGenericGuessLabel isSsdPdfName
                 sourceBaseName canonicalSourceName isUsefulGuessLabel isErrorStatus
                 formatActivityDuration)
         (def (normalizeFiringArc (value : String)) : String
@@ -1220,6 +1221,16 @@
                 (or (or (equal? normalized "boxes")
                         (equal? normalized "unknown"))
                     (equal? normalized "unclassified")))))
+        (def (shouldReplaceGuessLabel (status : String) (label : String)) : Bool
+          (let ((text (string-trim label)))
+            (if (or (equal? status "reviewed")
+                    (equal? status "approved"))
+              #f
+              (if (string-blank? text)
+                #t
+                (if (string-ends-with? text "?")
+                  #t
+                  (string-matches-regex? text "^\\d+\\s+boxes$"))))))
         (def (isSsdPdfName (name : String)) : Bool
           (let ((lower (string-lowercase name)))
             (and (string-ends-with? lower ".pdf")
@@ -1475,6 +1486,7 @@
        "    fun applyInitialGuesses(context: Context, session: SsdSession) {"
        "        val race = raceKey(session.sourceName + \" \" + session.sourceUri)"
        "        session.groups.forEach { group ->"
+       "            if (!shouldReplaceGuessLabel(group.status, group.label)) return@forEach"
        "            val guess = guessForGroup(group, session.imageWidth, session.imageHeight, race)"
        "            val boxType = BoxTypes.resolve(context, guess.boxType)"
        "            group.label = \"${guess.label}?\""