Resolves the sync.rs conflict between M3 (real JmapEngine::with_credentials
wiring, wiremock-backed sync tests) and M4 (FilterEngine threaded through
sync, apply_filter_actions, is_new_message fire-once guard): keeps dev's
JMAP wiremock stub counts/assertions and this branch's FilterEngine
parameters and new filter E2E tests side by side.
Sync is not incremental yet, so every re-sync re-fetches and
re-persists messages the backend still reports. Applying filter
actions unconditionally on every persisted message meant a rule kept
re-firing on every sync cycle for a message that arrived once,
enqueueing a fresh outbox mutation and execution log entry each time.
Guard fetch_and_persist so it only routes a message through
apply_filter_actions when the message did not already exist in the
store before this sync pass (DatabaseError::is_not_found, checked
before save_email's INSERT OR REPLACE erases the distinction). Also
makes the immediate-action match in apply_filter_actions exhaustive
over the remote action variants instead of a wildcard, so a future
immediate-style action can't silently fall through to remote routing.
Every synced message is now evaluated against the daemon's live
FilterEngine after it is persisted. MARK READ/UNREAD apply immediately
via set_message_read; MOVE/COPY/FLAG/UNFLAG/DELETE/FORWARD/CALL WEBHOOK
enqueue a real PendingRemoteMutation through the existing outbox for
the background worker to drain. Every match appends a
FilterExecutionLog entry, so a rule match now produces a genuine,
persisted, observable side effect instead of nothing.
The evaluate-and-route logic lives in a standalone apply_filter_actions
helper (not inlined into fetch_and_persist) so other sync paths can
reuse the exact same routing instead of re-deriving it.