···11# Changelog
2233+# 2026-04-21
44+- **Fix: reply-all excludes all own addresses** — `ctrl+r` reply-all now excludes all configured email addresses (accounts + sender aliases) from the CC field; previously only excluded the active account's address, causing your own email to appear in CC when the original email was sent to one of your other addresses
55+36# 2026-04-18
47- **Headless daemon mode (`--headless`)** — run neomd as a background daemon on a server (NAS, VPS, always-on device) to continuously screen emails without launching the TUI; daemon fetches inbox and auto-screens every `bg_sync_interval` minutes (configurable in config); watches screener list files (`~/.config/neomd/lists/*.txt`) and reloads when they change (designed for Syncthing multi-device sync); graceful shutdown on SIGTERM/SIGINT; structured logging to stdout with `log/slog`; use case: run daemon on NAS with `bg_sync_interval = 5`, disable background sync on laptop/Android with `bg_sync_interval = 0`, classify senders in TUI, Syncthing syncs lists to NAS, daemon auto-screens incoming emails so mobile IMAP apps see correctly filtered folders; perfect for using neomd's screener with native mobile email clients; daemon only reads screener lists and moves emails (never modifies lists), all sender classification happens in TUI; includes comprehensive tests for classification logic and daemon lifecycle; documented in `docs/configurations/headless.md` with Syncthing setup guide, systemd service example, and multi-device workflow; `make daemon` target for testing
58- **Fix: headless screening paused when lists empty** — daemon now checks `screener.IsEmpty()` and skips screening when all lists are empty, mirroring TUI behavior; prevents sweeping entire inbox to ToScreen on first run or before Syncthing completes initial sync; logs "screening paused: screener lists are empty (classify your first sender to activate)" until first classification exists; regression test added
+10-4
internal/ui/model.go
···3884388438853885 cc := ""
38863886 if replyAll {
38873887- // Collect original To + CC, exclude own address
38883888- own := strings.ToLower(extractEmailAddr(m.activeAccount().User))
38873887+ // Collect original To + CC, exclude all own addresses
38883888+ ownAddrs := make(map[string]bool)
38893889+ for _, from := range m.presendFroms() {
38903890+ ownAddrs[strings.ToLower(extractEmailAddr(from))] = true
38913891+ }
38893892 var parts []string
38903893 for _, addr := range splitAddrs(e.To + "," + e.CC) {
38913891- if a := strings.TrimSpace(addr); a != "" && strings.ToLower(extractEmailAddr(a)) != own {
38923892- parts = append(parts, a)
38943894+ if a := strings.TrimSpace(addr); a != "" {
38953895+ addrLower := strings.ToLower(extractEmailAddr(a))
38963896+ if !ownAddrs[addrLower] {
38973897+ parts = append(parts, a)
38983898+ }
38933899 }
38943900 }
38953901 cc = strings.Join(parts, ", ")