Merge branch 'mantra' into claude/project-feature-plan-b58ab2

Eighteen commits of subgroup adoption and key-ceremony work, and one collision
that was the point of writing Phase 0 down.

**Schema.** `mantra` took 21 for `GroupKeyStateParent` -- a room's parentage as a
row, so it can have two -- as a manual `MIGRATION_20_21` that copies before it
drops. Projects had taken 21 on this branch. Neither had shipped, so projects
renumbered to **22**, an `AutoMigration(from = 21, to = 22)` on top of theirs;
`schemas/21.json` is now theirs and `22.json` is the two together, 58 tables.
docs/project-feature.md records it, including that Phase 0 told us to check for
exactly this and checked the sibling fork rather than the base branch.

Three conflicts. `MantraDatabase.kt` wanted both entity sets and both
migrations, and their v21 contributes only a comment there since the entry is
manual. `ProposedEventTest.kt` and the schema file were additive either way.

Nine files auto-merged, and `ChatMessage.kt` was the one to watch: they reshaped
the `SubgroupBirthCertificateEvent` arm while this branch added three project
arms to the same `when`. Git saw no conflict and the compiler agrees.

**One test fails, and it arrives with the merge rather than from it.**
`NavigationRoutingTest` reports "uncaught exceptions before the test started",
which is leakage, not its own failure: `CeremonyQueueRoutingJvmTest` (new in
b6c3e3dd) builds a `ChatMessageListViewModel` over
`NO_OP_NOSTR_REPOSITORY`, whose `queueNegentropySynchronizeRequest` is a
`TODO()`, and it throws on a background dispatcher. Whichever `runTest` starts
next is blamed, so it is order-dependent -- the same test passes alone and in
smaller selections. This branch touches neither `scheduleSynchronization` nor
`NostrRepository`; its only edit to that view model is the `summarize` call.

1051 jvmTest with that one failure, 482 testDebugUnitTest clean, m3Audit's
budgets all met.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Kgothatso NgakoandClaude Opus 5 committed 2026-10-02 20:48:17 +02:00
commit 3cd9001c27
57 files changed
+12661 -1055

No files matched your search

@@ -77,6 +77,7 @@ import press.mantra.compose.database.model.GiftWrapMessage
import press.mantra.compose.database.model.GiftWrapPayload
import press.mantra.compose.database.model.GiftWrapSeal
import press.mantra.compose.database.model.GroupKeyState
import press.mantra.compose.database.model.GroupKeyStateParent
import press.mantra.compose.database.model.GroupSignedEvent
import press.mantra.compose.database.model.InReplyToRelation
import press.mantra.compose.database.model.MantraArtifact
@@ -145,6 +146,7 @@ val GENESIS_AT = Instant.fromEpochMilliseconds(1231006505000L)
GiftWrapSeal::class,
GiftWrapPayload::class,
GroupKeyState::class,
GroupKeyStateParent::class,
GroupSignedEvent::class,
InReplyToRelation::class,
MantraArtifact::class,
@@ -186,7 +188,7 @@ val GENESIS_AT = Instant.fromEpochMilliseconds(1231006505000L)
UnsignedNostrEvent::class,
Zap::class
],
version = 21,
version = 22,
autoMigrations = [
// v2 only adds the DkgSession/DkgParticipantMessage tables, so Room can
// generate the migration itself — nothing existing changes shape.
@@ -330,9 +332,25 @@ val GENESIS_AT = Instant.fromEpochMilliseconds(1231006505000L)
// which is what they were, and any identity may drain them. The broadcast
// queue deliberately gets no owner: a signed event is anyone's to carry.
AutoMigration(from = 19, to = 20),
// v21 adds the three project tables -- a project, and one row per source
// v21 moves a room's parentage off two GroupKeyState columns onto the new
// GroupKeyStateParent table, so a room can be the child of more than one
// group -- founded with one and adopted by another later. It copies
// before it drops, which no AutoMigration can express, and it drops
// ChatRoom.parentChatRoomId as well: that column was a cache of the
// verified value which nothing in production ever read, and keeping it
// would mean a column holding *one* of several parents, which is worse
// than unused. See MIGRATION_20_21 and
// docs/subgroups-multiple-parents.md.
//
// v22 adds the three project tables -- a project, and one row per source
// version or translation that is part of it. Three new tables and
// nothing existing changes shape, so Room generates this itself.
// nothing existing changes shape, so Room generates this one itself.
//
// It is v22 rather than v21 because two trees allocated out of the same
// space in one release window: this landed as 21 on its own branch while
// the parentage table above took 21 on `mantra`. Renumbering was free --
// nothing had shipped -- but see Phase 0 of docs/project-feature.md,
// which says to check, and checked the wrong tree.
//
// A project's contents are foreign keys into this room's own rows, with
// CASCADE, like every other reference in this family. That is the
@@ -345,7 +363,7 @@ val GENESIS_AT = Instant.fromEpochMilliseconds(1231006505000L)
// The owners are a comma-joined sorted string on the project row rather
// than a fourth table, which is what DkgSession.participantPublicKeys
// already does with a set of keys and for the same reason.
AutoMigration(from = 20, to = 21),
AutoMigration(from = 21, to = 22),
]
)
@ColumnTypeConverters(MantraConverters::class)