Six cases, and the ones worth writing are the ones where reading the room instead of the ceremony produces a plausible wrong answer rather than a crash. **The transport, and the p-tags on it.** The proposal is a `MarmotInnerEvent` and there are no gift wraps at all, and it p-tags the picked admins and nobody else. Both halves: the transport is the change, and the p-tags are what keeps the change from being a disaster, because dropping them the way `FrostSigningManager` correctly does in a Marmot room would enrol the whole parent in the child's permanent signing quorum. **The name.** On the proposal, on the session, once -- and the room keeps its own. There is nowhere else for a child's name to live now, and the parent's is the one name it must not take. **A parent member who was not picked drops the proposal**, opens no session, and publishes nothing. This is the property that makes the parent's room a safe place to hold a ceremony a subset of it is in: everyone can read the message, only the p-tagged are in the ceremony, and "can read" and "is in" have to stay different questions when the second one is permanent. **A picked admin joins, and not before approving.** The approval gate is unchanged by the move and has to stay that way -- a relay delivering a group event to a phone in a pocket must not enrol its owner in anything -- so this asserts nothing goes out until `approve`, then that what goes out is an inner event carrying the roster minus its own sender. **A host key that beat the proposal is replayed**, out of the inner-event store. The resume machinery is what makes the ritual safe to run anywhere, and it reads the backlog out of whichever store the room's transport writes to; looking in the wrong one is a stall with nothing to blame it on. **`completedKey` refuses to hand the parent its child's key.** The regression test for `ba8aa1e2`: a parent member with no key state and no share -- the position every member welcomed after the group's own ceremony is in, and the one that reaches the last fallback -- with a completed subgroup ceremony sitting in the room. Before the fix that fallback returned the child's key and the parent would have authored events as its own subgroup. It also asserts the child's *own* room still resolves it, by rederiving the id from the key, which is the check that actually binds a room to a key. Two databases, and the MLS group in each is real but not joint: messages are handed to the manager rather than encrypted between two trees. That is the right seam here -- what is under test is which store a message is queued in and which set it names, not whether quartz can encrypt it -- and `SignedGroupKeyStateTest` is where a session runs end to end. 1136 tests pass, `m3Audit` meets every budget. 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…