feat(workflow): implement the send_dm action (WF-07) - #2614
Draft
SeanGearin wants to merge 1 commit into
Draft
Conversation
Signed-off-by: Sean Gearin <sgearin@gmail.com>
SeanGearin
force-pushed
the
feat-workflow-send-dm
branch
from
July 23, 2026 20:20
4dd361a to
4742454
Compare
SeanGearin
marked this pull request as draft
July 23, 2026 21:21
Contributor
Author
|
Converted to draft pending the design discussion in #2628, per CONTRIBUTING's guidance to open an issue first for significant changes. I'll mark it ready once the approach — in particular the signing-key question raised there — is settled. |
Wirenut33
added a commit
to Wirenut33/buzz
that referenced
this pull request
Aug 2, 2026
Narrow the upstream pull request to the unique atomic workflow deletion repair; send_dm remains under discussion in PR block#2614 and issue block#2628. Co-authored-by: Michael Morale <michaelmorale1@gmail.com> Signed-off-by: Michael Morale <michaelmorale1@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The
send_dmworkflow action is fully wired through the schema(
ActionDef::SendDm) and template resolution, but its dispatch arm is amaintainer TODO —
crates/buzz-workflow/src/executor.rs:582// TODO (WF-07): emit DM event.— so every workflow step that uses it failswith
WorkflowError::NotImplemented("SendDm").Scope is deliberately
send_dmonly; the neighbouringSetChannelTopicTODOat executor.rs:588 is untouched (one action per PR).
Fix
Mirrors the two patterns the codebase already establishes:
SendMessagearm exactly: the executorloads the run + workflow (community-scoped, same wrong-community rationale
as SendMessage) to obtain the owner pubkey, then calls a new
ActionSink::send_dm. The relay-sideRelayActionSink::send_dmbuilds akind:9 event, signs with the relay keypair, attributes the owner via
ptag, persists via
insert_event_with_thread_metadata(top-level: depth 0),and runs
dispatch_persistent_eventunder the run's owning community —the same flow as
send_messageincrates/buzz-relay/src/workflow_sink.rs.channel_type='dm'), notNIP-17 wraps. The sink opens (or reuses) the owner↔recipient DM channel via
the same idempotent
open_dmpath the kind:41010handle_dm_opencommandhandler uses, and mirrors that handler's post-open side effects: on create —
channels-created metric, membership-cache invalidation,
dm_createdsystemmessage, group discovery events, membership notifications; on re-open — the
owner's NIP-DV visibility snapshot refresh.
Details:
p-tagged on the message event in addition to the ownerattribution tag: agent wake (
event_mentions_agentin buzz-acp) and pushdelivery are
p-tag gated, so an un-tagged DM would never notify anyone.resolve_dm_recipientnormalizes thetofield tolowercase hex before the sink call (the executor-side analogue of
resolve_send_message_channel). It accepts hex or bech32npub— the| npubtemplate filter produces the latter — and rejects empty, malformed,and unresolved
{{...}}literal values.to== workflow owner) is rejected asInvalidInputrather thanattempting a 1-participant channel (
create_dmrequires >= 2 participants).SendMessage, the DM body is deliberately not written to relay logs(DMs are private; logs are not).
buzz:workflowtag is carried, preserving the recursive-trigger guard.Design note for reviewers: signing follows the established workflow pattern —
relay keypair signs, owner attributed via
ptag. If WF-07 intendedowner-key-signed DMs or a dedicated workflow identity instead, the sink method
is the single place to change.
Test evidence
cargo fmt -p buzz-workflow -p buzz-relay— clean.cargo clippy -p buzz-workflow -p buzz-relay --all-targets— no warnings.-p buzz-workflow—fmt/clippy/nextestall green (includes this PR'snew
resolve_dm_recipientunit tests).-p buzz-relay—fmt+clippygreen;nextestpasses except 8environment-dependent failures in
api::media::*/api::admin::feedback_*fixtures (they want the sidecar/storage setup — thegenuinely DB-gated tests show as
ignored, requires Postgresin the samerun, so these are a different class). They fail identically on unpatched
main, and none are in files this change touches (the diff onlymodifies
workflow_sink.rs,action_sink.rs,executor.rs). Reproduce:cargo nextest run -p buzz-relayonmainshows the same 8.resolve_send_message_channeltests): hex accepted, npub normalized tohex, uppercase normalized, whitespace trimmed, empty/whitespace rejected,
malformed + unresolved-template literals rejected, and
resolve_step_templatesresolving bothsend_dmfields from a parsed YAMLstep.
workflow_send_message_p_tags_mentioned_member, run withcargo test -p buzz-relay --lib workflow_sink -- --ignored):workflow_send_dm_opens_channel_and_p_tags_recipientasserts the emittedevent lands in a
channel_type='dm'/visibility='private'channel, bothparticipants are
p-tagged, a second send reuses the same channel(idempotent open), and self-DM is rejected.
Links
crates/buzz-workflow/src/executor.rs:582(
TODO (WF-07): emit DM event)RelayActionSink::send_message(
crates/buzz-relay/src/workflow_sink.rs)buzz_db::dm::open_dm/handle_dm_open(crates/buzz-relay/src/handlers/command_executor.rs)