Skip to content

feat: shared thread titles on NIP-AR artifacts - #8563

Open
jhedlund wants to merge 1 commit into
block:mainfrom
jhedlund:feat/thread-titles
Open

jhedlund wants to merge 1 commit into
block:mainfrom
jhedlund:feat/thread-titles

Conversation

@jhedlund

Copy link
Copy Markdown
Contributor

Summary

Threads in Buzz have no name anyone can set. This adds shared, editable thread titles as a client-only convention on NIP-AR channel artifacts (#7791, #7919). The relay needs no changes.

Convention (buzz_sdk::thread_title, so the CLI and desktop write byte-identical artifacts):

Tag Value
kind 45010
type buzz.thread-title
d UUIDv5(fixed namespace, "buzz.thread-title:" + lowercase root id): one title per thread. The type is folded into the name, so a future type change gets fresh ids (NIP-AR never reuses a deleted d).
h the channel
root the thread root event
title the title, also copied into content for readers that ignore tags

Renaming is a normal artifact update from the current head. Clearing is op=delete, and setting a title after a clear is restore. Authorization is NIP-AR's: anyone who can post a kind:9 in the channel, agents included, can title a thread.

CLI: buzz threads title set|clear|get|list. Writes plan create/update/delete/restore from the current head and re-read on a head conflict. set --if-unset leaves an existing or cleared title alone.

Agent auto-titling (buzz-acp): when a human's new top-level channel message mentions agents, the first-mentioned agent is asked to title the thread with --if-unset, the way chat apps name a new conversation. Replies, edits, DMs and agent-authored roots never trigger it, and later mentions never rename a thread.

Desktop (through Tauri commands, since artifact queries are HTTP-only on the relay):

  • The thread panel header shows the title with an inline editor: pencil on hover, Enter saves, Escape cancels, blank clears. Edits carry the revision they started from, so a concurrent rename is reported ("someone else changed this thread's title") instead of overwritten.
  • The title sits on its own line under the author of a thread root in the channel, and clicking it opens the thread. It appears before anyone replies.
  • Inbox rows show the title under "Mentioned in #channel", and the detail header reads "Title · #channel".
  • Titles are looked up per root by their derived d, up to 100 roots per query. Lookups made in the same tick share one query. A returned title only counts when its d matches its root. There's no global title list, so lookups stay exact however many titles a community has.
  • Artifact events in the open channel refresh titles live.

Not in this PR:

  • Search. The title is in content, but desktop search hardcodes [9, 40002, 45001, 45003], and the FTS allowlist (fresh installs, nip_rs_search_allowlist.sql) leaves search_tsv NULL for kind 45010. Enabling it means widening the allowlist, which rewrites a generated column on populated relays, plus mapping hits back to root. I'd rather a maintainer decides that separately.
  • A cross-channel Threads list. Kept apart because it overlaps feat(read-state): followed threads, follow intents and a threads view #8264, but I am putting mine in Draft so it's a visible PR next.
  • Mobile. Titles don't show on mobile yet.

Open question: buzz.* is the reserved artifact type namespace. I used buzz.thread-title because this is meant to become a Buzz convention. If you'd rather it start outside the namespace, it's a one-constant change.

Related issue

Partially closes #6739 (title plumbing). Related: #8264 (relay threads view; its /me/threads rows carry the channel_id + root_id a title is keyed by), #6067, #5254 (thread search in forums).

Testing

  • SDK (thread_title.rs): head selection follows the prev chain rather than timestamps. Also covers the write-plan lifecycle, revisions passing relay validation, ignoring other artifact types, title normalization, root filters naming each derived d, and that only compare-and-set losses are retryable.
  • CLI: --if-unset skips live and cleared titles but not untitled threads.
  • buzz-acp: the first-mentioned agent is asked to title a new thread, and an unknown profile ahead of it doesn't block that. Replies, agent authors, DMs and unmentioned agents never auto-title.
  • Desktop unit (threadTitles.test.mjs): same-tick lookups share one fetch, invalid ids are skipped, a failed batch rejects every lookup in it. Also covers revision handling on writes, draft trimming and clearing, and UTF-8 byte bounds.
  • Desktop e2e (thread-titles.spec.ts): name a thread from its header, then clear it. A title another user sets appears before anyone replies.
  • cargo fmt and cargo clippy (0 warnings) are clean. Desktop typecheck, file-size checks and biome on changed files pass.
  • Tests, run on Windows on the feat/threads-view tip (which includes this commit): desktop Tauri 3254 passed; desktop unit 161/161; buzz-cli 500 passed; buzz-sdk and the other crates pass; buzz-acp 1046 passed. Five buzz-acp tests in acp.rs/relay (idle/keepalive, blocked recovery, two steer-capture tests) hang or fail the same way on plain main on this machine, because they drive sh/sleep child processes.

I've run this daily against my own relay since early October, with agents auto-titling threads.

A thread's title is a kind-45010 artifact with type buzz.thread-title
(the name proposed upstream in block#6739):

- d = UUIDv5(namespace, "buzz.thread-title:" + root). Folding the type
  into the name means a future type change gets fresh ids, since NIP-AR
  never reuses a deleted d.
- h = channel, root = thread root, title tag. The title is also copied
  into content for readers that ignore tags; relay search does not index
  kind 45010, so titles are not searchable yet. Clearing is op=delete;
  setting again after a clear is restore.

The convention lives in buzz_sdk::thread_title (type, namespace, head
selection, write planning, revision builder) so the CLI and the desktop
write byte-identical artifacts.

CLI: `buzz threads title set|clear|get|list`. Writes plan
create/update/delete/restore from the current head and re-read on head
conflicts. `set --if-unset` leaves an existing or cleared title alone.

buzz-acp: when a human's new top-level channel message mentions agents,
the first-mentioned agent is asked to title the thread with --if-unset.
Replies, edits, DMs and agent-authored roots never trigger it.

Desktop, through Tauri commands (artifact queries are HTTP-only on the
relay):

- The thread panel header shows the title with an inline editor (pencil,
  Enter saves, Escape cancels, blank clears). Edits carry the revision
  they started from, so a concurrent rename is reported, not overwritten.
- The title sits on its own line under the author of a thread root in
  the channel (clickable, opens the thread) and on Inbox rows and the
  Inbox detail header.
- Titles are looked up per root by their derived d, in batches of 100,
  and lookups made in the same tick share one query, so lookups stay
  exact however many titles exist. A returned title only counts when its
  d matches its root.
- Artifact events in the open channel refresh titles live.

Co-authored-by: Jeff Hedlund <jhedlund@gmail.com>
Signed-off-by: Jeff Hedlund <jhedlund@gmail.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@jhedlund
jhedlund requested a review from a team as a code owner October 23, 2026 21:01
@github-actions

Copy link
Copy Markdown

🔐 Codex Security Review

Status: review required for the current range.

The current range is 9e8c9fc04daf20259cd259dcaec89109587cc7f5...5ce84f6520f02743a89600be9625265714490e29.
A new review must complete for this exact range. When manual authorization
is required, a user with write access must comment exactly
@buzz-security-review 5ce84f6520f02743a89600be9625265714490e29 to authorize a new review.
Any previous review applies only to its recorded range.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: allow creating and editing thread titles

1 participant