Cut views over to ConversationStore; delete legacy store, bridge, resolver

Feature models observe per-conversation objects directly: PublicChatModel
forwards the active Conversation's objectWillChange, PrivateInboxModel
republishes only for the selected peer's conversation - background
appends no longer invalidate foreground views. LegacyConversationStore,
the coalescing bridge, and IdentityResolver are deleted (resolver
canonicalization proved display-invisible: nothing enumerates direct
conversations, lookups are by exact peer ID, and raw keying is strictly
more robust - documented as a design deviation). Selection state moves
into the store. ChatViewModel.messages/privateChats survive as derived
read views for coordinators that genuinely need them; hot paths use a
new store-direct privateMessages(for:) witness.

Final numbers vs pre-migration baselines:
pipeline.privateIngest 9.7k -> 24.0k msg/s (2.5x)
pipeline.publicIngest  6.8k -> 13.7k msg/s (2.0x)
delivery updates       38k  -> 117-133k/s

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
jack
2026-06-11 13:57:12 +02:00
co-authored by Claude Fable 5
parent 7fb1f4a219
commit 38331e62f1
32 changed files with 542 additions and 1214 deletions
+5 -5
View File
@@ -15,19 +15,19 @@ final class ConversationUIModel: ObservableObject {
private let chatViewModel: ChatViewModel
private let privateConversationModel: PrivateConversationModel
private let conversationStore: LegacyConversationStore
private let conversations: ConversationStore
private var activeChannel: ChannelID
private var cancellables = Set<AnyCancellable>()
init(
chatViewModel: ChatViewModel,
privateConversationModel: PrivateConversationModel,
conversationStore: LegacyConversationStore
conversations: ConversationStore
) {
self.chatViewModel = chatViewModel
self.privateConversationModel = privateConversationModel
self.conversationStore = conversationStore
self.activeChannel = conversationStore.activeChannel
self.conversations = conversations
self.activeChannel = conversations.activeChannel
self.currentNickname = chatViewModel.nickname
self.isBatchingPublic = chatViewModel.isBatchingPublic
self.showAutocomplete = chatViewModel.showAutocomplete
@@ -151,7 +151,7 @@ final class ConversationUIModel: ObservableObject {
.receive(on: DispatchQueue.main)
.assign(to: &$isBatchingPublic)
conversationStore.$activeChannel
conversations.$activeChannel
.receive(on: DispatchQueue.main)
.sink { [weak self] channel in
self?.activeChannel = channel