Add nudge_channels to MessagingConfig schema (gt-3shmx)
- Add NudgeChannels field to MessagingConfig struct in types.go - Initialize NudgeChannels map in NewMessagingConfig() - Add validation in validateMessagingConfig(): channel names must be non-empty and each channel must have at least one recipient - Add tests for valid nudge channels and empty recipient validation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
committed by
Steve Yegge
parent
d28ba6e2c6
commit
d94fb4669b
@@ -629,6 +629,9 @@ func validateMessagingConfig(c *MessagingConfig) error {
|
||||
if c.Announces == nil {
|
||||
c.Announces = make(map[string]AnnounceConfig)
|
||||
}
|
||||
if c.NudgeChannels == nil {
|
||||
c.NudgeChannels = make(map[string][]string)
|
||||
}
|
||||
|
||||
// Validate lists have at least one recipient
|
||||
for name, recipients := range c.Lists {
|
||||
@@ -657,6 +660,16 @@ func validateMessagingConfig(c *MessagingConfig) error {
|
||||
}
|
||||
}
|
||||
|
||||
// Validate nudge channels have non-empty names and at least one recipient
|
||||
for name, recipients := range c.NudgeChannels {
|
||||
if name == "" {
|
||||
return fmt.Errorf("%w: nudge channel name cannot be empty", ErrMissingField)
|
||||
}
|
||||
if len(recipients) == 0 {
|
||||
return fmt.Errorf("%w: nudge channel '%s' has no recipients", ErrMissingField, name)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -566,6 +566,8 @@ func TestMessagingConfigRoundTrip(t *testing.T) {
|
||||
Readers: []string{"@town"},
|
||||
RetainCount: 100,
|
||||
}
|
||||
original.NudgeChannels["workers"] = []string{"gastown/polecats/*", "gastown/crew/*"}
|
||||
original.NudgeChannels["witnesses"] = []string{"*/witness"}
|
||||
|
||||
if err := SaveMessagingConfig(path, original); err != nil {
|
||||
t.Fatalf("SaveMessagingConfig: %v", err)
|
||||
@@ -606,6 +608,17 @@ func TestMessagingConfigRoundTrip(t *testing.T) {
|
||||
if a, ok := loaded.Announces["alerts"]; !ok || a.RetainCount != 100 {
|
||||
t.Error("announce not preserved")
|
||||
}
|
||||
|
||||
// Check nudge channels
|
||||
if len(loaded.NudgeChannels) != 2 {
|
||||
t.Errorf("NudgeChannels count = %d, want 2", len(loaded.NudgeChannels))
|
||||
}
|
||||
if workers, ok := loaded.NudgeChannels["workers"]; !ok || len(workers) != 2 {
|
||||
t.Error("workers nudge channel not preserved")
|
||||
}
|
||||
if witnesses, ok := loaded.NudgeChannels["witnesses"]; !ok || len(witnesses) != 1 {
|
||||
t.Error("witnesses nudge channel not preserved")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMessagingConfigValidation(t *testing.T) {
|
||||
@@ -696,6 +709,27 @@ func TestMessagingConfigValidation(t *testing.T) {
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "valid config with nudge channels",
|
||||
config: &MessagingConfig{
|
||||
Type: "messaging",
|
||||
Version: 1,
|
||||
NudgeChannels: map[string][]string{
|
||||
"workers": {"gastown/polecats/*", "gastown/crew/*"},
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "nudge channel with no recipients",
|
||||
config: &MessagingConfig{
|
||||
Version: 1,
|
||||
NudgeChannels: map[string][]string{
|
||||
"empty": {},
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
|
||||
@@ -278,6 +278,11 @@ type MessagingConfig struct {
|
||||
// Used for broadcast announcements that don't need acknowledgment.
|
||||
// Example: {"alerts": {"readers": ["@town"]}}
|
||||
Announces map[string]AnnounceConfig `json:"announces,omitempty"`
|
||||
|
||||
// NudgeChannels are named groups for real-time nudge fan-out.
|
||||
// Like mailing lists but for tmux send-keys instead of durable mail.
|
||||
// Example: {"workers": ["gastown/polecats/*", "gastown/crew/*"], "witnesses": ["*/witness"]}
|
||||
NudgeChannels map[string][]string `json:"nudge_channels,omitempty"`
|
||||
}
|
||||
|
||||
// QueueConfig represents a work queue configuration.
|
||||
@@ -306,10 +311,11 @@ const CurrentMessagingVersion = 1
|
||||
// NewMessagingConfig creates a new MessagingConfig with defaults.
|
||||
func NewMessagingConfig() *MessagingConfig {
|
||||
return &MessagingConfig{
|
||||
Type: "messaging",
|
||||
Version: CurrentMessagingVersion,
|
||||
Lists: make(map[string][]string),
|
||||
Queues: make(map[string]QueueConfig),
|
||||
Announces: make(map[string]AnnounceConfig),
|
||||
Type: "messaging",
|
||||
Version: CurrentMessagingVersion,
|
||||
Lists: make(map[string][]string),
|
||||
Queues: make(map[string]QueueConfig),
|
||||
Announces: make(map[string]AnnounceConfig),
|
||||
NudgeChannels: make(map[string][]string),
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user