Skip to content

fix(cli): resolve dms list from DM discovery instead of an unpublished kind - #5725

Open
Chessing234 wants to merge 2 commits into
block:mainfrom
Chessing234:fix/dms-list-empty-5424
Open

fix(cli): resolve dms list from DM discovery instead of an unpublished kind#5725
Chessing234 wants to merge 2 commits into
block:mainfrom
Chessing234:fix/dms-list-empty-5424

Conversation

@Chessing234

Copy link
Copy Markdown
Contributor

Root cause

dms list filtered on kind:41001. Nothing in the tree publishes that kind — KIND_DM_CREATED is declared in crates/buzz-core/src/kind.rs:513 and listed in the kind registry, and there is no emitter anywhere in crates/ or desktop/. The relay's DM-open path (command_executor.rs) writes the channel row, emits a dm_created system message, and calls emit_group_discovery_events — it never signs a 41001.

So the command returned [] for every identity, not only agent ones. That matches the report exactly: dms list[], then dms open on the same pair → {"created":false}, i.e. the relay resolved a DM that the listing had just denied. The two further agent identities the reporter could not confirm were almost certainly the same false negative.

This matters more than an ordinary empty result: channels get / channels members on a DM UUID return nothing from a non-member seat, so dms list is the only listing surface an agent has for its own DMs. "Identity X is a member of zero DMs" reads as a fact and, in the report's case, misattributed a production failure's root cause.

The fix

Query the DM's kind:39000 discovery event instead. emit_group_discovery_events (side_effects.rs:1070-1080) signs it at channel creation and, for channel_type == "dm" only, attaches hidden plus one p tag per participant. Filtering kind:39000 by our own pubkey therefore returns exactly the DMs we are a party to:

  • p tags are attached to DM discovery only, so non-DM channels do not match on #p in the first place;
  • channel-scoped storage keeps other people's DM discovery unreadable regardless of the filter, so this widens nothing;
  • the t=dm check still happens client-side, because #p alone cannot express it — a channel of any other type is skipped rather than listed.

This is the same resolution shape channels list --member already uses (membership events → channel metadata), so it is not a new access pattern.

Output is unchanged — still [{dm_id, participants, created_at}].

Uncertainty, stated plainly

  • I could not run this against a live relay, so the evidence here is code-path reading plus unit tests on the filter and the projection. What I can assert: 41001 has no emitter, and 39000 for a DM carries d + p-per-participant + t=dm.
  • DMs created before discovery events existed rely on reconcile_channel_discovery_events (side_effects.rs:3045) having backfilled a 39000. Any DM it missed will still not list. If you would rather this fall back to the 39002 → 39000 two-step that channels list --member uses, that is a small change and I am happy to make it.
  • KIND_DM_CREATED is now referenced by nothing but the registry. I left it in place rather than remove a public constant in a fix PR.

Verification

cargo fmt --all --check, cargo clippy -p buzz-cli --all-targets (clean), cargo test -p buzz-cli --lib — 348 pass, including 5 new tests pinning the filter and the projection against the tag shape the relay actually writes.

Closes #5424

dms list filtered on kind:41001. Nothing in the tree publishes that
kind - KIND_DM_CREATED is declared in buzz-core and emitted nowhere -
so the command returned [] for every identity, including ones with
existing DMs that `dms open` immediately resolves with created:false.
A false negative here is worse than an error: it is the only listing
surface an agent has for its own DMs, so "member of zero DMs" reads as
a fact.

Query the DM's kind:39000 discovery event instead. The relay signs it
on creation with t=dm, hidden, and one p tag per participant
(emit_group_discovery_events), so filtering that kind by our own pubkey
returns exactly the DMs we are a party to, and channel-scoped storage
keeps everyone else's unreadable. The output shape is unchanged.

The t=dm check happens client-side because the p tag alone cannot
express it; a channel of any other type is skipped rather than listed.

Refs block#5424

Signed-off-by: Taksh <takshkothari09@gmail.com>
Pins the filter (kind:39000 by participant, not 41001) and the row
projection against the tag shape emit_group_discovery_events writes for
a DM: d, hidden, one p per participant, closed, t=dm.

Also covers the two ways a p-tagged event must not become a row - a
non-DM channel type, and a DM with no d tag, since an id-less row is
worse than a missing one.

Refs block#5424

Signed-off-by: Taksh <takshkothari09@gmail.com>
@Chessing234
Chessing234 requested a review from a team as a code owner August 13, 2026 00:54
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.

dms list returns empty for agent identities that have existing DMs (false negative)

1 participant