Kgothatso Ngako 4cba5b9a69 feat(groups): schema 23 -- a membership is a row, so a group is the group's
`ChatRoom` was two things in one row keyed by the group id: the group's own facts --
its id, name and description -- and the asking profile's membership of it, which is
its MLS ratchet state, when it joined and left, whether it asked for a chronicle,
and whether it has been archived. A second profile joining the same group wrote over
the first's ratchet state, which breaks both members in the shape of "the other
device never got it". `679d5249` refuses that Welcome; this removes the reason it
had to.

**Split rather than a wider key.** Keying the room by (`id`, `userPublicKey`) was the
obvious reading and the worse schema. Of the nineteen tables referencing a room,
fourteen hold the *group's* signed work -- `GroupSignedEvent`, `GroupKeyState` and
the twelve `Mantra*` tables -- and two profiles in one group read the same chapters.
Those fourteen would have had to carry a `userPublicKey` that misrepresents them or
drop their foreign key and the cascade with it; `GroupSignedEvent` could not have
carried one at all, its key being the event's hash. Splitting leaves all nineteen
pointing at a room that is unambiguously the group's.

`ChatRoomDao` follows from that: a listing joins the membership, because "mine" and
"archived" are things one profile says about a group, while a lookup by id stays the
group's and filters nobody's `deletedAt` -- a manager asking for the room it is about
to act in wants the group, whether or not somebody tidied it away.

**The migration turns on one fact, and the fact is now measured.** Rebuilding
`ChatRoom` is unavoidable (a foreign key names two of the columns to remove, so
`DROP COLUMN` will not), and with foreign keys enforced no rebuild survives: the drop
fires all nineteen cascades, and `RENAME TO` rewrites the children's clauses to
follow it -- `PRAGMA legacy_alter_table` does **not** prevent that, which I checked
rather than assumed. A migration cannot disable foreign keys itself either, inside
Room's transaction. So this depends on Room disabling them, exactly the kind of
dependency `MIGRATION_9_10` warns against taking on trust -- and
`ChatRoomSplitThroughRoomJvmTest` asserts it against a real v22 file through
`getRoomDatabase`, with rows seeded in four of the nineteen. An earlier draft of this
migration lost every child row; that test is what caught it.

The replacement is also built *beside* the original and renamed into place after it
is gone, so nothing is ever renamed that the children point at.

The acting profile now comes from where it is actually known. `DatabaseChatRepository`
and `DatabaseDkgRepository` read it off the `activeIdentity` they already hold, the
inbound MLS path derives it from the `activeKeyPair` it is already given, and
`ChatMessageListViewModel` takes it from the screen that opened it. Leaving a group
and archiving one are a membership's acts now, so one profile doing either leaves the
other's list alone.

1008 tests, 0 failures. Schema export committed beside its predecessors.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-02 21:46:35 +02:00
2026-09-09 17:04:09 +02:00
2026-03-24 04:47:19 +02:00
2026-03-23 01:41:39 +02:00
2026-03-23 01:41:39 +02:00
2026-03-23 01:41:39 +02:00

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…

Description
Mantra as a kotlin multiplatform project
https://mantra.press
Readme 13 MiB
Languages
Kotlin 100%