Skip to content

Commit a7d292e

Browse files
committed
fix(mail): fall back to home DB when cross-rig bead ID not found in routed dir
Mail messages are always created in town beads (via the mail router), but their IDs may have rig-prefixes (e.g. ne-*) when the creating agent is running from within a rig. ResolveBeadsDirForID routes ne-* to the rig database, but the message actually lives in the town (HQ) database. When bd close/label/reopen returns 'not found' for the routed dir, fall back to m.beadsDir (the home database) before returning ErrMessageNotFound. This applies to all five beads-mode operations: getBeads, markReadBeads, markReadOnlyBeads, markUnreadOnlyBeads, and markUnreadBeads. Fixes: gt mail archive failing for cross-rig HANDOFF messages (ne-bgr)
1 parent 85816bc commit a7d292e

1 file changed

Lines changed: 66 additions & 5 deletions

File tree

internal/mail/mailbox.go

Lines changed: 66 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -454,7 +454,15 @@ func (m *Mailbox) Get(id string) (*Message, error) {
454454

455455
func (m *Mailbox) getBeads(id string) (*Message, error) {
456456
// Resolve correct beadsDir based on bead ID prefix (GH#2423)
457-
return m.getFromDir(id, beads.ResolveBeadsDirForID(m.beadsDir, id))
457+
primary := beads.ResolveBeadsDirForID(m.beadsDir, id)
458+
msg, err := m.getFromDir(id, primary)
459+
if errors.Is(err, ErrMessageNotFound) && primary != m.beadsDir {
460+
// Cross-rig bead IDs (e.g. ne-*) may live in the home DB when created
461+
// via the mail router (which always uses town beads). Fall back to
462+
// m.beadsDir before giving up. See ne-bgr.
463+
return m.getFromDir(id, m.beadsDir)
464+
}
465+
return msg, err
458466
}
459467

460468
// getFromDir retrieves a message from a beads directory.
@@ -510,7 +518,15 @@ func (m *Mailbox) MarkRead(id string) error {
510518

511519
func (m *Mailbox) markReadBeads(id string) error {
512520
// Resolve correct beadsDir based on bead ID prefix (GH#2423)
513-
return m.closeInDir(id, beads.ResolveBeadsDirForID(m.beadsDir, id))
521+
primary := beads.ResolveBeadsDirForID(m.beadsDir, id)
522+
err := m.closeInDir(id, primary)
523+
if errors.Is(err, ErrMessageNotFound) && primary != m.beadsDir {
524+
// Cross-rig bead IDs (e.g. ne-*) may live in the home DB when created
525+
// via the mail router (which always uses town beads). Fall back to
526+
// m.beadsDir before giving up. See ne-bgr.
527+
return m.closeInDir(id, m.beadsDir)
528+
}
529+
return err
514530
}
515531

516532
// closeInDir closes a message in a specific beads directory.
@@ -579,12 +595,26 @@ func (m *Mailbox) MarkReadOnly(id string) error {
579595
func (m *Mailbox) markReadOnlyBeads(id string) error {
580596
// Add "read" label to mark as read without closing
581597
args := []string{"label", "add", id, "read"}
598+
primary := beads.ResolveBeadsDirForID(m.beadsDir, id)
582599

583600
ctx, cancel := bdWriteCtx()
584601
defer cancel()
585-
_, err := runBdCommand(ctx, args, m.workDir, beads.ResolveBeadsDirForID(m.beadsDir, id))
602+
_, err := runBdCommand(ctx, args, m.workDir, primary)
586603
if err != nil {
587604
if bdErr, ok := err.(*bdError); ok && bdErr.ContainsError("not found") {
605+
if primary != m.beadsDir {
606+
// Cross-rig bead IDs (e.g. ne-*) may live in the home DB. See ne-bgr.
607+
ctx2, cancel2 := bdWriteCtx()
608+
defer cancel2()
609+
_, err2 := runBdCommand(ctx2, args, m.workDir, m.beadsDir)
610+
if err2 != nil {
611+
if bdErr2, ok := err2.(*bdError); ok && bdErr2.ContainsError("not found") {
612+
return ErrMessageNotFound
613+
}
614+
return err2
615+
}
616+
return nil
617+
}
588618
return ErrMessageNotFound
589619
}
590620
return err
@@ -606,12 +636,29 @@ func (m *Mailbox) MarkUnreadOnly(id string) error {
606636
func (m *Mailbox) markUnreadOnlyBeads(id string) error {
607637
// Remove "read" label to mark as unread
608638
args := []string{"label", "remove", id, "read"}
639+
primary := beads.ResolveBeadsDirForID(m.beadsDir, id)
609640

610641
ctx, cancel := bdWriteCtx()
611642
defer cancel()
612-
_, err := runBdCommand(ctx, args, m.workDir, beads.ResolveBeadsDirForID(m.beadsDir, id))
643+
_, err := runBdCommand(ctx, args, m.workDir, primary)
613644
if err != nil {
614645
if bdErr, ok := err.(*bdError); ok && bdErr.ContainsError("not found") {
646+
if primary != m.beadsDir {
647+
// Cross-rig bead IDs (e.g. ne-*) may live in the home DB. See ne-bgr.
648+
ctx2, cancel2 := bdWriteCtx()
649+
defer cancel2()
650+
_, err2 := runBdCommand(ctx2, args, m.workDir, m.beadsDir)
651+
if err2 != nil {
652+
if bdErr2, ok := err2.(*bdError); ok && bdErr2.ContainsError("not found") {
653+
return ErrMessageNotFound
654+
}
655+
if bdErr2, ok := err2.(*bdError); ok && bdErr2.ContainsError("does not have label") {
656+
return nil
657+
}
658+
return err2
659+
}
660+
return nil
661+
}
615662
return ErrMessageNotFound
616663
}
617664
// Ignore error if label doesn't exist
@@ -634,12 +681,26 @@ func (m *Mailbox) MarkUnread(id string) error {
634681

635682
func (m *Mailbox) markUnreadBeads(id string) error {
636683
args := []string{"reopen", id}
684+
primary := beads.ResolveBeadsDirForID(m.beadsDir, id)
637685

638686
ctx, cancel := bdWriteCtx()
639687
defer cancel()
640-
_, err := runBdCommand(ctx, args, m.workDir, beads.ResolveBeadsDirForID(m.beadsDir, id))
688+
_, err := runBdCommand(ctx, args, m.workDir, primary)
641689
if err != nil {
642690
if bdErr, ok := err.(*bdError); ok && bdErr.ContainsError("not found") {
691+
if primary != m.beadsDir {
692+
// Cross-rig bead IDs (e.g. ne-*) may live in the home DB. See ne-bgr.
693+
ctx2, cancel2 := bdWriteCtx()
694+
defer cancel2()
695+
_, err2 := runBdCommand(ctx2, args, m.workDir, m.beadsDir)
696+
if err2 != nil {
697+
if bdErr2, ok := err2.(*bdError); ok && bdErr2.ContainsError("not found") {
698+
return ErrMessageNotFound
699+
}
700+
return err2
701+
}
702+
return nil
703+
}
643704
return ErrMessageNotFound
644705
}
645706
return err

0 commit comments

Comments
 (0)