Phase 7 of docs/subgroups.md, and the first commit where a user can make a subgroup. Four pieces. **A subgroups section on the group detail screen**, above members, listing `SubgroupManager.subgroupsOf` -- so it shows a child this device holds no room for, which is the normal position of a member who is not in the subgroup and of everybody between the certificate being signed and the room being created. A row titles itself from the *room* where there is one and only otherwise from the certificate, because the certificate's name and `p` tags are the founding roster and a renamed or grown subgroup would otherwise be listed under a name nobody uses. The supporting line says which of the two absences it is: certified but not created, or created and you are not in it. Tapping opens the child where this device has it and the certificate where it does not, since that is the whole of what is known and it is checkable. **A parent row on the child's detail screen**, directly under the signing key, because between them they are what the room *is*: the identity it signs as and whose child it is. It comes off the verified `GroupKeyState.parentChatRoomId`, so a member welcomed in after the founding sees nothing there rather than an unverified guess. **`SelectSubgroupAdminsScreen`**, where the three things that cannot change later are settled. The pool is the parent's own members, admins and non-admins alike and marked rather than filtered -- the point of a subgroup is that it can be run by people the parent does not let run the parent. The coordinator is shown, ticked and locked, since they hold a share by construction and leaving them off the list would make "pick two more" read as a group of two. Key packages are resolved as the screen opens and a member without one is marked and unselectable. A key package is one-time-use, so every group a member joins burns one; `MarmotGroupCreation` would refuse to create the room for a missing one -- correctly, the address being permanent -- but only after a ChillDKG, a parent quorum and a child quorum had all completed, each needing every selected admin present. Finding out at the picker costs nothing and finding out at step 4 costs three ceremonies. The supporting line names the remedy rather than the diagnosis, because only its owner can publish another. The quorum stepper is here and nowhere else, and that is a protocol fact: ChillDKG hashes the threshold and the host keys into the session identity, so `t` is fixed the moment the proposal goes out. It is also the one value picked for other people, and consent survives it -- `t` rides on the proposal, `acceptProposal` re-checks it against `quorumRange`, and the host-key gate is where each invitee agrees to the `t`-of-`n` they can now see. **The ritual screen grows a rung rather than being cloned.** `DkgRitualRoute` takes an optional `parentChatRoomId`, and with one the ladder is four steps instead of three: key, certificate, key state, room. A parallel subgroup screen would have duplicated a progress ladder, a threshold picker, three approval gates and a key-state rung in order to insert one step, and the copies would drift within a release. The certificate rung is watched off the *parent's* signed events and sessions rather than this room's -- it is signed where the parent's key can sign it, which is never the ceremony's room. The key-state button stays shut until it is done, because a subgroup's state carries the certificate and `GroupKeyStateManager` refuses one without it; opening that session early would throw rather than fail. And `createAdminGroup` passes the verified parent through to the room, names a subgroup what the coordinator called it rather than "X (#admins)", and says so. No new approval UI. The certificate is a `FrostSigningEvents.PROPOSAL` in the parent's room and the key state one in the ceremony room; `ProposalListScreen` and `FrostSigningScreen` already show and approve both, on both transports. Six repository methods carry it: `subgroupsOf`, `parentOf`, `canSign` and `refuseSubgroup` on `ChatRepository`, and `proposeBirthCertificate`, `observeBirthCertificate` and `proposeSubgroupKeyState` on `DkgRepository`. The view models talk to repositories and the managers take the database, which is where the rest of the app has that line. `refuseSubgroup` returns a refusal when it cannot compute one, because a guard that fails open is not a guard. 25 new strings in the catalogue in sentence case; 397 common tests, 701 jvm tests, and `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…