docs(quartz): correct the Buzz kind-conflict table and document how to read Buzz's source

Two rows of "Kind conflicts — implemented but NOT registered in EventFactory" were
stale: 20001 and 39005 are both dispatched now, disambiguated by tag shape inside
the kind's block (`g` for BitChat presence, `h` for a Buzz thread summary). Split
the table into the disambiguated pair and the three where the incumbent really does
keep the slot, and record why the 39005 signal is safe — the NIP-29 relay-generated
39xxx family is `d`-addressed and never emits `h` — plus the fact that nothing
throws when it is wrong, so the failure is silent.

Also document reading Buzz's Rust without a checkout, which currently costs everyone
the same detour: KDoc across this package cites crate-relative paths
(`buzz-relay/src/handlers/...`) while the repo puts everything under `crates/`, and
`raw.githubusercontent.com` 404s on those paths with plain curl usually sandboxed —
`gh api ... contents/... | base64 -d` is the way in. Notes where the answers live
(handlers for what the relay emits, buzz-db/channel.rs for channel_type and the two
visibility values, desktop/src/features for what Buzz's own client renders — which
decides whether an event we publish is visible to anyone), and that the relay's tests
are the best spec: `channel_scoped_content_kinds_require_h_tags` is what establishes
that canvas and the forum kinds are per-channel, not per-workspace.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Vitor Pamplona
2026-07-26 19:18:53 -04:00
co-authored by Claude Opus 5
parent bc1cce6065
commit 6a5d55aa9b
@@ -25,6 +25,37 @@ The prose specs under Buzz's `docs/nips/` are **drafts and lag the code**. Every
here is confirmed against the authoritative Rust — `crates/buzz-core` (the per-kind
modules), `crates/buzz-sdk` (event builders, `nip_oa.rs`) — not the markdown.
### Reading Buzz's source without a checkout
KDoc across this package cites Rust files as `buzz-relay/src/handlers/side_effects.rs`.
Those are **crate-relative**: on disk everything lives under `crates/`, so the real path is
`crates/buzz-relay/src/handlers/side_effects.rs`. Prefix accordingly or the fetch 404s.
`gh` is the way in (the repo is public, but `raw.githubusercontent.com` 404s on these paths
and plain `curl` is usually sandboxed):
```bash
gh api -X GET search/code -f q='emit_system_message repo:block/buzz' --jq '.items[].path'
gh api repos/block/buzz/contents/crates/buzz-relay/src/handlers/side_effects.rs --jq '.content' | base64 -d
```
Worth knowing where the answers tend to live:
- `crates/buzz-relay/src/handlers/``side_effects.rs` (what the relay emits and when:
system messages, discovery, thread summaries), `event.rs` (ingest + acceptance),
`command_executor.rs` (the 90xx command verbs).
- `crates/buzz-db/src/channel.rs` — the channel row: `channel_type` (`stream` / `forum` /
`dm` / `workflow`) and `visibility` (exactly two values, `open` = searchable + anyone can
join, `private` = hidden + invite-only).
- `desktop/src/features/**` — what Buzz's own client actually *renders*, which is what
decides whether an event we publish is visible to anyone else. Worth checking before
adding a write path: e.g. forum posts only ever surface in `channelType === "forum"`.
The relay's **tests are the best spec** — several encode invariants as named assertions.
`channel_scoped_content_kinds_require_h_tags` in `handlers/event.rs` is the one to know:
canvas (40100) and the forum kinds (45001/45002/45003) are per-**channel**, never
per-workspace, because an `h` tag is mandatory on all of them.
Compliance is verified against **vectors generated by Buzz's own code**, not
hand-transcribed schemas:
@@ -64,17 +95,33 @@ conflicts below).
| `presence` / `huddles` / `pairing` / `audit` / `media` | presence + misc | 20001, 20002 / 24810, 48100-48106 / 24134 / 48001 / 49001 |
| `rsReadState` | NIP-RS | (helpers on `AppSpecificDataEvent`, kind 30078) |
### Kind conflicts — implemented but NOT registered in EventFactory
### Kind conflicts — where a Buzz kind number is already owned
These Buzz kind numbers are already owned by an existing Amethyst/Nostr class, so the
Buzz model exists (build/parse it explicitly) but the incumbent keeps the `EventFactory`
dispatch slot:
Several Buzz kind numbers collide with an existing Amethyst/Nostr class. `EventFactory`
resolves what it can by **tag shape inside the kind's block** — the two meanings never
coexist on one relay, and each carries a tag the other never emits, so one `if` inside the
branch routes both. The rest keep the incumbent and are built/parsed explicitly.
**Disambiguated — both classes reachable:**
| Kind | Discriminator | Present → | Absent → |
|---|---|---|---|
| 20001 | `g` (geohash) tag | `bitchat.geohash.GeohashPresenceEvent` | `presence.PresenceUpdateEvent` |
| 39005 | `h` (channel) tag | `cwChannelWindow.ThreadSummaryEvent` | `nip29RelayGroups.metadata.GroupPinnedEvent` |
Both discriminators are load-bearing in *both* directions, since outbound signing goes
through the same factory — check any new one against the two builders as well as the wire.
For 39005 the signal is that the whole NIP-29 relay-generated 39xxx family (metadata,
admins, members, participants, supported-roles, pinned) is addressed by `d` alone and never
emits `h`, while a Buzz thread summary always carries one. Nothing throws on a mismatch, so
getting this wrong is silent: a summary parsed as a pin list would have reported the thread
root as a pinned message. Covered by `nip29RelayGroups/PinEventsTest`.
**Incumbent keeps the slot — build/parse the Buzz model explicitly:**
| Kind | Buzz class | Incumbent (registered) |
|---|---|---|
| 9041 | `moderation.ModerationUnbanEvent` | `nip75ZapGoals.GoalEvent` |
| 20001 | `presence.PresenceUpdateEvent` | `experimental.bitchat.geohash.GeohashPresenceEvent` |
| 39005 | `cwChannelWindow.ThreadSummaryEvent` | `nip29RelayGroups.metadata.GroupPinnedEvent` |
| 49001 | `media.MediaUploadEvent` | — (Buzz's own `kind.rs` marks 49001 "Not a relay event kind") |
| 30078 | `rsReadState` (helpers) | `nip78AppData.AppSpecificDataEvent` (NIP-RS reuses 30078) |