Files
email_agent/docs/outline/03-code-map.md
Documenter c5760db43d docs: Phase 8 documentation - Gitea, Obsidian, Outline, Vikunja, map (AUD-74)
- README: architecture Mermaid, Phase 8 status, Phase 7 endpoints, Documentation index
- docs/outline/: AI-context collection (index, system, config, code map, runbook)
- docs/email-agent-architecture.mmd + rendered PNG + Archify map (showcase pass)
- docs/vikunja-tasks.json (Vikunja project 17, tasks 179-185)
- docs/phase8-documentation.md evidence
2026-10-13 10:46:21 +11:00

159 lines
7.9 KiB
Markdown

# Email Agent — Code Map
## Folder structure
```
email_agent/
├── AGENTS.md Build spec (phases 1-8, requirements, rules).
├── README.md Technical repository README.
├── cmd/email-agent/
│ └── main.go Entry point. HTTP server and every CLI flag.
├── internal/
│ ├── apprise/ Apprise API client for the daily summary email.
│ ├── classify/ JEV client, rule engine, classification orchestration.
│ ├── config/ Environment-driven configuration.
│ ├── hub/ Hub contract: capabilities, /ask answerer, notify.
│ ├── mailfetch/ Stdlib-only IMAP client, MIME parse, FR-2 source address.
│ ├── mailops/ Gmail label moves, flag changes, and SMTP sending.
│ ├── reply/ OmniRoute LLM drafts and reply templates.
│ ├── server/ HTTP routes, auth middleware, HTMX handlers.
│ ├── session/ Server-side session store and signed cookie values.
│ ├── store/ SQLite open, schema, migrations, all queries.
│ ├── summary/ Deterministic daily-summary builder.
│ ├── unsubscribe/ RFC 2369/8058 and body unsubscribe-link detection.
│ └── version/ Build metadata (-ldflags).
├── web/
│ ├── embed.go Embeds the static and template files.
│ ├── static/ tokens.css, app.css, Inter fonts, htmx.min.js.
│ └── templates/ login, dashboard, inbox, message, categories, rules,
│ unsubscribe, reports.
├── docs/ Phase evidence, AI context, map, diagrams.
├── Dockerfile Multi-stage, CGO-free build.
├── docker-compose.yml Own container, image, port, and volume.
└── Makefile Build, test, and run targets.
```
## Entry points
| Entry point | Language | Role |
| :--- | :--- | :--- |
| `cmd/email-agent/main.go` | Go | Service and CLI. |
| `internal/server/server.go` | Go | HTTP router and middleware. |
| `internal/store/schema.sql` | SQL | The AGENTS.md section 7 tables. |
## CLI flags
| Flag | Effect |
| :--- | :--- |
| `-version` | Print version, commit, and build time. |
| `-healthcheck` | Probe the local `/health` endpoint and exit. |
| `-fetch` | Run one Gmail IMAP scan and exit (FR-1). |
| `-classify` | Classify pending messages with JEV and exit. |
| `-classify-all` | Re-classify every stored message. |
| `-check-smtp` | Verify SMTP IPv4, STARTTLS, and login. Sends no mail. |
| `-check-thread <id>` | Confirm a stored thread has a sent reply. Read-only. |
| `-summary` | Build today's summary, store it, and print it. Sends nothing. |
| `-summary-send` | Build the summary and deliver it by email via Apprise. |
| `-notify` | Send one notification to the Hub (`POST /notify`). |
| `-retain` | Clear old message bodies and cap `actions_log` (AGENTS.md section 7). |
## HTTP routes
| Method | Path | Auth | Purpose |
| :--- | :--- | :--- | :--- |
| `GET` | `/health` | public | Liveness and database check. |
| `GET` | `/readyz` | public | Readiness. |
| `GET` | `/login` | public | Sign-in form. |
| `POST` | `/login` | public | CSRF-checked sign-in. |
| `POST` | `/logout` | session | End the session. |
| `GET` | `/` | session | Dashboard. |
| `GET` | `/partials/inbox` | session | HTMX inbox fragment. |
| `POST` | `/actions/fetch` | session + CSRF | One IMAP scan. |
| `POST` | `/actions/classify` | session + CSRF | Classify pending messages. |
| `POST` | `/actions/reclassify` | session + CSRF | Re-classify every message. |
| `POST` | `/actions/bulk` | session + CSRF | Bulk delete, move, mark, restore. |
| `GET` | `/partials/message` | session | Message detail fragment. |
| `POST` | `/actions/draft` | session + CSRF | Generate and store a reply draft. |
| `POST` | `/actions/draft/save` | session + CSRF | Save draft edits. |
| `POST` | `/actions/send` | session + CSRF + confirm | Send a reply over SMTP. |
| `GET` | `/categories` | session | Category editor (FR-14). |
| `POST` | `/categories/add` | session + CSRF | Add a category. |
| `POST` | `/categories/rename` | session + CSRF | Rename and cascade. |
| `POST` | `/categories/remove` | session + CSRF | Remove and reassign. |
| `GET` | `/rules` | session | Rules editor (FR-11). |
| `POST` | `/rules/save` | session + CSRF | Save a new rules version. |
| `POST` | `/rules/restore` | session + CSRF | Restore an older version. |
| `POST` | `/rules/reclassify` | session + CSRF | Re-classify with current rules. |
| `GET` | `/unsubscribe` | session | Unsubscribe links and ignore list (FR-7). |
| `POST` | `/unsubscribe/ignore` | session + CSRF | Add a domain to the ignore list. |
| `POST` | `/unsubscribe/unignore` | session + CSRF | Remove a domain record. |
| `POST` | `/unsubscribe/mark` | session + CSRF + confirm | Record a domain. No link is followed. |
| `GET` | `/reports` | session | Daily summary and delete-report. |
| `GET` | `/capabilities` | Hub bearer | Hub capability list (FR-12). |
| `POST` | `/ask` | Hub bearer | Read-only short answer. |
| `GET` | `/static/...` | public | Embedded CSS, fonts, htmx. |
## Data model
SQLite. The schema is in `internal/store/schema.sql`. Migrations run at start
and are recorded in `schema_migrations`.
| Table | Holds |
| :--- | :--- |
| `messages` | Gmail id, thread id, headers, from, to, subject, body, source address. |
| `classifications` | Category, importance, action, confidence, model. |
| `categories` | The editable category list (FR-14). |
| `rules` | Plain-text rules, versioned (FR-11). |
| `actions_log` | Append-only action log (FR-13). Capped by `-retain`. |
| `unsubscribe` | Detected domains, status, and ignore list (FR-7). |
| `drafts` | Reply draft body, template, and sent state (FR-8). |
| `summaries` | Daily summary text and delivery target (FR-10). |
Migrations: v2 adds IMAP UID fields. v3 seeds the first categories and rules.
v4 adds `folder` and `mailbox`. v5 adds `message_id_header` and `drafts.sent_at`.
v6 adds `list_unsubscribe` fields and `unsubscribe.url`.
## Key modules
| Module | Role |
| :--- | :--- |
| `internal/mailfetch/imap.go` | Minimal IMAP client. Forces `tcp4`. |
| `internal/mailfetch/message.go` | MIME parse and the FR-2 source address. |
| `internal/mailfetch/imap_write.go` | IMAP MOVE, COPY, and flag changes. |
| `internal/classify/jev.go` | JEV `/v1/systemone` client. Three Choice questions. |
| `internal/classify/rules.go` | Rule parse and first-match-wins engine (FR-4). |
| `internal/mailops/mailops.go` | App folder to Gmail label mapping. |
| `internal/mailops/smtp.go` | SMTP send with IPv4 and STARTTLS. |
| `internal/reply/draft.go` | Reply prompt and OmniRoute call. |
| `internal/reply/templates.go` | Built-in reply templates. |
| `internal/store/retention.go` | Body clearing and `actions_log` cap. |
| `internal/hub/ask.go` | Deterministic, read-only answerer. |
## Tests
All Go tests run in a container:
```sh
timeout 600 docker run --rm -v "$PWD":/src -w /src golang:1.25-alpine go test ./...
```
| Package | Coverage |
| :--- | :--- |
| `internal/classify` | JEV client, rule engine, orchestration. |
| `internal/hub` | Answer intents, auth, read-only proof, notify. |
| `internal/mailfetch` | IMAP parse, MIME, FR-2, write ops. |
| `internal/mailops` | Label mapping, SMTP. |
| `internal/reply` | Draft prompts and templates. |
| `internal/server` | Auth, routes, CSRF, handlers. |
| `internal/store` | Schema, retention, triage, categories, summaries. |
| `internal/summary` | Counts, ordering, text. |
| `internal/unsubscribe` | Link detection. |
| `internal/apprise`, `internal/config`, `internal/session` | Client and config. |
## Dependencies
Go 1.25. The only database driver is `modernc.org/sqlite` (pure Go, no CGO).
The mail path uses the Go standard library plus a minimal IMAP client. The UI
uses vendored HTMX and the Family Home Lab `tokens.css`. No JavaScript build
step.