From 562d3b01c393eb399d08efde9adeff002fcfc2ff Mon Sep 17 00:00:00 2001 From: thuanle Date: Tue, 28 Apr 2026 06:53:05 +0700 Subject: [PATCH] fix: reorder ingress checks and update requirements.md - Move idempotency before external rules per ADR-0002 - Update requirements.md: new pipeline flow, BLOCKLIST_EMAILS, dispatch_context metadata Co-Authored-By: Claude Opus 4.7 --- internal/mail/imap.go | 26 +++++++++++++------------- requirements.md | 8 +++++--- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/internal/mail/imap.go b/internal/mail/imap.go index da22273..ab9bd12 100644 --- a/internal/mail/imap.go +++ b/internal/mail/imap.go @@ -356,7 +356,19 @@ func (w *IMAPWatcher) processMessage(c *imapclient.Client, buf *imapclient.Fetch return } - // 2. External rules: evaluate operator-configurable policy. + // 2. Idempotency: skip if message_id already exists. + existing, err := database.FindByMessageID(w.db, messageID) + if err != nil { + log.Error("imap: db lookup failed", "error", err) + return + } + if existing != nil { + log.Info("imap: duplicate message_id, skipping") + w.markSeen(c, buf.UID) + return + } + + // 3. External rules: evaluate operator-configurable policy. ruleResult := w.rules.Evaluate(rules.EmailContext{ Sender: sender, Subject: subject, @@ -369,18 +381,6 @@ func (w *IMAPWatcher) processMessage(c *imapclient.Client, buf *imapclient.Fetch return } - // 3. Idempotency: skip if message_id already exists. - existing, err := database.FindByMessageID(w.db, messageID) - if err != nil { - log.Error("imap: db lookup failed", "error", err) - return - } - if existing != nil { - log.Info("imap: duplicate message_id, skipping") - w.markSeen(c, buf.UID) - return - } - // 4. Threading: determine thread_id. threadID := messageID inReplyTo := "" diff --git a/requirements.md b/requirements.md index 9ea3f7c..26782fe 100644 --- a/requirements.md +++ b/requirements.md @@ -68,12 +68,12 @@ Danh sách trạng thái hợp lệ: - Nếu có `IMAP_PROXY_URL` thì kết nối IMAP qua proxy; nếu không thì direct. - Khi có email mới: 1. Anti-loop: nếu `sender` trùng email hệ thống -> `IGNORED`. - 2. Whitelist: nếu không nằm trong danh sách cho phép -> `IGNORED`. - 3. Idempotency: nếu `message_id` đã tồn tại -> bỏ qua. + 2. Idempotency: nếu `message_id` đã tồn tại -> bỏ qua. + 3. External rules: chạy pipeline các rule configurable (whitelist, blocklist, v.v.). Nếu reject -> `IGNORED`. Rule metadata được lưu vào `dispatch_context`. 4. Threading: - Nếu có `In-Reply-To`/`References`: tìm email cha trong DB, kế thừa `thread_id`. - Nếu không có: `thread_id = message_id`. - 5. Lưu DB với `status = RECEIVED`. + 5. Lưu DB với `status = RECEIVED`, bao gồm `dispatch_context` từ rules. 4.2 Dispatch (OpenClaw) - Lấy nội dung email hiện tại và history của cùng `thread_id` (chỉ các bản ghi `COMPLETED`). @@ -84,6 +84,7 @@ Danh sách trạng thái hợp lệ: - `history` (optional) - `callback_url` - `metadata.task_uuid` + - `metadata.*` từ `dispatch_context` (rule-derived context) - Cập nhật `status = AI_PROCESSING`. Retry OpenClaw: @@ -146,6 +147,7 @@ Required: - `OPENCLAW_URL`, `OPENCLAW_API_KEY` (nếu dùng) - `BRIDGE_CALLBACK_TOKEN` - `WHITELIST_EMAILS` (comma-separated) +- `BLOCKLIST_EMAILS` (comma-separated, optional) - `SYSTEM_EMAIL` - `IMAP_PROXY_URL` (optional)