A subgroup's ceremony started and nobody else could see it. The session row was written, the transcript said so, and the shared-key screen showed "start a ceremony" as though nothing had happened. **Only the coordinator arrives knowing it is a subgroup.** They come from the picker, which puts `parentChatRoomId` on the route. Everybody else reaches that screen from the room -- the transcript's ritual notice, or the group's details -- and neither has a purpose to hand it, so both construct `DkgRitualRoute(chatRoomId)` with nothing. When the previous commit scoped the room's session lookup by purpose, `observeLatestSessionForChatRoom(room, null)` started meaning "the ceremony that is *not* for a subgroup", and a subgroup's session stopped being visible to every member but the one who opened it. **Scoping belongs where scoping is the question being asked**, which is "may I open another" -- `proposeRitual`'s guard and `refuseCeremonyRoom`. Those keep `getLatestSessionFor(room, parent)`. The screen asks a different question, "what is happening in this room", and there is only one honest answer to it: whatever ceremony is there. So the room's lookup goes back to being room-scoped and the purpose is read *off the session that turns up* rather than required in order to find it. That is also the better shape. A member who did not open the subgroup has no idea it is one until the proposal they were sent arrives, so the purpose could never have been an input on their side. **The certificate watchers move to follow the session.** They cannot start at init any more -- there is no parent to watch until one is known -- so they are cancelled and restarted from the session collector when the purpose changes, the way the message watcher already is. An ordinary ceremony passes null and gets nothing watched. **Three actions were reading the route as well**, which is the same bug one step on. `docs/subgroups.md` says only step 1 belongs to the coordinator: the key state and the room are open to any of the subgroup's admins, and they arrive by room. So `proposeBirthCertificate`, `proposeKeyState` and `createAdminGroup` now take the parent from the resolved state rather than the constructor -- otherwise a second admin finishing the flow would have created an ordinary `#admins` room with no parentage on it. Two tests in `RobustRoomKeyCeremonyTest`, both of which fail against the previous commit: a subgroup's ceremony is found by the room alone and carries the purpose with it, and a room holds its own ceremony and a subgroup's at once without either being mistaken for the other or a third being opened. 397 common tests, 715 jvm tests, `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…