From e350c798bba77231e61a376ecea354fe0fc01ee7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Padez?= Date: Tue, 18 Aug 2026 01:05:31 +0000 Subject: [PATCH] todo: the three plugin-system gaps the email extraction exposed --- TODO.md | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/TODO.md b/TODO.md index 97116aef..7b07f328 100644 --- a/TODO.md +++ b/TODO.md @@ -298,6 +298,60 @@ pane belonging to the folder you left; and the breadcrumb opted out of that last restored. Deliberate as written — a dashboard can hold two browsers and one URL cannot serve both — but worth re-deciding rather than inheriting. +## Plugin system — gaps the email extraction found (2026-08-18) + +- [ ] **A plugin cannot declare its own npm dependencies, so the PLATFORM is carrying them.** `imapflow`, + `mailparser` and `@types/mailparser` are in the platform's `package.json` and are imported by nothing + in `src/` — they exist solely for `plugins/email/sidecar/`. Plugins have no `package.json`, run with + `cwd: PLATFORM_DIR`, and resolve from the platform's `node_modules`, so deleting those three lines + breaks the installed plugin on its first sync. + + This does not scale and it is the wrong party holding the risk: the platform cannot be answerable for + what an arbitrary plugin decides to install, and `bunfig.toml` sets `frozenLockfile = true` precisely + so a dependency change is a deliberate, reviewed act. Publishing will be reviewed rather than a + free-for-all, which narrows the threat but does not move the responsibility. + + What it probably needs: a `dependencies` field in the manifest (the manifest already holds + `osDependencies` for host binaries, which is the same shape of problem solved once), installed into + the plugin's own directory at install time rather than merged into the platform's tree. Note the + install path already learned not to trust `$PATH` — `pushPluginSchema` resolves a binary rather than + spawning `bunx` — so whatever runs the install has to do the same. + + Until then: `imapflow` and `mailparser` STAY. Removing them is not tidying, it is breaking email. + +- [ ] **`email_accounts/` is created by the PLATFORM for every account, and should be the plugin's job.** + `USER_DIRS` in `data-path.ts` pre-creates it at 0700 for every user, whether or not the email plugin + is installed — so a core install provisions a directory named for a plugin it does not have. + + Ownership should invert: the plugin provisions its own storage, in all three moments — + + 1. **on plugin install**, for accounts that already exist; + 2. **on user provisioning**, for accounts created afterwards; + 3. **lazily**, the first time a provisioned user opens the plugin, so a machine that missed either of + the above still heals itself. + + The third is the one that makes the other two non-critical, and it belongs in the email sidecar + rather than in the platform's account handler. + + This is entangled with the `email_db` leak below — core reads that directory today, so the plugin + cannot become its sole owner until that is resolved too. Do them together. + +- [ ] **The platform still knows email's on-disk vocabulary, deliberately, and it is untested by anything.** + `findEmailAccountName` in `data-path.ts` reads `DATA_PATH//email_accounts/*/emails.db` and picks + the newest by mtime. Two core callers use it: `user-instance.ts` (`OFFICER_EMAIL_DB`, for the agent's + `email_db` MCP tool) and `chat/websocket.ts` (`resolveEmailCwd`, for a chat opened against a mailbox). + `ChatPanelWrapper.tsx` hardcodes `workspace.id === 'email'` to trigger the second. + + This traded a CODE dependency for a CONVENTION, which was the right trade at the time — the code + dependency is what put `officer-agent` into a restart loop on 2026-08-13 (`fbb6d6c7`). But the + convention has no enforcement on either side: if the plugin ever renames `emails.db`, moves to one + database per folder, or nests differently, core silently points the agent at a path that does not + exist. No import breaks, no type fails, no test covers it. The tool just goes quiet. + + The clean version needs two things the platform does not have: a way for a plugin to ship an MCP tool, + and a way for a plugin to declare a chat context. Until both exist this stays — but it should be a + decision someone re-takes, not one that is inherited. + ## Plugin repos — what writing their READMEs turned up Three READMEs were written on 2026-08-16 (`music`, `offscale`, `example`) and pushed to their own repos.