Files
amethyst/.claude/skills/nostr-expert/references/event-hierarchy.md
Vitor PamplonaandClaude Opus 5.5 395af4dc9c chore(skills): consolidate 33 skills into 14, verified against the tree
The 33 descriptions (~16k chars, ~19k with paths) overran Codex's skill-list
budget (2% of the context window, or 8,000 chars when unknown), and a review
found most bodies stale: paths from before the commons/commonsUI moves, the
deleted legacy desktop app, a callback signer API that no longer exists, and
example APIs that were never in the tree.

Kept, rewritten and checked against the code (every path, class and command):
account-state (+feeds), amy-expert, android-expert, auth-signers,
compose-expert (+API style, Navigation 3 back stacks), desktop-expert
(+run/package), event-store-semantics (+NIP-50 search), find-missing-
translations, kotlin-coroutines (+structured concurrency, flow modeling),
kotlin-multiplatform (+Gradle, composable extraction), ngit-pr,
nip85-trusted-assertions, nostr-expert (+NIP lookup, generated NIP catalog),
relay-client.

Dropped: textbook Compose/Kotlin technique skills, kotlin-expert,
kotlin-types-value-class, gradle-expert, quartz-integration (external-facing;
see the Quartz docs commit), find-non-lambda-logs (audit finished; the rule is
now in AGENTS.md Kotlin Style), and the /nip, /desktop-run, /extract commands.
The skill list is now ~4k chars. Also drops .claude/core-skills-plan.md
(stale history) and repoints SearchableEvent's KDoc to event-store-semantics.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 12:21:01 -04:00

3.0 KiB
Raw Permalink Blame History

Event hierarchy

All in quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip01Core/core/ unless noted.

IEvent (marker interface)
└── Event(id, pubKey, createdAt, kind, tags, content, sig)      Event.kt, @Immutable, open
    ├── BaseReplaceableEvent   kinds 0, 3, 10000–19999; dTag() = FIXED_D_TAG ("")
    ├── BaseAddressableEvent   kinds 30000–39999; dTag() from tags, address() = Address(kind, pubKey, dTag)
    └── every other typed event class (subclasses Event directly)

Both bases implement AddressableEvent (dTag(), address()), so a replaceable event also has an Address — with an empty d-tag. BaseReplaceableEvent does not extend BaseAddressableEvent.

Kind ranges (Kind.kt, verbatim)

fun Kind.isRegular() = this > 0 && this < 10_000 && this != ContactListEvent.KIND
fun Kind.isEphemeral() = this >= 20_000 && this < 30_000
fun Kind.isReplaceable() = this == MetadataEvent.KIND || this == ContactListEvent.KIND || (this >= 10_000 && this < 20_000)
fun Kind.isAddressable() = this >= 30_000 && this < 40_000

Note isRegular() excludes kind 0 and kind 3 and starts at 1, not 1000.

Shape of a typed event

Copy an existing class in the same NIP. TextNoteEvent (nip10Notes/TextNoteEvent.kt):

class TextNoteEvent(id: HexKey, pubKey: HexKey, createdAt: Long, tags: Array<Array<String>>, content: String, sig: HexKey) :
    BaseThreadedEvent(id, pubKey, createdAt, KIND, tags, content, sig),
    EventHintProvider, AddressHintProvider, PubKeyHintProvider, IForkableEvent, SearchableEvent {
    companion object {
        const val KIND = 1
        fun build(note: String, replyingTo: EventHintBundle<TextNoteEvent>? = null, …,
                  initializer: TagArrayBuilder<TextNoteEvent>.() -> Unit = {}) =
            eventTemplate(KIND, note, createdAt) { … initializer() }
    }
}

House rules visible there:

  • The constructor takes no kind; it passes its KIND constant up.
  • Construction goes through a companion build(...) that returns an EventTemplate<T> (unsigned); a NostrSigner signs it (signer.sign(template), see auth-signers).
  • References to other events come in as EventHintBundle<T> (nip01Core/hints/), which carries the relay hint along with the event.

Optional interfaces

Interface File Purpose
EventHintProvider / AddressHintProvider / PubKeyHintProvider nip01Core/hints/HintProviders.kt eventHints()/linkedEventIds() etc. — feed the relay-hint indexes and LocalCache gatherers
SearchableEvent nip50Search/SearchableEvent.kt NIP-50 full-text indexing — see the event-store-semantics skill
PrivateTagArrayEvent / PrivateReplaceableTagArrayEvent nip51Lists/ lists with NIP-44-encrypted private tags in content

Validation (nip01Core/crypto/EventExt.kt)

generateId(), verifyId(), verifySignature(), verify(): Boolean, and checkSignature() (throws). JSON: Event.fromJson(json) / Event.fromJsonOrNull(json) / event.toJson() via OptimizedJsonMapper.