fix: reorder ingress checks and update requirements.md
CI / fmt (pull_request) Successful in 4m43s
CI / test (pull_request) Failing after 21m37s

- 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 <noreply@anthropic.com>
This commit is contained in:
2026-04-28 06:53:05 +07:00
co-authored by Claude Opus 4.7
parent 92121278bb
commit 562d3b01c3
2 changed files with 18 additions and 16 deletions
+13 -13
View File
@@ -356,7 +356,19 @@ func (w *IMAPWatcher) processMessage(c *imapclient.Client, buf *imapclient.Fetch
return 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{ ruleResult := w.rules.Evaluate(rules.EmailContext{
Sender: sender, Sender: sender,
Subject: subject, Subject: subject,
@@ -369,18 +381,6 @@ func (w *IMAPWatcher) processMessage(c *imapclient.Client, buf *imapclient.Fetch
return 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. // 4. Threading: determine thread_id.
threadID := messageID threadID := messageID
inReplyTo := "" inReplyTo := ""
+5 -3
View File
@@ -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. - 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: - Khi có email mới:
1. Anti-loop: nếu `sender` trùng email hệ thống -> `IGNORED`. 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`. 2. Idempotency: nếu `message_id` đã tồn tại -> bỏ qua.
3. 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: 4. Threading:
- Nếu có `In-Reply-To`/`References`: tìm email cha trong DB, kế thừa `thread_id`. - 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`. - 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) 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`). - 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) - `history` (optional)
- `callback_url` - `callback_url`
- `metadata.task_uuid` - `metadata.task_uuid`
- `metadata.*` từ `dispatch_context` (rule-derived context)
- Cập nhật `status = AI_PROCESSING`. - Cập nhật `status = AI_PROCESSING`.
Retry OpenClaw: Retry OpenClaw:
@@ -146,6 +147,7 @@ Required:
- `OPENCLAW_URL`, `OPENCLAW_API_KEY` (nếu dùng) - `OPENCLAW_URL`, `OPENCLAW_API_KEY` (nếu dùng)
- `BRIDGE_CALLBACK_TOKEN` - `BRIDGE_CALLBACK_TOKEN`
- `WHITELIST_EMAILS` (comma-separated) - `WHITELIST_EMAILS` (comma-separated)
- `BLOCKLIST_EMAILS` (comma-separated, optional)
- `SYSTEM_EMAIL` - `SYSTEM_EMAIL`
- `IMAP_PROXY_URL` (optional) - `IMAP_PROXY_URL` (optional)