- FR-11 rules editor: plain-text area at /rules, saved and versioned
(SaveRules appends a version; restore re-saves an old version), parse
errors surfaced, one-click re-classify. The reply model now reads the
rules document too (FR-11).
- FR-7 unsubscribe: parse and store List-Unsubscribe / List-Unsubscribe-Post
(migration v6), detect email and web links (internal/unsubscribe), a
domain-level ignore list, and an explicit record of what was marked. No
code path contacts an unsubscribe link; acting on one needs Sam's approval.
- Migration v6 is additive; unsubscribe gains url/updated_at.
- Tests: detector, parser, store migration/records/candidates, rules editor,
unsubscribe handlers, message-detail rendering.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Implements FR-3, FR-4, FR-5 and FR-14.
- internal/classify: TypeSafe System One (JEV) HTTP client with the confirmed
request/response shape, retry with backoff on 429/529/5xx, a plain-text rule
engine (first match wins, overrides only the named fields), and bounded
orchestration with a per-run cap.
- internal/store: migration v3 adds an editable `categories` table (name =
JEV Choice option and classifications key; rename cascades, remove reassigns)
and seeds a starter rules version once. Classification persistence, coverage,
and pending/all message queries.
- internal/server: POST /actions/classify and /actions/reclassify, plus the
FR-14 category editor (GET /categories and add/rename/remove). The triage list
now groups by the live category list and shows action + confidence.
- CLI: -classify and -classify-all for n8n.
- Docs: docs/phase3-classify.md with the confirmed JEV shape, a live read-only
classification, and the DoD evidence.
DoD: all 100 stored messages have a non-null category, importance, and suggested
action (0 nulls); JEV picked a newly added/renamed category for 6 real messages
over HTTP. One live JEV call recorded in the phase doc.
Co-Authored-By: Paperclip <noreply@paperclip.ing>