Repository navigation
feat: allow creating and editing thread titles #6739
Description
Activity
One motivating case I left implicit, in regards to question 2 above: the subject doesn't have to be typed by a human.
The familiar shape from AI chat UIs maps onto this primitive with no protocol change, the AI names the conversation from it's first exchange.
An auto-labeling agent and the human correcting it are different authors and both events survive.
Update: since I opened this, NIP-AR channel artifacts landed (spec in #7791, relay in #7919), and they cover most of what this issue was asking for. So rather than a new kind, I built thread titles on top of NIP-AR as a client-only convention. I've been running it locally against my own relay. @baxen, since this builds directly on your artifact work, I'd like your read on whether this works alongside the intended use.
What it boils down to
A thread title is a kind
45010artifact:Tag Value typebuzz.thread-title(I can move it outside the buzz reserved namespace prior to PR, just let me know)dUUIDv5 of the thread root event id (fixed namespace), so there is exactly one title per thread and a second concurrent create is rejected hthe channel rootthe thread root event titlethe title, also copied into contentso full-text search finds itRenaming is
op=updatewithprevset to the current head, and clearing isop=delete(thenrestoreto set it again). No relay changes were needed.What this replaces in my original proposal
- New kind 30180: not needed. Kind 45010 with a typed artifact covers it.
- Authorization: settled by NIP-AR. Editing a title requires the same permission as posting a kind:9 in the channel, which includes agents.
- Precedence between two editors: settled by the
prevchain. A stale edit getsconflict: artifact head changedinstead of silently winning. The desktop surfaces that as "someone else changed this thread's title". - Clearing a title:
op=delete/restore. - NIP-14
subjecttag: I don't think it's needed anymore, since the artifact is the single source of truth. - Searchability: still the same trap as issue subjects (search indexes
content, not tags), which is why the title is duplicated intocontent.
What it looks like
These are preview screenshots from my local build (mocked data, not a real channel):
In the channel: the title sits on its own line under the author of the thread's first message. Clicking it opens the thread.
In the thread panel: the title replaces "Thread" in the header and can be edited inline (pencil on hover; Enter saves, Escape cancels, empty clears).
In the Inbox: mentions and activity from a named thread show the title under "Mentioned in #channel", and the detail header reads "Title · #channel".
A new Threads view next to Inbox in the sidebar: every named thread you can read, across channels, sorted by most recent activity, with the same unread dot and "N new" counts that channels use.
There's also a CLI side,
buzz threads title set|clear|get|list, so agents can name the threads they pick up work in.Next step
If this direction is acceptable, I have the branch ready to open as a PR (SDK convention + CLI + desktop, with tests). I'm also happy to adjust the type name or any part of the convention to match how you plan to define the first
buzz.*artifact contracts.- added 2 commits that reference this issue
on Oct 6, 2026 One thing I have not done yet is automated title creation by the agent. I am thinking that if you start a thread by tagging an agent, then the agent could automatically name the thread (just like in other desktop AI chats like Berd/ChatGPT/etc) but if just tagged later in the thread it doesn't automatically set the title even if it's empty. That could also be an optional setting whether or not to let agents name threads when tagged at top of the thread.
- changed the title
[-]Threads have no editable, shared subject, feed rows are titled by kind, Inbox rows have no headline[/-][+]feat: allow creating and editing thread titles[/+]on Oct 6, 2026 - added a commit that references this issue
on Oct 9, 2026



Update (Oct 2026): NIP-AR channel artifacts (#7791 spec, #7919 relay) cover most of this, so the proposal below is superseded. Thread titles are now built as a
buzz.thread-titleartifact. Current design and screenshots: #6739 (comment). Agent auto-titling idea: #6739 (comment)Original proposal (Aug 2026): dedicated kind 30180, kept for context
Motivation
A thread in Buzz has no name anyone can set, so every surface that wants to title one invents something.
feedHeadline()(desktop/src/features/home/ui/FeedSection.tsx:61) is a pure kind→string switch rendered as the row headline at:196—"Forum post","Forum reply","Mention","Channel update". Ten conversations render as ten rows reading "Mention" / "Channel update".getInboxTypeLabel(), then the raw message preview (InboxListPane.tsx:306,:443). Nothing says what the conversation is about.feedHeadline()atdesktop/src/features/home/lib/inbox.ts:137does the right thing —subjecttag → first line → fallback — and assigns it toInboxItem.subject(declared:59, computed:591, set:621). Nothing renders it. Those four lines are everysubjecthit underfeatures/home.Prior art: NIP-14 defines a
subjecttag for threaded events, and Buzz usessubjecton issues and PRs (crates/buzz-sdk/src/builders.rs:1116,:1501). What's missing is an editable, shared, searchable subject for kind:9 / 45001 threads. NIP-14's lives on the root, so it's write-once —build_edit(builders.rs:389) carries onlyh,eand content, and nothing in Buzz mutates a tag on a stored event — and tags aren't full-text indexed.Proposed solution
A stored, user-signed, channel-scoped parameterized-replaceable kind whose
contentis the subject text.30180looks free;kind.rsis authoritative).d= the thread root event id — the replacement key, so relabeling is just publishing again and old threads behave like new ones.e= the same root id,h= the channel.replace_parameterized_event(community, &event, &d_tag, channel_id)(crates/buzz-relay/src/handlers/ingest.rs:3153) already takes achannel_id, so channel-scoped 30xxx is supported — existing 30xxx kinds just haven't used it that way.content, not a tag. The FTS generated column indexescontentonly (migrations/0008_fresh_install_search_allowlist.sql:15). A tag would be permanently unsearchable — which is why issue and PR subjects aren't full-text searchable today.Unlabeled stays first-class: no prompt, no empty-state nag, no required field. Plenty of channels need no labels at all.
This needs a dedicated ingest validator — the generic NIP-33 path enforces none of it. Invariants: exact tag cardinality and
d == e, both valid 64-hex; target event exists, is top-level, and belongs to the channel inh;contentnon-empty after trimming and ≤256 chars; plus an explicit authorization rule. Note thatcheck_channel_membership(handlers/ingest.rs:742) returnsOk(())for non-members in open channels, while the closest precedent — the kind:9002 channel topic — callsis_member_cached()and rejects them outright (handlers/side_effects.rs:623-633).Decisions I'd like from a maintainer before writing code:
is_member_cached), root author only, or channel owner/admin?(pubkey, kind, d), so distinct authors survive side by side and clients need a deterministic pick. "Root author wins" breaks the case this feature is partly for: an agent that authors both root and subject is the root author, so its owning human could never override it. Client-side rule, or one relay-selected canonical value with an explicit writer list?subjecttag on the root at creation? I'd default no: write-once, unsearchable in Buzz, and permanently divergent once the real subject is edited. Only worth it if a specific non-Buzz client is known to readsubjecton kinds 9/45001.Alternatives considered
subjecttag on the root (the NIP-14 shape). Write-once, per above — can't label an existing thread, can't correct a bad label, can't be set by anyone but the author, and unsearchable.api/bridge.rs:539-543), which is exactly the state a freshly created, freshly labeled thread is in.Additional context
Known required work — this is not a one-file change, which is why I'd rather agree the shape here than open a speculative PR:
buzz-coreALL_KINDSWINDOW_AUX_KINDS(api/bridge.rs:384-389) — it's a closed 4-kind list, so anetag alone gets a new kind nothing and a subject would store fine then vanish on reloadcrates/buzz-db/src/migration.rs:772-773), plusscripts/maintenance/nip_rs_search_allowlist.sqlfor populated installssrc-tauri/src/commands/messages.rs:178hard-codes[9, 40002, 45001, 45003]), and result→root mapping so a hit opens the thread rather than the metadata eventmobile/lib/features/search/search_provider.dart:139)docs/nips/NIP-**.md,kind.rsI have the full tracing behind the migration and search rows available if it's useful.
Related: #6067 (working set of labelled items — a shared signed subject would be an input, not a competitor); #4266 and draft PR #6350 (thread rail); #5254 (search/filter threads in a forum channel).
Searched open issues and PRs for
topic,subject,rename, andthread titlebefore filing; the onlytopichits are channel-level (set_channel_topic). Point me at a duplicate and I'll close this.Happy to implement once the four questions are settled.