From f18ba03a482b254292b77bff3b1432b1b06f8ea7 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 11 Jul 2026 03:53:17 +0000 Subject: [PATCH] fix(concord): store the kind-13302 list replaceably so the hub populates MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Concord hub was empty even after the 13302 arrived: ConcordCommunityListEvent extended plain Event, so LocalCache filed it by id and the dispatch when() had no branch for it — it fell through to consumeRegularEvent. But ConcordChannelListState observes the addressable note at Address(13302, pubkey, ""), which never received the event, so liveCommunities stayed empty. Make ConcordCommunityListEvent a BaseReplaceableEvent (kind 13302 is a NIP-01 replaceable — fixed empty d-tag, address = (kind, pubkey, "")) and add the consumeBaseReplaceable branch next to the kind-10009 list, so it lands in the addressable cache exactly like its NIP-29 sibling. The plane-wrap path (1059 -> concordSessions.ingest in DecryptAndIndexProcessor) was already correct. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01CzJ2Cwo8tg4oZq43oRa3ig --- .../java/com/vitorpamplona/amethyst/model/LocalCache.kt | 9 +++++++++ .../concord/cord02Community/ConcordCommunityListEvent.kt | 4 ++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt index 6dc1630e79..206a76eabf 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt @@ -49,6 +49,7 @@ import com.vitorpamplona.amethyst.model.nipBCOnchainZaps.OnchainZapResolver import com.vitorpamplona.amethyst.service.BundledInsert import com.vitorpamplona.amethyst.service.checkNotInMainThread import com.vitorpamplona.amethyst.ui.note.dateFormatter +import com.vitorpamplona.quartz.concord.cord02Community.ConcordCommunityListEvent import com.vitorpamplona.quartz.concord.cord03Channels.ConcordChannelId import com.vitorpamplona.quartz.experimental.agora.FundraiserEvent import com.vitorpamplona.quartz.experimental.attestations.attestation.AttestationEvent @@ -3772,6 +3773,14 @@ object LocalCache : ILocalCache, ICacheProvider { consumeBaseReplaceable(event, relay, wasVerified) } + // Concord private joined-communities list (kind 13302). Replaceable, self-encrypted; + // ConcordChannelListState observes it via the addressable cache (Address(13302, me, "")), + // so — exactly like the 10009 list above — it must be stored replaceably or the Concord + // hub stays empty even after the event arrives. + is ConcordCommunityListEvent -> { + consumeBaseReplaceable(event, relay, wasVerified) + } + is GroupMetadataEvent -> { consume(event, relay, wasVerified) } diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/concord/cord02Community/ConcordCommunityListEvent.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/concord/cord02Community/ConcordCommunityListEvent.kt index 96949c81d9..1bd59225bd 100644 --- a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/concord/cord02Community/ConcordCommunityListEvent.kt +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/concord/cord02Community/ConcordCommunityListEvent.kt @@ -23,7 +23,7 @@ package com.vitorpamplona.quartz.concord.cord02Community import androidx.compose.runtime.Immutable import com.vitorpamplona.quartz.concord.events.ConcordKinds import com.vitorpamplona.quartz.nip01Core.core.Address -import com.vitorpamplona.quartz.nip01Core.core.Event +import com.vitorpamplona.quartz.nip01Core.core.BaseReplaceableEvent import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner import com.vitorpamplona.quartz.utils.TimeUtils @@ -48,7 +48,7 @@ class ConcordCommunityListEvent( tags: Array>, content: String, sig: HexKey, -) : Event(id, pubKey, createdAt, KIND, tags, content, sig) { +) : BaseReplaceableEvent(id, pubKey, createdAt, KIND, tags, content, sig) { override fun isContentEncoded() = true /** Decrypts this list's entries with [signer], or empty on failure / wrong key. */