Antispam: one button, and only where it means something

Spam / Not spam appeared on every message in every thread. "Not spam" on an
inbox message is nonsense — it was never filed as spam — and a pair of buttons
everywhere is noise that trains nobody.

The slot now shows a single action chosen by where the message actually is:
"Spam" in the inbox or archive, "Not spam" in the spam folder, nothing at all in
Sent, Drafts or Trash, where no move is a verdict about spam. A message already
taught says so instead of offering the same lesson again.

That needs the message's folder, which the slot was never told. Rather than a
core change to the slot props, /verdict/{id} becomes /context/{id} and returns
the folder alongside the verdict — one round-trip, answered for any message
including ones never classified.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018SqJYt7jD7oUEkoY68sBZo
This commit is contained in:
2026-09-17 19:01:46 +02:00
co-authored by Claude Opus 5
parent 149c11581e
commit e420126145
5 changed files with 94 additions and 31 deletions
@@ -54,6 +54,19 @@ class VerdictOut(BaseModel):
learned: str | None
class MessageContextOut(BaseModel):
"""What the per-message UI slots need in one round-trip.
``special_use`` is where the message currently sits, which decides which
action is meaningful: "Spam" in the inbox, "Not spam" in the spam folder,
neither in Sent or Drafts.
"""
message_id: uuid.UUID
special_use: str | None
verdict: VerdictOut | None
class StatsOut(BaseModel):
counts: dict[str, int]
top_symbols: list[dict[str, object]]