Clean.Email-parity feature build-out plus a Stripe/Notion-style UI rebuild on Tailwind + shadcn-style primitives. Locks the hybrid-automation, Gmail-only, light+dark, incremental-rollout decisions and lays out the backend/frontend build sequence. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3.4 KiB
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
AutomationActionrows withStatus in (Applied, Rejected, Undone, Failed). IAutomationActionService.GetActivityAsync(userId, take)returnsActivityLogEntryDto, 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
UndoStateJsonand the message exists. - Trash — undoable (Gmail untrash) within Gmail's 30-day window.
- Hard delete — N/A (never automated).
- Safe actions (Archive/SkipInbox/ApplyLabel/MarkRead/Star) — always undoable while we
still hold
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):
- Load the
Appliedactions (verifyUserId). - For each, parse
UndoStateJson(captured pre-apply: which labels were present, whetherINBOXwas set, whether it was in Trash). - Issue the inverse
BatchModifyAsync/BatchUntrashto restore prior state. - Set
Status = Undone, stampAppliedUtc = nowon the undo. - Log is append-only in spirit: the original row flips to
Undonerather than being deleted.
UndoStateJson shape (kept tiny):
{ "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.BuildHtmlgains 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
AutomationActionwhereAppliedUtc > user.LastDigestSentUtc. - No new toggle — folds into the existing digest opt-in. (Optional later: a separate
User.ActivitySummaryEnabledif 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.