A room with two proposals open showed "Review" on both, and then dropped it from both the moment either one was decided. The second proposal was still waiting on the reader, still had a decision in it, and had nowhere left to be reached from. Two proposals at once is not a corner case any more: a chapter and the translation scaffolding beside it are proposed as separate sessions, on purpose, and they run at the same time. Both write the same line types into the same stretch of transcript. `answeredRequests` matched a request against any later line of the fulfilling type, and `settledRequests` against any later ending. That reads a room signing one thing at a time exactly right -- the nonce after the request is the answer to it, because there is nothing else it could be an answer to -- and a room signing two things at once exactly wrong. Nothing else on the row could separate them: same type, same room, same minute, and `ChatMessage` carried no session. So the session goes on the row. `ChatMessage.frostSigningSessionId` is nullable, added as schema v11 through `AutoMigration(10, 11)`, and stamped by `FrostSigningManager.announce` -- the one place every FROST line is written, so there is no line that can be forgotten. Both rules read it when both rows have one and fall back to the clock when either does not. The fallback is not a compromise, it is the right reading of the rows it applies to. A line written before this column has no session and never will, and the rooms that wrote those lines could not run two sessions at once, so the clock is the whole truth there. A ceremony line falls back too and always will: a room runs one ritual at a time, and a DKG step is either taken or still waited on. **This reverses a call `FrostSigningRoute` argued for.** Its note said a chat row carrying a session id was "a poor trade for a lookup the screen can do". That was right when the lookup could only be wrong about which of one session it meant. The batch work made two sessions ordinary, and the lookup and the rules both became guesses at the same moment. A column on the table every message uses is the cost; two proposals, one of them unreachable, was the alternative. **Tests.** Three in TranscriptRequestStateTest for what the column buys: a nonce answers its own session's request and not the other's, one session completing settles nothing in the other, and a line naming no session is still read by the clock. TranslationBatchProposalJvmTest proves the other half against a real two-session proposal -- every FROST line the manager writes names its own session, and neither session's lines are attributed to the other. The rule is tested on rows and the stamping is tested on a database, because a rule that is right about rows nothing writes correctly is worth nothing. 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…