From 7befaf032a8a4a0937056b9929c2fb9467f45e4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Padez?= Date: Fri, 14 Aug 2026 19:31:48 +0000 Subject: [PATCH] the manifest holds only what the tree cannot say MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit lean it to identity facts and human choices: publisher, version, platform range, the four presentation fields, and permissions. everything structural becomes convention, where presence is the declaration — sidecar/, api/router.ts, db/schema.ts, web/Router.tsx, web/panels.ts. appName comes from the directory name, so the id cannot disagree with where the code sits. the dock tile and page title needed no fields at all: the tile is label + icon + color + mountPrefix, and the title is label. writing them again was duplication that could only drift. runtime is the file extension. index.mjs is node, index.ts is bun — implicit, but already the rule here, since officer-pty runs under node for node-pty's abi and everything else is bun. better than a field that can contradict the file. dependsOn is gone; nothing read it. Co-Authored-By: Claude Opus 5 --- docs/offscale-plugin.md | 74 ++++++++++++++++++++--------------------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/docs/offscale-plugin.md b/docs/offscale-plugin.md index 082c4e56..715fd990 100644 --- a/docs/offscale-plugin.md +++ b/docs/offscale-plugin.md @@ -465,32 +465,20 @@ from nothing includes what nothing needs and misses what is awkward. The field s is the floor, not the ceiling. ```ts +// plugins/offscale/manifest.ts export const manifest = { - // ── Identity ───────────────────────────────────────────────────────────── - /** THE id. Route segment, table prefix, sidecar suffix, install key. One name, everywhere. */ - appName: 'offscale', - /** Who published it. The ONLY input that decides the mount prefix — see `mountPrefix()`. */ + /** Constant today. The one input to `mountPrefix()`, and the seam third parties hang off later. */ publisher: 'officerdev', /** The plugin's own semver. Updates compare against this. */ version: '1.0.0', /** Which platforms this build is good for. Refused at install when it does not match. */ platform: '>=1.0.0 <2.0.0', - // ── Presentation ───────────────────────────────────────────────────────── label: 'Offscale', summary: 'Your tailnet — machines, users, pre-auth keys and access policy', icon: 'Network', color: '#818cf8', - // ── Backend ────────────────────────────────────────────────────────────── - sidecar: { - /** PM2 process name. Always `officer-`, but written out because PM2 is told it verbatim. */ - process: 'officer-offscale', - /** `bun` or `node`. A field rather than an assumption: officer-pty needs node for node-pty's ABI. */ - runtime: 'bun', - script: 'sidecar/index.ts', - }, - // Named `permissions`, NOT `capabilities`. That word already means three different things here — the // permission registry, the officer-items store, and the sidecar's routing keys — and a fourth would be // one too many. `permissions` is accurate and free: the old table of that name went in 044aacf4. @@ -503,32 +491,44 @@ export const manifest = { ownerOnly: true, }, ], - - // ── Data ───────────────────────────────────────────────────────────────── - /** Drizzle schema. Every table must be prefixed `offscale_`; the installer enforces it. */ - schema: 'db/schema.ts', - - // ── Frontend ───────────────────────────────────────────────────────────── - frontend: { - /** Default export is mounted at `/*` by the generated Plugins.tsx. */ - router: 'web/Router.tsx', - /** `appRegistryMetas` — the panel apps this plugin contributes. */ - panels: 'web/panels.ts', - dock: { label: 'Offscale', to: '/offscale' }, - title: 'Offscale', - }, - - // ── Optional dependencies ──────────────────────────────────────────────── - // Informational, not enforced: both are service dependencies that already degrade. They exist so the - // store can say "the Console section needs the terminal plugin" instead of the section silently not - // working, which is the difference between a missing feature and a broken one. - dependsOn: [ - { appName: 'anthropic-proxy', optional: true, reason: 'the ACL drafting assistant' }, - { appName: 'pty', optional: true, reason: 'the SSH console section' }, - ], } as const; ``` +### Everything the tree can say, the tree says + +The manifest holds only what a directory listing genuinely cannot tell you: an identity fact, or something +a human chose. Everything structural is convention, and **presence is the declaration**: + +| Path | Means | +| -------------------- | --------------------------------------------------------------------------------------------- | +| _the directory name_ | `appName` — `plugins/offscale/` **is** the id, so it cannot disagree with where the code sits | +| `sidecar/index.ts` | there is a sidecar; PM2 gets an entry. `.mjs` instead means node — see below | +| `api/router.ts` | there is a backend router, mounted at `mountPrefix(manifest)` | +| `db/schema.ts` | there are tables; pushed on install, every name prefixed `offscale_` | +| `web/Router.tsx` | there is a frontend; its default export mounts at `/*` | +| `web/panels.ts` | it contributes panels; exports `appRegistryMetas` | + +The dock tile and the page title need no fields either — the tile is `{ label, icon, color, to: +mountPrefix(manifest) }` and the title is `label`, all of which are already above. Writing them again was +duplication that could only ever drift. + +**The runtime is the file extension.** `sidecar/index.mjs` runs under node, `sidecar/index.ts` under bun. +Implicit, but it is the rule this repo already follows — `officer-pty` is `pty/index.mjs` under node +because node-pty is a native module built against Node's ABI, and everything else is bun. Better than a +field that can contradict the file it describes. + +### Dropped from the first draft + +- **`dependsOn`** — nothing read it and nothing enforced it. Both of offscale's dependencies already + explain themselves where it matters (`assistant_unavailable`; "no SSH host configured"). A field whose + only job is to be displayed, that nothing displays, is stale the first time anyone looks at it. Add it + when something consumes it. +- **`kind`** — see below. +- **`sidecar` / `schema` / `frontend` objects** — all convention now. + +`[open]` A plugin with a frontend that should NOT get a dock tile has no way to say so: `web/` present +means a tile. Fine for offscale; add a flag the first time something needs it. + ### `admin` has to be allowed, and the pilot proved it immediately The earlier rule here was "a plugin may declare `app`, and nothing else". **That is wrong, and offscale is