# AGENTS.md Guidance for a coding agent working on **this plugin's own repository**. `CLAUDE.md` beside it only points here — the two harnesses discover different filenames and one set of instructions has to reach both. **This is not what the plugin is.** `README.md` answers that, for a person deciding whether to install it. This file answers "I have to change something in here — what will I break?" ## Where you are A plugin repository, cloned into `~/officer-dev/plugins/email`, its own git remote. It is **not** part of the platform's repository, and `platform/plugins/email` is a different thing — that is where this plugin gets INSTALLED, by the installer cloning it from gitea. Never edit there; the next install overwrites it. Check the checkout with `bun run plugins:check` from the platform repo. ## What it ships - `api/router.ts` — routes, mounted at `/api/email` - `sidecar/index.ts` — a process, run by PM2 as `officer-email` - `db/schema.ts` — tables: `email_accounts` - `web/` — panels and a layout; the platform renders the screen, not this plugin - `chat/AGENTS.md` — copied into a chat's working directory as its brief - `skills/` — read by every agent session, harness-agnostic Presence is the declaration: the platform decides what this plugin has by what files exist. Adding `db/schema.ts` gives it tables; deleting `api/router.ts` removes its routes. There is no field to keep in step. ## What must not change without knowing why - **Permission key `email`** — stored as the subject of every grant. Renaming one is a data change, not a rename. - **Panel keys** (`email-list`, `email-reader`, `email-chat`) — a user's stored layout names them. - **Table names** — they hold real rows on installed machines. - **`appName`** — the directory name IS the id: route segment, table prefix, sidecar suffix, install key. ## Traps that have already cost time - **`useClient()` rebuilds its verbs on every render.** Anything putting one in a `useCallback` dependency list gets a new identity every render. This silently disabled every playback report in the Jellyfin player for days; the fix is refs. - **The reader reads the URL param the PLATFORM passes**, which is `section`, not a name of this plugin's choosing. Asking for the wrong one returns `undefined` and the pane renders its empty state forever — that happened here and looked like a broken reading pane. - **Snippets are extracted from raw MIME without decoding**, so some read `This is a multi-part message in MIME format.` and others show `m=C3=A1s`. Known, cosmetic, not yet fixed. ## Verifying From the platform repo, which is where the toolchain lives: ```bash bunx tsgo # a red typecheck is not done bun test # 1 known failure: ModelSelector, unrelated bun run plugins:check bunx prettier --write ``` A sidecar change needs `pm2 restart officer-email`; anything in `api/` or `web/` needs `pm2 restart officer`. ## Related - `README.md` — what it is and what it claims, for someone installing it - `skills/` — how an agent USES this, as opposed to changing it