diff --git a/CHANGELOG.md b/CHANGELOG.md index 0348138..9863820 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ # Changelog # 2026-05-08 +- **`Ms` move to Sent** — added `Ms` chord to move the cursor or marked email(s) to the configured Sent folder, mirroring the `gs` (go to Sent) shortcut. Previously every other major folder had a matching `M*` mover (`Mi`/`Ma`/`Mt`/…) but Sent was missing, so manually filing already-sent correspondence forwarded from another client required `:` commands or a multi-step copy. Lives in the same `M`-prefix dstMap as the rest, no behaviour change for other letters +- **`/` filter searches recipients in Sent folder** — the in-memory inbox filter (`/`) previously matched on `From + Subject` in every folder. In Sent that meant typing `/hussain` after sending Hussain an email returned nothing, because `From` is always you. The filter now matches on `To + CC + BCC + Subject` whenever the active folder is the configured Sent folder; all other folders are unchanged. The server-side IMAP search (`/` / `:search`) already searches To and is unaffected - Review and stability - **Concurrency: data race in spy pixel cache save eliminated** — `bodyLoadedMsg` and `spyScanProgressMsg` previously launched `safeGo(func() { saveSpyPixelCache(copyMap(m.spyPixelKeys), copyMap(m.spyScannedKeys)) })`, evaluating `copyMap` *inside* the goroutine — the closure captured the model and read the live maps while the main goroutine continued mutating them on the next message. Both call sites now snapshot the maps on the main goroutine before launching the writer, so `copyMap` cannot race with subsequent map writes. The `// Takes copied maps to avoid concurrent access` comment on `saveSpyPixelCache` was true of the helper signature but the call sites violated the contract; both are now correct - **Concurrency: `Screener` is now safe for concurrent use** — added `sync.RWMutex` guarding every map (`screenedIn`, `screenedOut`, `feed`, `paperTrail`, `spam`, `notify`). Auto-screen, background sync, search, notify, and TUI mutation paths all share one `*Screener`, so concurrent map writes would have panicked at runtime. `Classify`, `ClassifyDebug`, `IsEmpty`, `AllAddresses`, `ShouldNotify`, and `Snapshot` take a read lock; `Approve`, `Block`, `MarkSpam`, `MarkFeed`, `MarkPaperTrail`, `AddNotify`, `RemoveNotify`, and `Restore` take a write lock. Refactored `Snapshot`/`Restore` into public-locked + private `snapshotLocked`/`restoreLocked` variants so the existing transactional rollback inside `Approve`/`Block`/etc. doesn't double-lock. RWMutex keeps the hot read path parallel; `make test -race` clean