Two properties here decide whether a ceremony can finish, and the compiler sees neither. A participant gets one message per round, and that is enforced by the composite key (sessionId, participantPublicKey, kind) rather than by any code that writes to the table. Rounds advance on countMessagesByKind reaching the participant count, so a redelivered message that added a row instead of replacing one would let the count reach the threshold while a member had still never been heard from -- and the ritual would proceed on a participant set it never assembled. Covered by resending a participant's message with a different payload and asserting the count stays at one and the payload is the newer of the two, and separately by writing the same participant into two different rounds and asserting neither overwrites the other. A key-holding session needs both halves. thresholdPublicKey without secretShare is a ceremony that produced a group key this device cannot sign against; secretShare without thresholdPublicKey is a share with no key to sign for. Either alone is a failed ceremony, and offering it up as a signing key means attempting to sign with half a result. Covered with all four combinations present in the table at once, asserting only the complete one comes back. Also covered: the live ritual for a room is the newest, because a group may have abandoned earlier attempts and a resume that picked up an abandoned one would wait forever on participants who have moved to the newer; rituals belonging to another room are not offered as this room's; key-holding sessions come back newest first; and messages are counted per session and per round rather than across either. And the ordering, which is the one with a reason beyond tidiness: a round's messages come back ordered by participant public key, not by arrival. Every device has to assemble a round in the same order to compute the same thing, and arrival order is per-device. The test writes three participants in an order deliberately unlike the sorted one. Real secp256k1 keys throughout rather than hex filler, since these are the values a canonical ordering is defined over. Verified by mutation: relaxing the key-holding predicate to `OR` returns all three of the incomplete sessions and fails that test; reordering the round query by createdAt fails the canonical-order test. Both mutations were reverted; no production source is touched by this commit. 8 tests. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This is a Kotlin Multiplatform project targeting Android, iOS, Desktop (JVM).
-
/composeApp is for code that will be shared across your Compose Multiplatform applications. It contains several subfolders:
- commonMain is for code that’s common for all targets.
- Other folders are for Kotlin code that will be compiled for only the platform indicated in the folder name. For example, if you want to use Apple’s CoreCrypto for the iOS part of your Kotlin app, the iosMain folder would be the right place for such calls. Similarly, if you want to edit the Desktop (JVM) specific part, the jvmMain folder is the appropriate location.
-
/iosApp contains iOS applications. Even if you’re sharing your UI with Compose Multiplatform, you need this entry point for your iOS app. This is also where you should add SwiftUI code for your project.
Build and Run Android Application
To build and run the development version of the Android app, use the run configuration from the run widget in your IDE’s toolbar or build it directly from the terminal:
- on macOS/Linux
./gradlew :composeApp:assembleDebug - on Windows
.\gradlew.bat :composeApp:assembleDebug
Build and Run Desktop (JVM) Application
To build and run the development version of the desktop app, use the run configuration from the run widget in your IDE’s toolbar or run it directly from the terminal:
- on macOS/Linux
./gradlew :composeApp:run - on Windows
.\gradlew.bat :composeApp:run
Build and Run iOS Application
To build and run the development version of the iOS app, use the run configuration from the run widget in your IDE’s toolbar or open the /iosApp directory in Xcode and run it from there.
Learn more about Kotlin Multiplatform…