feat(channels): add subscriber fan-out delivery

When messages are sent to a channel, subscribers now receive a copy
in their inbox with [channel:name] prefix in the subject.

Closes: gt-3rldf6

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
mel
2026-01-17 03:44:01 -08:00
committed by Steve Yegge
parent f14dadc956
commit e853ac3539

View File

@@ -804,6 +804,7 @@ func (r *Router) sendToAnnounce(msg *Message) error {
// sendToChannel delivers a message to a beads-native channel.
// Creates a message with channel:<name> label for channel queries.
// Also fans out delivery to each subscriber's inbox.
// Retention is enforced by the channel's EnforceChannelRetention after message creation.
func (r *Router) sendToChannel(msg *Message) error {
channelName := parseChannelName(msg.To)
@@ -872,7 +873,23 @@ func (r *Router) sendToChannel(msg *Message) error {
// Enforce channel retention policy (on-write cleanup)
_ = b.EnforceChannelRetention(channelName)
// No notification for channel messages - readers poll or check on their own schedule
// Fan-out delivery: send a copy to each subscriber's inbox
if len(fields.Subscribers) > 0 {
for _, subscriber := range fields.Subscribers {
// Skip self-delivery (don't notify the sender)
if isSelfMail(msg.From, subscriber) {
continue
}
// Create a copy for this subscriber with channel context in subject
msgCopy := *msg
msgCopy.To = subscriber
msgCopy.Subject = fmt.Sprintf("[channel:%s] %s", channelName, msg.Subject)
// Best-effort delivery - don't fail the channel send if one subscriber fails
_ = r.sendToSingle(&msgCopy)
}
}
return nil
}