load affordances and append them to every claude session

The platform hosts the agent and never introduces itself. An agent gets a
shell, a filesystem and a prompt; everything about the CLIENT on the other end
has to be guessed. It writes ![](/home/me/chart.png), nothing renders, and
nothing says why — because an <img> carries no bearer token and it is the
user's DEVICE that fetches the URL.

servers/affordances/load.ts reads capabilities/affordances/ and compiles one
block; claude-manager passes it as systemPrompt.append with the claude_code
preset, so the harness keeps its own prompt.

Passed there rather than written to a file because it is the one place every
session goes through, owner and member alike. A generated CLAUDE.md cannot do
that — user-instance.ts deliberately refuses to write one for the owner, since
it would pollute the personal ~/.claude/CLAUDE.md they maintain themselves.

Read per session, not cached: capabilities/ is already read fresh each session,
so a plugin installed an hour ago is live on the next message. Synchronous
because createSession is not async, and making it so to read a few kilobytes
would ripple through every caller.

No filtering. Everything is loaded into every conversation, because the axis to
filter on is not one dimension — for image rendering it is what the CLIENT
renders, for gitea what TOOLING exists on the machine. Fine at two files, not
at twenty; TODO.md carries the argument.

Opencode gets nothing, and that is recorded as a gap rather than hidden: an
affordance that holds on one harness is the drift this removes. Deliberate —
opencode is paused pending v2 and the work would be thrown away.

Nine tests, aimed at the failure that matters: a file that silently does not
reach the agent looks exactly like one nobody wrote.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-19 07:27:01 +00:00
co-authored by Claude Opus 5
parent d8c97b6814
commit 057bf7b183
4 changed files with 225 additions and 10 deletions
+15 -10
View File
@@ -304,20 +304,25 @@ Agreed and written down in `plugins/CLAUDE.md`; **nothing is built**. An afforda
on the other end offers the agent — declared by the code that implements it, read at runtime. The first
two are image rendering (core) and gitea (`git` for git, `tea` for everything else).
To build:
Built 2026-08-19: `servers/affordances/load.ts` reads the tree and compiles one block, and
`claude-manager.ts` appends it to every Claude session via
`systemPrompt: { type: 'preset', preset: 'claude_code', append }`. Read per session, so a plugin
installed an hour ago is live on the next message.
Still to build:
- `capabilities/affordances/` created by the setup script, with `officer/core/` populated from an
officer-capabilities repository. The clone is future work; the directory is not.
- A compiler: parse the tree, take each body under its `name` as a heading, produce one small block.
Per session, not per boot — `capabilities/` is already read fresh each agent session, and a plugin
installed mid-day should be live on the next message.
- Delivery. Claude sessions take `systemPrompt: { type: 'preset', preset: 'claude_code', append }`,
which nothing passes today. **Opencode has its own driver and would need the same**, or the affordance
is a claim that only holds on one harness — which is the drift this exists to remove. A bare `claude`
run in a terminal on the host gets neither unless a file is also written.
officer-capabilities repository. The directory exists on this machine by hand; a fresh install gets
nothing, so the first affordance is currently a local artefact rather than a shipped one.
- **Opencode delivery.** It has its own driver and gets nothing, so image rendering is currently a claim
that holds on one harness — exactly the drift affordances exist to remove. Deliberate: opencode work is
paused pending its v2 migration and this would be thrown away. Revisit when v2 lands.
- A bare `claude` run in a terminal on the host gets nothing either, since it never passes through
`claude-manager`. Would need a generated file, which is the thing `user-instance.ts` refuses to write
for the owner.
- Plugin contribution: an `affordances/` directory in the plugin repository, copied to
`capabilities/affordances/<publisher>/<plugin>/` on install and removed on uninstall. Same shape as
`assets/` and `publishAssets`.
`assets/` and `publishAssets`. **Nothing does this yet** — gitea will be the first.
Open, and deliberately deferred: