Move SSD group edit mutation to typed Kotlin

ober

d0e6f6e529dff3bb031a3782ea827f2068c291c1

diff --git a/templates/ssd-review.ss b/templates/ssd-review.ss
index ebaeaf6..d40c247 100644
--- a/templates/ssd-review.ss
+++ b/templates/ssd-review.ss
@@ -4613,6 +4613,7 @@
                 openSsdPdfTitle noSsdPdfsDialogMessage
                 noSsdPdfsFoundStatus
                 dialogCountValue editedGroupCountText
+                ssdSessionApplyEditedGroup
                 selectionIgnoredLoadingStatus
                 shouldShowFiringArc shouldFocusFiringArc
                 canvasLabelPresent canvasLabelMissing groupIdSelected
@@ -5132,6 +5133,17 @@
           (parsedCountOrFallback text (int32 1)))
         (def (editedGroupCountText (text : String) (fallback : Int32)) : Int32
           (parsedCountOrFallback text fallback))
+        (def (ssdSessionApplyEditedGroup (session : SsdSession)
+                                         (group : SsdGroup)
+                                         (firingArcText : String)
+                                         (countText : String)) : Unit
+          (begin
+            (SsdGroup-firingArc-set! group (normalizeFiringArc firingArcText))
+            (SsdGroup-count-set!
+              group
+              (editedGroupCountText countText (SsdGroup-count group)))
+            (SsdGroup-status-set! group "reviewed")
+            (ssdSessionRecomputeGroup session group)))
         (def (selectionIgnoredLoadingStatus) : String
           "Selection ignored: truth/detection is still loading")
         (def (shouldShowFiringArc (firingArcText : String)
@@ -7528,10 +7540,7 @@
        "            val resolved = BoxTypes.resolve(this, boxTypeText)"
        "            group.label = manualGroupLabel(labelText, resolvedBoxTypeNameOr(resolved, group.label), group.label)"
        "            group.boxTypeId = resolvedBoxTypeIdOr(resolved, boxTypeText)"
-       "            group.firingArc = normalizeFiringArc(firingArcText)"
-       "            group.count = editedGroupCountText(count.text.toString(), group.count)"
-       "            group.status = \"reviewed\""
-       "            ssdSessionRecomputeGroup(session, group)"
+       "            ssdSessionApplyEditedGroup(session, group, firingArcText, count.text.toString())"
        "            truthStore.appendEvent(\"group_edited\", session)"
        "            reviewView.performHapticFeedback(HapticFeedbackConstants.CONFIRM)"
        "            reviewView.invalidate()"