# Spec: Activity Log, Undo & Activity Summaries A unified, trustworthy record of everything automation did — and a way to take it back. Built entirely on the `AutomationAction` table from `feature-rules-engine.md`; no new storage. ## 1. Activity Log - Backed by `AutomationAction` rows with `Status in (Applied, Rejected, Undone, Failed)`. - `IAutomationActionService.GetActivityAsync(userId, take)` returns `ActivityLogEntryDto`, newest first, grouped where it reads naturally (e.g. "Archived 38 emails from LinkedIn — Rule: Social noise"). - Each entry exposes `CanUndo`: - Safe actions (Archive/SkipInbox/ApplyLabel/MarkRead/Star) — always undoable while we still hold `UndoStateJson` and the message exists. - Trash — undoable (Gmail untrash) within Gmail's 30-day window. - Hard delete — N/A (never automated). ### UI (`/app/activity`) - Reverse-chronological feed with source chips (Rule / Policy / Screener / Age sweep), action icon, affected count, timestamp, and an **Undo** button where `CanUndo`. - Filter by source and action type. Date range. Search by sender. ## 2. Undo `UndoAsync(userId, actionIds)`: 1. Load the `Applied` actions (verify `UserId`). 2. For each, parse `UndoStateJson` (captured pre-apply: which labels were present, whether `INBOX` was set, whether it was in Trash). 3. Issue the inverse `BatchModifyAsync` / `BatchUntrash` to restore prior state. 4. Set `Status = Undone`, stamp `AppliedUtc = now` on the undo. 5. Log is append-only in spirit: the original row flips to `Undone` rather than being deleted. `UndoStateJson` shape (kept tiny): ```json { "hadInbox": true, "labels": ["Label_12","Label_88"], "wasTrashed": false } ``` Captured by the engine/approval step immediately before mutating. ## 3. Activity Summaries (extends existing digest) Clean.Email's "Activity Summaries" = periodic notification of what automation did. We already have the SMTP digest infra (`IDigestService`, `DigestWorker`, `User.DigestEnabled`). Extend rather than add: - `DigestService.BuildHtml` gains an **"Automation activity since last digest"** section: counts of archived/labeled/screened, pending-approval count (with a nudge to review), top rules by volume, new screener senders. - Pull from `AutomationAction` where `AppliedUtc > user.LastDigestSentUtc`. - No new toggle — folds into the existing digest opt-in. (Optional later: a separate `User.ActivitySummaryEnabled` if users want activity summaries without the analytics digest.) ## 4. Cleanup Reminders A lightweight nudge when the inbox needs attention, reusing `DigestWorker`'s tick: - If a user has **pending destructive approvals** older than `ReminderAfterDays` (default 3) and digests are on, the next digest leads with "You have N actions awaiting approval." - If automation is **off** but the dashboard health score is poor / unsubscribe backlog is large, include a "Time to clean up" prompt with a deep link. No new infrastructure — just content rules inside `DigestService`. ## 5. API Covered by `feature-rules-engine.md` §7: `GET /automation/activity`, `POST /automation/activity/undo`. Add query params for filtering: `?source=&action=&from=&to=&q=`. ## 6. Security & safety - [ ] Activity + undo scoped to `UserId`; action ownership re-checked on undo. - [ ] Undo is best-effort and idempotent — undoing an already-undone/missing message logs and no-ops rather than erroring. - [ ] Log never exposes another user's senders/emails.