fix(mail): prevent message type failures (#960)

This commit is contained in:
Artem Bambalov
2026-01-26 04:05:11 +02:00
committed by GitHub
parent 0ff092ae9f
commit 0e0547b3e1
3 changed files with 35 additions and 12 deletions

View File

@@ -11,9 +11,9 @@ import (
"github.com/spf13/cobra"
"github.com/steveyegge/gastown/internal/beads"
"github.com/steveyegge/gastown/internal/constants"
"github.com/steveyegge/gastown/internal/claude"
"github.com/steveyegge/gastown/internal/config"
"github.com/steveyegge/gastown/internal/constants"
"github.com/steveyegge/gastown/internal/deps"
"github.com/steveyegge/gastown/internal/formula"
"github.com/steveyegge/gastown/internal/shell"
@@ -408,11 +408,8 @@ func initTownBeads(townPath string) error {
// Configure custom types for Gas Town (agent, role, rig, convoy, slot).
// These were extracted from beads core in v0.46.0 and now require explicit config.
configCmd := exec.Command("bd", "config", "set", "types.custom", constants.BeadsCustomTypes)
configCmd.Dir = townPath
if configOutput, configErr := configCmd.CombinedOutput(); configErr != nil {
// Non-fatal: older beads versions don't need this, newer ones do
fmt.Printf(" %s Could not set custom types: %s\n", style.Dim.Render("⚠"), strings.TrimSpace(string(configOutput)))
if err := beads.EnsureCustomTypes(beadsDir); err != nil {
return fmt.Errorf("ensuring custom types: %w", err)
}
// Configure allowed_prefixes for convoy beads (hq-cv-* IDs).

View File

@@ -212,6 +212,10 @@ func (m *Mailbox) identityVariants() []string {
// queryMessages runs a bd list query with the given filter flag and value.
func (m *Mailbox) queryMessages(beadsDir, filterFlag, filterValue, status string) ([]*Message, error) {
if err := beads.EnsureCustomTypes(beadsDir); err != nil {
return nil, fmt.Errorf("ensuring custom types: %w", err)
}
args := []string{"list",
"--type", "message",
filterFlag, filterValue,
@@ -829,8 +833,8 @@ func (m *Mailbox) rewriteLegacy(messages []*Message) error {
for _, msg := range messages {
data, err := json.Marshal(msg)
if err != nil {
_ = file.Close() // best-effort cleanup
_ = os.Remove(tmpPath) // best-effort cleanup
_ = file.Close() // best-effort cleanup
_ = os.Remove(tmpPath) // best-effort cleanup
return err
}
_, _ = file.WriteString(string(data) + "\n") // non-fatal: partial write is acceptable

View File

@@ -189,6 +189,13 @@ func (r *Router) resolveBeadsDir(_ string) string { // address unused: all mail
return filepath.Join(r.townRoot, ".beads")
}
func (r *Router) ensureCustomTypes(beadsDir string) error {
if err := beads.EnsureCustomTypes(beadsDir); err != nil {
return fmt.Errorf("ensuring custom types: %w", err)
}
return nil
}
// isTownLevelAddress returns true if the address is for a town-level agent or the overseer.
func isTownLevelAddress(address string) bool {
addr := strings.TrimSuffix(address, "/")
@@ -214,10 +221,10 @@ const (
// ParsedGroup represents a parsed @group address.
type ParsedGroup struct {
Type GroupType
RoleType string // witness, crew, polecat, dog, etc.
Rig string // rig name for rig-scoped groups
Original string // original @group string
Type GroupType
RoleType string // witness, crew, polecat, dog, etc.
Rig string // rig name for rig-scoped groups
Original string // original @group string
}
// parseGroupAddress parses a @group address into its components.
@@ -697,6 +704,9 @@ func (r *Router) sendToSingle(msg *Message) error {
}
beadsDir := r.resolveBeadsDir(msg.To)
if err := r.ensureCustomTypes(beadsDir); err != nil {
return err
}
_, err := runBdCommand(args, filepath.Dir(beadsDir), beadsDir)
if err != nil {
return fmt.Errorf("sending message: %w", err)
@@ -807,6 +817,9 @@ func (r *Router) sendToQueue(msg *Message) error {
// Queue messages go to town-level beads (shared location)
beadsDir := r.resolveBeadsDir("")
if err := r.ensureCustomTypes(beadsDir); err != nil {
return err
}
_, err = runBdCommand(args, filepath.Dir(beadsDir), beadsDir)
if err != nil {
return fmt.Errorf("sending to queue %s: %w", queueName, err)
@@ -878,6 +891,9 @@ func (r *Router) sendToAnnounce(msg *Message) error {
// Announce messages go to town-level beads (shared location)
beadsDir := r.resolveBeadsDir("")
if err := r.ensureCustomTypes(beadsDir); err != nil {
return err
}
_, err = runBdCommand(args, filepath.Dir(beadsDir), beadsDir)
if err != nil {
return fmt.Errorf("sending to announce %s: %w", announceName, err)
@@ -951,6 +967,9 @@ func (r *Router) sendToChannel(msg *Message) error {
// Channel messages go to town-level beads (shared location)
beadsDir := r.resolveBeadsDir("")
if err := r.ensureCustomTypes(beadsDir); err != nil {
return err
}
_, err = runBdCommand(args, filepath.Dir(beadsDir), beadsDir)
if err != nil {
return fmt.Errorf("sending to channel %s: %w", channelName, err)
@@ -988,6 +1007,9 @@ func (r *Router) pruneAnnounce(announceName string, retainCount int) error {
}
beadsDir := r.resolveBeadsDir("")
if err := r.ensureCustomTypes(beadsDir); err != nil {
return err
}
// Query existing messages in this announce channel
// Use bd list with labels filter to find messages with announce:<name> label