Files
mantra-kmp/gradle/libs.versions.toml

96 lines
6.6 KiB
TOML
Raw Normal View History

2026-03-23 01:41:39 +02:00
[versions]
build: unify the composite on AGP 9.4.0 so Android Studio can sync Android Studio refused to sync the project after the lightning-kmp-app bump in 4e939c4: Using multiple versions of the Android Gradle Plugin [9.1.1, 9.4.0] across Gradle builds is not allowed. Affected builds: [:, :lightning-kmp-app:lightning-kmp:bitcoin-kmp:secp256k1-kmp] A composite build has to settle on a single AGP version across every build that applies the plugin. Four builds in this tree do: : 9.1.1 version catalog :lightning-kmp-app 9.1.1 version catalog :secp256k1-frost-kmp 9.1.1 version catalog :lightning-kmp-app:lightning-kmp:bitcoin-kmp:secp256k1-kmp 9.4.0 hardcoded lightning-kmp and bitcoin-kmp apply no android plugin at all, which is why the message names only the two ends of the chain rather than everything between them. secp256k1-kmp pins 9.4.0 twice -- a buildscript classpath entry, and a pluginManagement resolutionStrategy that rewrites every requested com.android plugin to that version -- so it cannot be talked out of it from here. The three catalogs move up to meet it. Pinning it down to 9.1.1 instead would mean editing a build four submodules deep and then committing in bitcoin-kmp and lightning-kmp purely to carry the pointer, for no gain. Moving up is also the direction the chain already set: bbba08b's f22c30a moved every build in it onto Gradle 9.7.1 for the same reason, and 4e939c4 followed. Submodule commits carried here: lightning-kmp-app bbba08b -> ce1ed01 build: move to AGP 9.4.0, matching the version the submodule chain pins secp256k1-frost-kmp d3b294d -> 80a55b7 build: move to AGP 9.4.0 to match the rest of mantra-kmp's composite Both sit on branches (build/agp-9.4.0 and build/gate-ios-targets-on-macos) rather than their masters, and neither is pushed yet, so a fresh clone cannot resolve these two pointers until they are. 9.4.0 is a minor bump inside the AGP 9.x line, so the DSL is unchanged and every android.* flag in gradle.properties -- newDsl, builtInKotlin, the r8 settings -- keeps its meaning. `:composeApp:compileDebugKotlinAndroid` passes clean. Note that this is only what the IDE needs to sync, not everything worth knowing about the chain. bitcoin-kmp's settings.gradle.kts substitutes secp256k1-kmp, -jni-jvm and the three -jni-jvm-{linux,darwin,mingw} coordinates, but not -jni-android. So the android app still resolves secp256k1-kmp-jni-android from maven central at stock 0.24.0, without the FROST/prefractal native modules the threshold branch exists to provide, and a call into those from android would fail with UnsatisfiedLinkError. Fixing that needs a substitution rule in bitcoin-kmp. Untouched here because it is a runtime concern, not a sync one. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 22:58:32 +02:00
agp = "9.4.0"
2026-05-17 23:06:12 +02:00
android-compileSdk = "37"
2026-03-23 02:21:18 +02:00
android-minSdk = "26"
2026-05-17 23:06:12 +02:00
android-targetSdk = "37"
2026-03-30 13:21:14 +02:00
androidx-activity = "1.13.0"
2026-08-15 15:42:34 +02:00
androidx-appcompat = "1.8.0"
2026-06-05 23:55:44 +02:00
androidx-core = "1.19.0"
2026-03-23 01:41:39 +02:00
androidx-espresso = "3.7.0"
2026-08-15 15:42:34 +02:00
androidx-lifecycle = "2.11.0"
2026-03-23 01:41:39 +02:00
androidx-testExt = "1.3.0"
2026-08-15 15:42:34 +02:00
coilCompose = "3.5.0"
composeHotReload = "1.2.0"
composeMultiplatform = "1.11.1"
2026-04-18 21:58:32 +02:00
datastorePreferences = "1.2.1"
2026-03-23 01:41:39 +02:00
junit = "4.13.2"
2026-03-30 13:21:14 +02:00
kermit = "2.1.0"
2026-08-15 15:42:34 +02:00
kotlin = "2.4.10"
2026-05-17 23:12:49 +02:00
kotlinx-coroutines = "1.11.0"
kotlinx-datetime = "0.8.0"
2026-06-15 14:39:46 +02:00
kotlinxSerialization = "1.11.0"
2026-03-24 04:47:19 +02:00
ksp = "2.3.6"
2026-08-15 15:42:34 +02:00
ktor = "3.5.2"
lightningKmpCore = "1.11.5"
2026-03-23 01:41:39 +02:00
material3 = "1.10.0-alpha05"
materialIconsCore = "1.7.3"
materialIconsExtended = "1.7.3"
navigationCompose = "2.9.2"
2026-08-15 15:42:34 +02:00
okio = "3.18.1"
2026-07-01 15:11:31 +02:00
qrose = "1.1.2"
2026-08-29 16:22:56 +02:00
quartz = "1.14.0"
2026-08-15 15:42:34 +02:00
room3 = "3.0.1"
2026-06-15 14:39:46 +02:00
sqldelight = "2.3.2"
2026-08-15 15:42:34 +02:00
sqlite = "2.7.0"
2026-03-23 01:41:39 +02:00
[libraries]
androidx-core-ktx = { module = "androidx.core:core-ktx", version.ref = "androidx-core" }
androidx-testExt-junit = { module = "androidx.test.ext:junit", version.ref = "androidx-testExt" }
androidx-espresso-core = { module = "androidx.test.espresso:espresso-core", version.ref = "androidx-espresso" }
androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "androidx-appcompat" }
androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "androidx-activity" }
2026-04-19 04:30:29 +02:00
androidx-datastore = { module = "androidx.datastore:datastore", version.ref = "datastorePreferences" }
2026-04-18 21:58:32 +02:00
androidx-datastore-preferences = { module = "androidx.datastore:datastore-preferences", version.ref = "datastorePreferences" }
2026-03-24 04:47:19 +02:00
androidx-sqlite-bundled = { module = "androidx.sqlite:sqlite-bundled", version.ref = "sqlite" }
build: phase 0 of the jvm target -- clear the ground, correct the plan First phase of docs/jvm-target.md. Nothing here turns the target on; it removes what would break the moment it is turned on, and stages the two catalog entries that cannot be derived automatically. Two of the four steps as written in the doc turned out to be wrong, and implementing them is how that surfaced -- both are corrected in the doc in this commit. **Deleted the stale jvmMain tree.** Six files under composeApp/src/jvmMain/kotlin/ac/cord/auxiliary/ survived from the Aux project this codebase grew out of. They have gone unnoticed because `jvmMain` is currently an orphan source set -- the accessor creates it, no target compiles it -- so the wrong package, the Room 2 imports (androidx.room, not androidx.room3), and the references to a long-gone AuxDatabase and AuxGlobal have never had to resolve. They would all become compile errors in phase 4. They are not lost: they are the closest thing to a skeleton for five of the six platform actuals phase 4 needs, and main.kt is a reasonable starting shape for the phase 5 desktop entry point. `git show HEAD~1` has them. **Added two catalog entries, not four.** sqlite-bundled-jvm and sqldelight-sqlite-driver. Both earn their place by being unreachable otherwise: sqlite-bundled-jvm has to be named explicitly because variant-aware resolution hands the *android* artifact to anything running on the host, and sqldelight-sqlite-driver is the jvm counterpart to the android-driver and native-driver entries already there. The doc also listed room3-runtime-jvm and sqldelight-jdbc-driver. Neither is right. Once jvm() exists, commonMain's existing androidx-room3-runtime resolves to the -jvm variant on its own, so an explicit entry is redundant and would drift. And the SQLDelight drivers phase 2 needs are for DbFactory, which lives in lightning-kmp-app -- a separate gradle build with its own version catalog, where an entry here is simply not visible. **kspJvm cannot be wired yet, and the build file already said so.** The doc's phase 0 told you to uncomment composeApp/build.gradle.kts:194. It contradicted its own phase 4, which is where jvm() gets turned on. The comment three lines above it states the rule: These configurations only exist when the ios targets are declared, which the kotlin block above does only on a mac. The same holds for kspJvm -- `dependencies { add("kspJvm", ...) }` throws UnknownConfigurationException until a jvm() target creates the configuration. So it moves into phase 4, into the same edit that declares the target. composeApp/build.gradle.kts is deliberately untouched by this commit. **Also documented: gradle does not run in a worktree here at all** until the submodule is checked out, which worktrees do not do automatically. `lightning-kmp-app/` is empty and configuration fails with "Project with path ':library' not found in build ':lightning-kmp-app'". Recorded in the phase 0 verification section along with the caveat that a linked worktree shares .git/modules/ with the main checkout, so both trees end up on one submodule git dir. **Not verified by a build.** For that reason. The deletion is an orphan source set and the additions are unreferenced catalog lines, so neither can change a build's outcome -- but that is an argument, not a green check, and it is the second commit in a row on this branch that has not compiled anything. Phase 4 is the first phase that genuinely cannot be done without a working gradle invocation. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-06 00:44:10 +02:00
androidx-sqlite-bundled-jvm = { module = "androidx.sqlite:sqlite-bundled-jvm", version.ref = "sqlite" }
2026-05-02 00:05:38 +02:00
androidx-room3-runtime = { module = "androidx.room3:room3-runtime", version.ref = "room3" }
androidx-room3-compiler = { module = "androidx.room3:room3-compiler", version.ref = "room3" }
2026-05-20 21:17:47 +02:00
androidx-room3-sqlite-wrapper = { module = "androidx.room3:room3-sqlite-wrapper", version.ref = "room3" }
2026-03-23 01:41:39 +02:00
androidx-lifecycle-viewmodelCompose = { module = "org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "androidx-lifecycle" }
androidx-lifecycle-runtimeCompose = { module = "org.jetbrains.androidx.lifecycle:lifecycle-runtime-compose", version.ref = "androidx-lifecycle" }
2026-04-21 23:49:54 +02:00
coil-compose = { module = "io.coil-kt.coil3:coil-compose", version.ref = "coilCompose" }
coil-network-ktor3 = { module = "io.coil-kt.coil3:coil-network-ktor3", version.ref = "coilCompose" }
2026-03-30 13:14:37 +02:00
compose-components-resources = { module = "org.jetbrains.compose.components:components-resources", version.ref = "composeMultiplatform" }
2026-03-23 01:41:39 +02:00
compose-runtime = { module = "org.jetbrains.compose.runtime:runtime", version.ref = "composeMultiplatform" }
compose-foundation = { module = "org.jetbrains.compose.foundation:foundation", version.ref = "composeMultiplatform" }
compose-material3 = { module = "org.jetbrains.compose.material3:material3", version.ref = "material3" }
2026-03-23 02:21:18 +02:00
compose-material-icons-core = { module = "org.jetbrains.compose.material:material-icons-core", version.ref = "materialIconsCore" }
compose-material-icons-extended = { module = "org.jetbrains.compose.material:material-icons-extended", version.ref = "materialIconsExtended" }
2026-03-23 01:41:39 +02:00
compose-ui = { module = "org.jetbrains.compose.ui:ui", version.ref = "composeMultiplatform" }
2026-03-30 13:14:37 +02:00
compose-uiTooling = { module = "org.jetbrains.compose.ui:ui-tooling", version.ref = "composeMultiplatform" }
2026-03-23 01:41:39 +02:00
compose-uiToolingPreview = { module = "org.jetbrains.compose.ui:ui-tooling-preview", version.ref = "composeMultiplatform" }
2026-04-21 23:49:54 +02:00
junit = { module = "junit:junit", version.ref = "junit" }
2026-03-23 01:41:39 +02:00
kermit = { module = "co.touchlab:kermit", version.ref = "kermit" }
2026-04-21 23:49:54 +02:00
kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" }
kotlin-testJunit = { module = "org.jetbrains.kotlin:kotlin-test-junit", version.ref = "kotlin" }
2026-03-23 01:41:39 +02:00
kotlinx-coroutinesSwing = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-swing", version.ref = "kotlinx-coroutines" }
feat: read a room's group events again when they arrived out of order Relays impose no ordering, so a kind:445 can turn up before the group can read it: an application message encrypted under an epoch whose commit has not landed, or a commit for an epoch ahead of the local one. Both are stored and then dropped -- MarmotInboundManager refuses an out-of-epoch commit precisely so it does not half-mutate the group -- and nothing goes back for them once the missing event fills the gap. The message is on disk, readable, and never read. A "Reindex Events" button at the bottom of the group's detail screen is that second look. Only events with nothing to show for them are replayed: no chat line at all, or one of the two placeholder types. A room where nothing went wrong is left exactly as it was, which is what makes the button safe to press on a hunch. Passes repeat while a pass recovers something, because created_at order is not epoch order and a commit recovered by one pass is what lets the next read the messages that were waiting on it. **Replaying was not safe as it stood.** Every row the path writes is keyed on an event id and upserts in place -- MarmotGroupEvent, MarmotInnerEvent, and the nip30303 entities -- with one exception. ChatMessage's primary key is autogenerated, so writing a freshly built line always inserts, and a re-read would have left the room showing each recovered message twice, once as "Undecryptable Message" and once as itself. ChatMessage.reconcileMarmotLine matches on the group event id instead, so a re-read is an update, and refuses to let a placeholder overwrite a line that says something. That last rule is what protects the line this device wrote on the way out for a message it sent: our own kind:445 cannot be read back, since the sender ratchet has consumed the generation, and without the rule a replay would have replaced our words with "Undecryptable Message". The MLS group itself was already safe to replay against, which is worth saying because it is the part that looks dangerous: a commit behind the current epoch is rejected as a duplicate before it touches the group, one ahead is refused, and a consumed ratchet generation throws before mutating anything. The exception was quartz's EpochCommitTracker, which does not dedupe and only empties when a commit applies -- so replaying a held commit just grew the list and left it pending forever. forgetPendingCommits drops the room's entries first, and the sweep feeds the events back in the order CommitOrdering picks a winner in, so a contested epoch resolves the same way it would have on every other device. **What is testable, and what is not.** The DAO is not: testDebugUnitTest is plain JVM and Room's in-memory builder wants an Android Context. So the two pieces carrying decisions are lifted out where they can be run without one -- MarmotReindexSweep for the stopping rule, and reconcileMarmotLine for which of two lines wins -- and the DAO is left as query, sweep, write. The filter tests pin why the query's `tags LIKE` is a prefilter and not a test: an event belonging to another room can mention this one in a q tag, and its own h tag is what rejects it. **Not recovered by any of this.** A message whose key is gone -- one the ratchet has already advanced past, or one from an epoch predating this device's join. And events that never reached disk at all: storeNostrEvent is a single transaction, so a kind:445 arriving before its room exists rolls back its own insert along with the failed indexing, and only a re-sync brings it back. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-06 01:49:57 +02:00
kotlinx-coroutinesTest = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "kotlinx-coroutines" }
2026-03-30 13:09:49 +02:00
kotlinx-datetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version.ref = "kotlinx-datetime" }
2026-06-15 14:39:46 +02:00
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinxSerialization" }
kotlinx-serialization-cbor = { module = "org.jetbrains.kotlinx:kotlinx-serialization-cbor", version.ref = "kotlinxSerialization" }
2026-03-27 13:35:29 +02:00
ktor-client-core = { module = "io.ktor:ktor-client-core", version.ref = "ktor" }
ktor-client-cio = { module = "io.ktor:ktor-client-cio", version.ref = "ktor" }
2026-03-30 13:14:37 +02:00
ktor-client-websockets = { module = "io.ktor:ktor-client-websockets", version.ref = "ktor" }
ktor-serialization-kotlinx-json = { module = "io.ktor:ktor-serialization-kotlinx-json", version.ref = "ktor" }
lightning-kmp-core = { module = "fr.acinq.lightning:lightning-kmp-core", version.ref = "lightningKmpCore" }
2026-03-23 01:41:39 +02:00
navigation-compose = { module = "org.jetbrains.androidx.navigation:navigation-compose", version.ref = "navigationCompose" }
2026-04-18 02:41:31 +02:00
okio = { module = "com.squareup.okio:okio", version.ref = "okio" }
2026-07-01 15:11:31 +02:00
qrose = { module = "io.github.alexzhirkevich:qrose", version.ref = "qrose" }
2026-06-15 14:39:46 +02:00
sqldelight-android-driver = { module = "app.cash.sqldelight:android-driver", version.ref = "sqldelight" }
sqldelight-coroutines-extensions = { module = "app.cash.sqldelight:coroutines-extensions", version.ref = "sqldelight" }
sqldelight-native-driver = { module = "app.cash.sqldelight:native-driver", version.ref = "sqldelight" }
sqldelight-runtime = { module = "app.cash.sqldelight:runtime", version.ref = "sqldelight" }
build: phase 0 of the jvm target -- clear the ground, correct the plan First phase of docs/jvm-target.md. Nothing here turns the target on; it removes what would break the moment it is turned on, and stages the two catalog entries that cannot be derived automatically. Two of the four steps as written in the doc turned out to be wrong, and implementing them is how that surfaced -- both are corrected in the doc in this commit. **Deleted the stale jvmMain tree.** Six files under composeApp/src/jvmMain/kotlin/ac/cord/auxiliary/ survived from the Aux project this codebase grew out of. They have gone unnoticed because `jvmMain` is currently an orphan source set -- the accessor creates it, no target compiles it -- so the wrong package, the Room 2 imports (androidx.room, not androidx.room3), and the references to a long-gone AuxDatabase and AuxGlobal have never had to resolve. They would all become compile errors in phase 4. They are not lost: they are the closest thing to a skeleton for five of the six platform actuals phase 4 needs, and main.kt is a reasonable starting shape for the phase 5 desktop entry point. `git show HEAD~1` has them. **Added two catalog entries, not four.** sqlite-bundled-jvm and sqldelight-sqlite-driver. Both earn their place by being unreachable otherwise: sqlite-bundled-jvm has to be named explicitly because variant-aware resolution hands the *android* artifact to anything running on the host, and sqldelight-sqlite-driver is the jvm counterpart to the android-driver and native-driver entries already there. The doc also listed room3-runtime-jvm and sqldelight-jdbc-driver. Neither is right. Once jvm() exists, commonMain's existing androidx-room3-runtime resolves to the -jvm variant on its own, so an explicit entry is redundant and would drift. And the SQLDelight drivers phase 2 needs are for DbFactory, which lives in lightning-kmp-app -- a separate gradle build with its own version catalog, where an entry here is simply not visible. **kspJvm cannot be wired yet, and the build file already said so.** The doc's phase 0 told you to uncomment composeApp/build.gradle.kts:194. It contradicted its own phase 4, which is where jvm() gets turned on. The comment three lines above it states the rule: These configurations only exist when the ios targets are declared, which the kotlin block above does only on a mac. The same holds for kspJvm -- `dependencies { add("kspJvm", ...) }` throws UnknownConfigurationException until a jvm() target creates the configuration. So it moves into phase 4, into the same edit that declares the target. composeApp/build.gradle.kts is deliberately untouched by this commit. **Also documented: gradle does not run in a worktree here at all** until the submodule is checked out, which worktrees do not do automatically. `lightning-kmp-app/` is empty and configuration fails with "Project with path ':library' not found in build ':lightning-kmp-app'". Recorded in the phase 0 verification section along with the caveat that a linked worktree shares .git/modules/ with the main checkout, so both trees end up on one submodule git dir. **Not verified by a build.** For that reason. The deletion is an orphan source set and the additions are unreferenced catalog lines, so neither can change a build's outcome -- but that is an argument, not a green check, and it is the second commit in a row on this branch that has not compiled anything. Phase 4 is the first phase that genuinely cannot be done without a working gradle invocation. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-06 00:44:10 +02:00
sqldelight-sqlite-driver = { module = "app.cash.sqldelight:sqlite-driver", version.ref = "sqldelight" }
2026-03-30 13:14:37 +02:00
vitorpamplona-quartz = { module = "com.vitorpamplona.quartz:quartz", version.ref = "quartz" }
2026-03-23 01:41:39 +02:00
[plugins]
androidApplication = { id = "com.android.application", version.ref = "agp" }
2026-05-02 00:05:38 +02:00
androidx-room3 = { id = "androidx.room3", version.ref = "room3" }
2026-03-23 01:41:39 +02:00
androidLibrary = { id = "com.android.library", version.ref = "agp" }
composeHotReload = { id = "org.jetbrains.compose.hot-reload", version.ref = "composeHotReload" }
composeMultiplatform = { id = "org.jetbrains.compose", version.ref = "composeMultiplatform" }
composeCompiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
2026-03-23 02:21:18 +02:00
kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
2026-03-24 04:47:19 +02:00
kotlinPluginSerialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
2026-06-15 14:39:46 +02:00
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
sqldelight = { id = "app.cash.sqldelight", version.ref = "sqldelight" }