Antispam: never classify during backfill
message.new fires for imported history as well as new arrivals, and EventBus awaits hooks inline. As written, adding an account would have scored every message in its backfill — filing years-old inbox mail into Junk on the server, and adding ~70 ms of serialized HTTP per message to the backfill loop (twelve minutes on a 10k-message account). It is also when the filter is least able to judge, because Bayes has learned nothing yet. The envelope now carries account_state, and the hook returns unless it is "live". The backfill window belongs to the C2 learning bootstrap (§C.5), which reads that history as training data instead of acting on it. Tests pin the gating: backfilling accounts are not even read from the blob store, non-inbox folders are ignored, a live inbox arrival still classifies, and an unreachable engine still delivers. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018SqJYt7jD7oUEkoY68sBZo
This commit is contained in:
@@ -23,7 +23,7 @@ from sqlalchemy import select
|
||||
from app.db import session_scope
|
||||
from app.events import get_event_bus
|
||||
from app.indexer import blobstore
|
||||
from app.models import Mailbox, Message, Thread
|
||||
from app.models import Account, Mailbox, Message, Thread
|
||||
|
||||
|
||||
class PluginMessages:
|
||||
@@ -92,14 +92,15 @@ class PluginMessages:
|
||||
async with session_scope() as session:
|
||||
row = (
|
||||
await session.execute(
|
||||
select(Message, Mailbox.special_use)
|
||||
select(Message, Mailbox.special_use, Account.state)
|
||||
.join(Mailbox, Mailbox.id == Message.mailbox_id, isouter=True)
|
||||
.join(Account, Account.id == Message.account_id, isouter=True)
|
||||
.where(Message.id == message_id, Message.user_id == user_id)
|
||||
)
|
||||
).first()
|
||||
if row is None:
|
||||
return None
|
||||
msg, special_use = row
|
||||
msg, special_use, account_state = row
|
||||
sender = msg.from_addr[0] if msg.from_addr else {}
|
||||
return {
|
||||
"message_id": str(msg.id),
|
||||
@@ -108,6 +109,10 @@ class PluginMessages:
|
||||
# with no SPECIAL-USE. A plugin that should only act on newly
|
||||
# arrived mail checks this — `message.new` itself does not say.
|
||||
"mailbox_special_use": special_use,
|
||||
# "backfilling" | "live" | … — `message.new` fires for imported
|
||||
# history too, so a plugin that should only react to genuinely
|
||||
# new arrivals has to be able to tell the difference.
|
||||
"account_state": account_state,
|
||||
"subject": msg.subject or "",
|
||||
"from_email": (sender.get("email") or "").lower(),
|
||||
"from_name": sender.get("name") or "",
|
||||
|
||||
Reference in New Issue
Block a user