Move SSD group selection checks to typed Kotlin

ober

ca384851fa093fe739a6eaa490f3a78d6a3d80db

diff --git a/.build.yml b/.build.yml
index 68f4868..0cd7feb 100644
--- a/.build.yml
+++ b/.build.yml
@@ -5,7 +5,7 @@ packages:
   - make=4.4.1-r4
 sources:
   # Build dependency: full immutable commit, mirrored in dependencies.lock.json.
-  - "https://git.sr.ht/~lisp/jerboa#45689469df19c42f5f282439c3aa356ac96a5021"
+  - "https://git.sr.ht/~lisp/jerboa#69ae1ad7edfd9bdd01cae29eb01b79b37d784e50"
   # The second source is the build subject selected by the SourceHut submitter.
   - https://git.sr.ht/~lisp/jerboa-android
 tasks:
@@ -14,6 +14,6 @@ tasks:
       test "$(apk info -v chez-scheme)" = chez-scheme-10.3.0-r2
       test "$(apk info -v git)" = git-2.54.0-r0
       test "$(apk info -v make)" = make-4.4.1-r4
-      test "$(git -C ../jerboa rev-parse HEAD)" = 45689469df19c42f5f282439c3aa356ac96a5021
-      test "$(git -C ../jerboa rev-parse 'HEAD^{tree}')" = a35a87a4e3f83550eb1bcd5b5eef518c72321a6a
+      test "$(git -C ../jerboa rev-parse HEAD)" = 69ae1ad7edfd9bdd01cae29eb01b79b37d784e50
+      test "$(git -C ../jerboa rev-parse 'HEAD^{tree}')" = cbbe105f3c773e1fcb6bb5d8ba3eaab497e51b54
       JERBOA="chez --libdirs .:../jerboa/lib --script" make test
diff --git a/dependencies.lock.json b/dependencies.lock.json
index fac1dd6..d718a32 100644
--- a/dependencies.lock.json
+++ b/dependencies.lock.json
@@ -11,8 +11,8 @@
   "generator_runtime": {
     "name": "jerboa",
     "repository": "https://git.sr.ht/~lisp/jerboa",
-    "commit": "45689469df19c42f5f282439c3aa356ac96a5021",
-    "tree": "a35a87a4e3f83550eb1bcd5b5eef518c72321a6a"
+    "commit": "69ae1ad7edfd9bdd01cae29eb01b79b37d784e50",
+    "tree": "cbbe105f3c773e1fcb6bb5d8ba3eaab497e51b54"
   },
   "assurance_tools": {
     "osv_scanner": {
diff --git a/scripts/verify-supply-chain.sh b/scripts/verify-supply-chain.sh
index 2c8821f..6cf5190 100755
--- a/scripts/verify-supply-chain.sh
+++ b/scripts/verify-supply-chain.sh
@@ -3,8 +3,8 @@ set -eu
 
 repo=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd -P)
 lock="$repo/dependencies.lock.json"
-jerboa_commit=45689469df19c42f5f282439c3aa356ac96a5021 # gitsafe:ignore
-jerboa_tree=a35a87a4e3f83550eb1bcd5b5eef518c72321a6a # gitsafe:ignore
+jerboa_commit=69ae1ad7edfd9bdd01cae29eb01b79b37d784e50 # gitsafe:ignore
+jerboa_tree=cbbe105f3c773e1fcb6bb5d8ba3eaab497e51b54 # gitsafe:ignore
 gradle_sha=20f1b1176237254a6fc204d8434196fa11a4cfb387567519c61556e8710aed78
 jdk_macos_sha=8fa1eff40bb637a33613b2ccb8b12c70dc3661cc22cf8e784943715769a05336
 jdk_linux_sha=d8afc263758141a66e0e3aafc321e783f7016696f4eaea067d340a269037d331
diff --git a/templates/ssd-review.ss b/templates/ssd-review.ss
index 1c9bc69..722b8db 100644
--- a/templates/ssd-review.ss
+++ b/templates/ssd-review.ss
@@ -6511,7 +6511,8 @@
        "    }"
        ""
        "    private fun startAppendMode() {"
-       "        val session = currentSession ?: return"
+       "        if (sessionMissing(currentSession)) return"
+       "        val session = ssdSessionOrEmpty(currentSession)"
        "        val group = ssdSessionFindGroupById(session, selectedGroupId)"
        "        if (ssdGroupMissing(group)) {"
        "            appendMode = false"
@@ -6529,7 +6530,7 @@
 	       "    }"
 	       ""
 	       "    private fun startSsdAreaMode() {"
-	       "        currentSession ?: return"
+	       "        if (sessionMissing(currentSession)) return"
 	       "        appendMode = false"
 	       "        ssdAreaMode = true"
 	       "        reviewView.snapSelectionToCells = false"
@@ -6566,7 +6567,8 @@
 	       "    }"
 	       ""
 	       "    private fun applySsdArea(rect: FloatArray) {"
-	       "        val session = currentSession ?: return"
+	       "        if (sessionMissing(currentSession)) return"
+	       "        val session = ssdSessionOrEmpty(currentSession)"
        "        val area = ssdSessionBoundedSsdArea(session, rect)"
        "        if (ssdAreaSelectionTooSmall(area)) {"
        "            finishSelectInteraction()"
@@ -6593,48 +6595,60 @@
 	       "    }"
 	       ""
 	       "    private fun selectGroup(groupId: String?) {"
-       "        val session = currentSession ?: return"
-       "        val group = ssdSessionFindGroupById(session, groupId) ?: return"
-	       "        selectedGroupId = group.id"
-	       "        reviewView.selectedGroupId = group.id"
-	       "        reviewView.centerOnGroup(group)"
+       "        if (sessionMissing(currentSession)) return"
+       "        val session = ssdSessionOrEmpty(currentSession)"
+       "        val group = ssdSessionFindGroupById(session, groupId)"
+       "        if (ssdGroupMissing(group)) return"
+       "        val selectedGroup = ssdGroupOrEmpty(group)"
+	       "        selectedGroupId = selectedGroup.id"
+	       "        reviewView.selectedGroupId = selectedGroup.id"
+	       "        reviewView.centerOnGroup(selectedGroup)"
 	       "        reviewView.performHapticFeedback(HapticFeedbackConstants.CLOCK_TICK)"
-       "        setStatus(selectedGroupStatus(group))"
+       "        setStatus(selectedGroupStatus(selectedGroup))"
        "    }"
        ""
        "    private fun selectedGroup(): SsdGroup? {"
-       "        val session = currentSession ?: return null"
-       "        val group = ssdSessionSelectedGroup(session, selectedGroupId) ?: return null"
-       "        selectedGroupId = group.id"
-       "        reviewView.selectedGroupId = group.id"
-       "        return group"
+       "        if (sessionMissing(currentSession)) return null"
+       "        val session = ssdSessionOrEmpty(currentSession)"
+       "        val group = ssdSessionSelectedGroup(session, selectedGroupId)"
+       "        if (ssdGroupMissing(group)) return null"
+       "        val selectedGroup = ssdGroupOrEmpty(group)"
+       "        selectedGroupId = selectedGroup.id"
+       "        reviewView.selectedGroupId = selectedGroup.id"
+       "        return selectedGroup"
        "    }"
        ""
        "    private fun selectRelativeGroup(delta: Int) {"
-       "        val session = currentSession ?: return"
-       "        val next = ssdSessionRelativeGroupId(session, selectedGroupId, delta) ?: return"
-       "        selectGroup(next)"
+       "        if (sessionMissing(currentSession)) return"
+       "        val session = ssdSessionOrEmpty(currentSession)"
+       "        val next = ssdSessionRelativeGroupId(session, selectedGroupId, delta)"
+       "        if (!nullableTextPresent(next)) return"
+       "        selectGroup(nullableTextOrEmpty(next))"
        "    }"
        ""
        "    private fun editSelectedGroup() {"
-       "        val group = selectedGroup() ?: return"
-       "        showGroupEditDialog(group)"
+       "        val group = selectedGroup()"
+       "        if (ssdGroupMissing(group)) return"
+       "        showGroupEditDialog(ssdGroupOrEmpty(group))"
        "    }"
        ""
        "    private fun approveSelectedGroup() {"
-       "        val session = currentSession ?: return"
-       "        val group = selectedGroup() ?: return"
-       "        finalizeGroupLabel(group)"
-       "        group.status = \"reviewed\""
-       "        group.notes = \"Approved on Android\""
-       "        ssdSessionRecomputeGroup(session, group)"
+       "        if (sessionMissing(currentSession)) return"
+       "        val session = ssdSessionOrEmpty(currentSession)"
+       "        val group = selectedGroup()"
+       "        if (ssdGroupMissing(group)) return"
+       "        val approvedGroup = ssdGroupOrEmpty(group)"
+       "        finalizeGroupLabel(approvedGroup)"
+       "        approvedGroup.status = \"reviewed\""
+       "        approvedGroup.notes = \"Approved on Android\""
+       "        ssdSessionRecomputeGroup(session, approvedGroup)"
        "        truthStore.appendEvent(\"group_approved\", session)"
        "        reviewView.performHapticFeedback(HapticFeedbackConstants.CONFIRM)"
        "        reviewView.invalidate()"
        "        saveSessionAsync("
        "            session,"
-       "            approveSavingStatus(group),"
-       "            approveSavedStatus(group)"
+       "            approveSavingStatus(approvedGroup),"
+       "            approveSavedStatus(approvedGroup)"
        "        )"
        "    }"
        ""