offscale was missing from the permissions page because it declared ownerOnly, which mapped to kind admin, and admin capabilities are never offered for granting. correct by the old rule and wrong by the standard: every plugin follows the same platform-level permission model, appearing on the same page with the same read/write/none per role. so the field is gone rather than flipped. a plugin has no way to say owner-only, which makes the standard structural instead of remembered — the same move as the workspace rule. core, execution, confined and admin stay the platform's to assign and a plugin cannot name any of them, so the escalation question is removed rather than answered. this is the second draft of this decision to be deleted: first a full CapabilityKind with three of five values forbidden, then an ownerOnly boolean, now nothing. the doc records all three so the reasoning is visible rather than just the conclusion. finer visibility stays the plugin's job. offscale is the worked example of the gap that leaves and its manifest says so: it is grantable now, and its queries still scope by the caller, so a granted member would see their own empty server list rather than the owner's. closing that is a change inside the plugin. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
36 lines
1.3 KiB
TypeScript
36 lines
1.3 KiB
TypeScript
import type { PluginManifest } from '@@/plugins/manifest';
|
|
|
|
// The reference plugin. Not a fixture — this is what a plugin author reads first, and it is deliberately
|
|
// the smallest thing that is still a real one: a manifest and one route.
|
|
//
|
|
// Everything structural is convention, so this directory IS the documentation:
|
|
//
|
|
// manifest.ts you are here — only what a directory listing cannot say
|
|
// api/router.ts exports `router`; mounted at /api/example
|
|
// db/schema.ts tables, if it had any (every name prefixed `example_`)
|
|
// sidecar/index.ts a process, if it needed one (.mjs instead means node)
|
|
// web/Router.tsx a frontend, if it had one
|
|
//
|
|
// `appName` is not declared anywhere: it is the directory name, so the id cannot disagree with where the
|
|
// code sits.
|
|
export const manifest: PluginManifest = {
|
|
publisher: 'officerdev',
|
|
version: '1.0.0',
|
|
platform: '>=1.0.0',
|
|
|
|
label: 'Example',
|
|
summary: 'The reference plugin — one route, nothing else',
|
|
icon: 'Puzzle',
|
|
color: '#94a3b8',
|
|
|
|
// One permission gating the whole surface. `ownerOnly: false` means a role can be granted it — which is
|
|
// the interesting case, because it is the one the capability gate actually has to resolve.
|
|
permissions: [
|
|
{
|
|
key: 'example',
|
|
label: 'Example',
|
|
description: 'The reference plugin',
|
|
},
|
|
],
|
|
};
|