A translation is scaffolded from both ends now -- the chapters that existed when it was proposed, and each chapter signed afterwards putting itself in. Neither end closes the gap on its own, and no snapshot taken at proposal time can. Two ways they miss each other. A chapter and a translation proposed at the same moment each read what exists when they are proposed, so neither sees the other and nothing retries. And a scaffolding session that fails to reach a quorum leaves nothing behind to try again with -- the chapter's id is spent, since a re-proposed chapter is a different one. So the translation's own screen says what it is missing and offers to ask for it. Above the chapter list rather than below, because a chapter that is not in a translation is invisible from a list of the ones that are: the whole failure is that nothing looks wrong. **Matched on the chapter named, not counted.** `chaptersMissingFrom` compares which source chapter each translation chapter stands for. A count would read a translation that is missing its second chapter but picked up its third as one missing its last, and would then scaffold the wrong chapter -- leaving the real gap open and a duplicate beside it. It also means running a catch-up on a translation that is already complete proposes nothing at all, rather than a second copy of every chapter under fresh ids. **More sessions rather than a cap.** The missing chapters are chunked at MAX_BATCH_SIZE, one session each. A translation far enough behind to need more than a batch holds is not one to refuse; it is one the group answers for more than once. They share a timestamp, so a catch-up reads as the one act it is. The screen lands on the first session -- the rest are beside it in the room's list -- and the card stays until a quorum arrives, which is honest: the chapters are still missing until then. **Tests.** Two more in TranslationScaffoldTest, both about the matching rather than the counting: a gap in the middle, and a complete translation being missing nothing. Checked against a broken implementation -- taking the missing chapters as the tail after a count passes on a translation that fell behind at the end, which is the easy case, and is caught by the gap. Not covered: `catchUpMissingChapters` itself, which is plumbing over the templates those tests pin and the batch API the jvm tests pin. 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…