remove the email residue the extraction left in the platform

a sweep for remnants after 2026-08-18. nothing was broken - no dangling
import, symbol, route or permission key anywhere in src/ - but the platform
was still speaking the plugin's vocabulary in a dozen places.

the one that mattered is live code rather than prose: ChatPanelWrapper
hardcodes workspace.id === 'email' to send context: 'email', which is the
frontend half of the pair whose backend half (resolveEmailCwd) carries a
fifteen-line justification. the two ends now both say so, including that
adding a SECOND id there is the signal the platform needs a real way for a
plugin to declare a chat context.

the rest: two dead imports left by removing the settings section, an empty
'Email' type-section header, a protocol.ts section header with no members
naming the plugin's SSE design, and a queue comment pointing at
sidecar/email/sync-runner.ts - a path in another repository now.

CLAUDE.md, AGENTS.md and working-on-officer.md described email as a platform
feature, an app permission and a pm2 process. those are the files a fresh
agent reads first, so they were the highest-risk staleness in the tree.

TODO.md's whole Email section moved to the plugin's own repository. it held
OPEN items pointing at sidecar/email/store.ts, which is exactly how a leftover
list sends someone to edit files that do not exist.

the delete-account dialog promised it would remove email accounts and
playlists. both tables belong to plugins now, so that cascade only happens
where the plugin is installed - it now says 'anything an installed plugin
keeps for them' instead of naming two it cannot guarantee.

imapflow and mailparser STAY in package.json. the plugin has no package.json
of its own and resolves from the platform's node_modules, so removing them
would break the installed plugin's sidecar. the fix is giving plugins their
own dependency manifest, not deleting these.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-18 01:01:23 +00:00
co-authored by Claude Opus 5
parent e88e3a70cf
commit b63badaf67
10 changed files with 41 additions and 68 deletions
+4 -42
View File
@@ -132,49 +132,11 @@ the owner's OS user and can never be granted. Indirection there really is accide
## Email
- [x] **Gmail-style search operators** (done 2026-07-24, `0e5b91e`). `parseEmailQuery` +
`searchEmails` in `sidecar/email/store.ts` parse: `from:`/`to:`/`subject:`/`body:` → FTS5 column
filters; `has:attachment`, `is:unread`/`is:read`, `label:X`, `before:`/`after:YYYY-MM-DD` → SQL
`WHERE` on `emails`; quoted values → exact phrases; free text → prefix-AND full-text; unknown
`op:val` falls back to free text. Structured-filters-only queries skip the FTS join. Validated
on the real 18k-mail DB. Search box placeholder hints at operators.
- [x] **`OR`** (done 2026-07-24, `85d38fb`). Query splits on top-level uppercase `OR` into branches;
each branch is a self-contained condition (`id IN (FTS subquery)` + its SQL filters) and
branches are OR'd — so OR works across full-text _and_ structured filters. Verified on the 18k
DB (`from:github OR from:deepgram` = 90+7 = 97 exactly).
- [ ] **Remaining:** parenthesised grouping (nesting `(a OR b) c`), `in:sent/inbox/spam/trash`
(folder scope), and relative dates (`newer_than:7d`). Grouping needs a recursive parser.
- [ ] **Group search results into threads too.** Folder views group by conversation (`ee11c94`), but
`/email/search` still returns one row per message. Apply the same `thread_id` collapse to the
FTS result set so search matches Gmail's grouped results.
Email became a plugin on 2026-08-18 (`plugins/email/`, `gitea.officer.dev/plugins/email`). Its work queue
went with it — the open items pointed at `sidecar/email/store.ts`, a path in another repository, which is
exactly the way a leftover list sends someone to edit files that are not there.
- [x] **Conversation threading** (done 2026-07-24, `ee11c94`). `thread_id` column on `emails`:
header-based for new mail (`id` = `sha1(Message-Id)`, so `References[0]` hashes to the root's
id — `computeThreadId` in `sidecar/email/store.ts`), subject+counterpart backfill for already-synced mail.
`/messages` collapses to one row per thread (window fn) with count/unread; `/thread/:id` +
`/thread/:id/read`; reader renders a collapsible stack. Verified on synthetic + 18k real DB.
- [ ] **Upgrade old mail to exact threading.** One-time full re-fetch to capture `References` for
already-synced mail — replaces the subject+counterpart fallback (which can over-merge recurring
same-subject mail from one sender). Heavy/network-bound; opt-in.
- [ ] **Store the RFC `Message-Id` header** on ingest so replies can set a real `In-Reply-To`
(currently reply threading leans on subject/participants; `id` is a local hash, not the header).
- [x] **Compose/reply/send** (done 2026-07-24). Gmail SMTP send with contacts autocomplete, rich
contenteditable body (inline images at the caret via paste/drag-drop, Bcc), single close button.
- [x] **`busy_timeout` on the email db** (done 2026-07-24, `9527e0b`). API + email sidecar share
`emails.db`; wait out a concurrent writer instead of 500-ing with "database is locked".
- [ ] **Multiple views in the Email screen (tabs).** The screen grows beyond the current mailbox into
several switchable views:
- [ ] **Sender/domain management view.** Left panel: controls to group mail **by sender** or **by
domain** (room for more grouping axes later). From a group, run bulk actions on that
sender/domain: **unsubscribe + mark irrelevant**, **delete all mail** from it, **block/ignore
future incoming**, etc. Think "inbox cleanup / triage" — operate on a whole sender at once. - Needs: aggregate query (count/size per sender + per domain), a block/ignore list the
sync/IDLE path honors on incoming, an unsubscribe action (List-Unsubscribe header / link),
and bulk delete over a sender/domain.
- [ ] **Chat view.** The existing AI email-assistant flow — likely stays roughly as-is, just lives
as one of the tabs (main view).
- Open question: tabs vs. some other view switcher; which view is default.
Anything still owed to the mail client belongs in that repository's own TODO.
## Transmission