docs: Phase 6 unsubscribe + rules editor evidence (AUD-72)
Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
48
README.md
48
README.md
@@ -9,18 +9,24 @@ pre-planning material and is not authoritative.
|
||||
|
||||
## Status
|
||||
|
||||
**Phase 5 — Reply drafts.** The Go skeleton, session login, and HTMX dashboard
|
||||
(Phase 1), real mail over **IMAP with an app password, forced to IPv4** (Phase
|
||||
2), **JEV classification** (Phase 3), **bulk triage actions** (Phase 4), and now
|
||||
**reply drafts** (Phase 5). Open a message to read it, draft a reply with the LLM
|
||||
through **OmniRoute** from a template, edit it, and send it over Gmail SMTP
|
||||
(IPv4 + STARTTLS). A reply is sent **as the address the mail arrived at**
|
||||
(`X-Pm-Original-To`, FR-15) and returns to the original sender; the reply sets
|
||||
`In-Reply-To`/`References` so Gmail keeps the thread, and the original is filed
|
||||
under its `Replied-to/<address>` label. Sending is a deliberate UI action with a
|
||||
confirmation — there is no auto-send (§10). Every action, including drafts and
|
||||
sends, is appended to `actions_log` (FR-13). See
|
||||
[`docs/phase5-replies.md`](docs/phase5-replies.md) for the phase evidence and
|
||||
**Phase 6 — Unsubscribe and rules editor.** The Go skeleton, session login,
|
||||
and HTMX dashboard (Phase 1), real mail over **IMAP with an app password,
|
||||
forced to IPv4** (Phase 2), **JEV classification** (Phase 3), **bulk triage
|
||||
actions** (Phase 4), **reply drafts** (Phase 5), and now **unsubscribe
|
||||
detection and the rules editor** (Phase 6). Open a message to read it, draft a
|
||||
reply with the LLM through **OmniRoute** from a template, edit it, and send it
|
||||
over Gmail SMTP (IPv4 + STARTTLS). A reply is sent **as the address the mail
|
||||
arrived at** (`X-Pm-Original-To`, FR-15) and returns to the original sender; the
|
||||
reply sets `In-Reply-To`/`References` so Gmail keeps the thread, and the
|
||||
original is filed under its `Replied-to/<address>` label. Sending is a
|
||||
deliberate UI action with a confirmation — there is no auto-send (§10). The
|
||||
**rules editor** (`/rules`) saves and versions the plain-text rules that JEV and
|
||||
the reply model read. **Unsubscribe detection** (`/unsubscribe`) finds RFC
|
||||
2369/8058 links and body links, keeps a domain ignore list, and records what Sam
|
||||
marks — it **never follows a link**. Every action, including drafts, sends,
|
||||
rules edits, and unsubscribe marks, is appended to `actions_log` (FR-13). See
|
||||
[`docs/phase6-unsubscribe.md`](docs/phase6-unsubscribe.md) for the phase evidence,
|
||||
[`docs/phase5-replies.md`](docs/phase5-replies.md) for Phase 5, and
|
||||
[`docs/phase4-triage.md`](docs/phase4-triage.md) for Phase 4.
|
||||
|
||||
## Layout
|
||||
@@ -35,6 +41,7 @@ internal/reply/ LLM reply drafts through OmniRoute, reply templates
|
||||
internal/session/ server-side session store + signed cookie values
|
||||
internal/store/ SQLite open + schema (internal/store/schema.sql)
|
||||
internal/server/ HTTP routes, auth middleware, HTMX handlers
|
||||
internal/unsubscribe/ RFC 2369/8058 + body unsubscribe-link detection (no network)
|
||||
internal/version/ build metadata (set via -ldflags)
|
||||
web/static/ embedded design tokens, Inter fonts, htmx, app.css
|
||||
web/templates/ embedded server-rendered HTML
|
||||
@@ -123,8 +130,9 @@ from contains "amazon.com" => category=Newsletter, action=unsubscribe
|
||||
|
||||
Conditions join with ` and ` over `from`, `to`, `subject`, `source`, `body`
|
||||
using `contains`, `equals`, `starts_with`, `ends_with`. The first matching rule
|
||||
wins and overrides only the fields it names. The rules editor UI is Phase 6; the
|
||||
classifier already reads the stored text.
|
||||
wins and overrides only the fields it names. Edit and version the rules at
|
||||
`/rules` (Phase 6); the classifier and the reply model both read the newest
|
||||
version.
|
||||
|
||||
### Categories (FR-14)
|
||||
|
||||
@@ -231,6 +239,14 @@ Caddy on `.35` proxies `mail.lab.audasmedia.com.au` to this port.
|
||||
| `POST` | `/categories/add` | session + CSRF | Adds a category |
|
||||
| `POST` | `/categories/rename` | session + CSRF | Renames a category and updates existing classifications |
|
||||
| `POST` | `/categories/remove` | session + CSRF | Removes a category and reassigns its messages |
|
||||
| `GET` | `/rules` | session | Rules editor: current text, versions, parse errors (FR-11) |
|
||||
| `POST` | `/rules/save` | session + CSRF | Saves the rules text as a new version |
|
||||
| `POST` | `/rules/restore` | session + CSRF | Restores an older version as a new version |
|
||||
| `POST` | `/rules/reclassify` | session + CSRF | Re-classifies every message with the current rules |
|
||||
| `GET` | `/unsubscribe` | session | Detected unsubscribe links, ignore list, and records (FR-7) |
|
||||
| `POST` | `/unsubscribe/ignore` | session + CSRF | Adds a domain to the ignore list |
|
||||
| `POST` | `/unsubscribe/unignore` | session + CSRF | Removes a domain record |
|
||||
| `POST` | `/unsubscribe/mark` | session + CSRF + confirm | Records a domain for unsubscribe; never contacts the link |
|
||||
| `GET` | `/static/…` | public | Embedded `tokens.css`, `app.css`, Inter fonts, `htmx.min.js` |
|
||||
|
||||
Unauthenticated requests to `/` and `/partials/…` redirect to `/login?next=…`.
|
||||
@@ -248,4 +264,6 @@ and runs on every start. Migration v2 adds `imap_uid` and `imap_uidvalidity` to
|
||||
version once, so a category removed by the user is not silently re-added;
|
||||
migration v4 adds `folder` and `mailbox` to `messages` for the Phase 4 triage
|
||||
folders; migration v5 adds `messages.message_id_header` (reply threading) and
|
||||
`drafts.sent_at`.
|
||||
`drafts.sent_at`; migration v6 adds `messages.list_unsubscribe` and
|
||||
`messages.list_unsubscribe_post` and extends `unsubscribe` with `url` and
|
||||
`updated_at`.
|
||||
|
||||
Reference in New Issue
Block a user