diff --git a/plugins/CLAUDE.md b/plugins/CLAUDE.md new file mode 100644 index 00000000..f031ba9f --- /dev/null +++ b/plugins/CLAUDE.md @@ -0,0 +1,81 @@ +# Plugins + +`EXTRACTING-A-PLUGIN.md` is the runbook for turning a platform feature into a plugin, and +`EXTRACT-A-PLUGIN-PROMPT.md` is the way to hand that to a fresh agent. This file is for the concepts a +plugin author needs that are not procedure. + +**This directory holds documentation, not plugins.** Every plugin is its own repository; `plugins//` +appears by `git clone` when somebody installs one, and is gitignored here. + +--- + +## Affordances + +**DESIGN, NOT BUILT.** Agreed 2026-08-19. There is no registry, no API and nothing to call yet. It is +written down now because the name and the boundary are the parts that are expensive to change later, and +because a plugin author who invents a different word for this will be hard to merge with afterwards. + +### What one is + +> **An affordance is something the surface on the other end of the conversation offers the agent — +> declared by the code that implements it, and read by the agent at runtime.** +> +> Not what the agent can do. What the client will honour. + +### Why it needs to exist + +The platform hosts the agent and never introduces itself. An agent gets a shell, a filesystem and a +prompt; everything about the surface it is speaking _through_ has to be smuggled in as prose somebody +maintains by hand. + +The worked example is images. Officer's chat renders markdown images — `ChatImage` is wired into +`react-markdown` — but an `` carries no bearer token, so it can only load what the browser can fetch +unauthenticated. A fresh agent knows none of that. It writes `![](/home/me/chart.png)`, nothing renders, +and nothing tells it why. The knowledge currently lives in a hand-written `CLAUDE.md` on one machine, +which says "use a public URL" — a workaround, one step removed from the actual constraint, with nothing +keeping the two in sync. + +Three properties make prose the wrong container for this: + +- **It drifts.** The file describes a workaround; the code has a reason. Neither knows about the other. +- **It is per-machine.** It lives in one repository, copied by hand. +- **A plugin cannot contribute to it.** Install a plugin that adds a surface and no agent learns it + exists — which is the case that matters here, because a plugin is exactly a new surface arriving at + runtime. + +### The boundary + +Three neighbours, easily confused: + +| | answers | +| -------------- | ---------------------------------------------------- | +| **tool** | what the agent can _do_ — it acts, something happens | +| **permission** | what this _account_ may reach | +| **skill** | instructions the agent _follows_ | +| **affordance** | what the _client_ will render, accept or ignore | + +Two tests settle most cases: + +1. **Would it change if the same agent talked to a different client?** A phone app, a terminal, a + webhook, a cron run with no human at all. If yes, it is an affordance. Rendering an image is; reading + a file is not. +2. **Does the agent have to guess it today?** If the honest answer is "it writes the markdown and hopes", + that is a missing affordance. + +### The rule that makes it worth having + +**An affordance is declared where it is implemented.** `ChatImage` declares that images render, because +`ChatImage` is what makes them render. The moment it becomes a list maintained somewhere else, it is the +hand-written `CLAUDE.md` problem again with a better name — and this time with a build step. + +Same posture as `permissions/registry.ts`, and for the same reason: one enumeration, owned by the code it +describes, which cannot quietly disagree with reality. + +### On the word + +`capability` was taken — `CAPABILITIES` is the permission registry. `skill` was taken twice: by +`capabilities/skills/` here and by the wider AI ecosystem. + +`affordance` is the exact term for what an environment offers an actor, and the precision earns the +slight awkwardness: rendering an image is not a skill the agent has, it is something the client on the +other end permits. Keeping that distinction in the name saves explaining it every time.