feat: mail uses wisps for ephemeral orchestration messages (gt-lg66)

Add dual-inbox architecture where ephemeral messages go to
.beads-wisp/.beads/ instead of .beads/. Lifecycle messages
(POLECAT_STARTED, NUDGE, etc.) auto-detect as ephemeral.

Changes:
- Add Ephemeral and Source fields to mail.Message
- Add --ephemeral flag to gt mail send
- Router auto-detects lifecycle message patterns
- Mailbox merges messages from both persistent and wisp storage
- Inbox displays (ephemeral) indicator for wisp messages
- Delete/archive works for both message types

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Steve Yegge
2025-12-24 20:03:37 -08:00
parent 9d1ebfc54d
commit 200a09a02d
5 changed files with 166 additions and 19 deletions

View File

@@ -28,6 +28,17 @@ const (
// MessageType indicates the purpose of a message.
type MessageType string
// MessageSource indicates where a message is stored.
type MessageSource string
const (
// SourcePersistent indicates the message is in permanent .beads storage.
SourcePersistent MessageSource = "persistent"
// SourceWisp indicates the message is in ephemeral .beads-wisp storage.
SourceWisp MessageSource = "wisp"
)
const (
// TypeTask indicates a message requiring action from the recipient.
TypeTask MessageType = "task"
@@ -97,6 +108,14 @@ type Message struct {
// Pinned marks the message as pinned (won't be auto-archived).
Pinned bool `json:"pinned,omitempty"`
// Ephemeral marks this as a transient message stored in wisps.
// Ephemeral messages auto-cleanup on patrol squash.
Ephemeral bool `json:"ephemeral,omitempty"`
// Source indicates where this message is stored (persistent or wisp).
// Set during List(), not serialized.
Source MessageSource `json:"-"`
}
// NewMessage creates a new message with a generated ID and thread ID.