Files
platform/todo/affordances-runtime/_index.md
T
pastilhasandClaude Fable 5 6071ded175 todo: one file per item, grouped by theme, with a generated index
TODO.md had reached 1050 lines and 72 open items across 28 sections, read mostly
on a phone. It is now a directory: todo/<theme>/<item>.md, one card per file, with
frontmatter carrying id, status, created date and order, and the prose body copied
across verbatim. TODO.md becomes a generated index (bun run todo:index).

Dates: where an item's prose stated one it is used; otherwise its old section's;
otherwise 2026-08-20, floored at the day before the split rather than invented.
date_source records which.

Shaped for a two-way Kanban board, filed as todo/tooling — a card is a file, id is
the identity, frontmatter is the machine's half and the body is the owner's.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-21 05:42:39 +00:00

50 lines
3.4 KiB
Markdown

---
theme: affordances-runtime
heading: "Affordances (2026-08-19)"
kind: theme
---
Agreed and written down in `plugins/CLAUDE.md`; **nothing is built**. An affordance is what the surface
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).
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 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~~ — built 2026-08-19 (`affordances/publish.ts`). An `affordances/` directory in
the plugin repository is copied to `capabilities/affordances/<publisher>/<plugin>/` on install and
removed on uninstall. Gitea is the first to ship one.
- **The other categories.** A plugin can contribute affordances and nothing else. Skills, tools, tasks
and processes are the obvious next ones and were deliberately left out — one mechanism, proven, before
five. When they land, `publishAffordances` generalises rather than gaining siblings.
- **`officer/core` and `officerdev/<plugin>` are both correct — do not normalise them.** The path uses
`publisher` verbatim, so a plugin published by officerdev lands under `officerdev/`. `officer/core` is
not a plugin at all: it is the PLATFORM's own, shipped by the setup script from the capabilities
repository. Two spellings marking two different things, arrived at by accident and kept on purpose.
Anyone tidying this into one name is removing the distinction, not the inconsistency.
Open, and deliberately deferred:
- **Scoping.** Everything is loaded into every conversation. Fine at two files, not at twenty. A
`surface` field was drafted and dropped because it was doing two jobs at once: for image rendering it
means _what the client renders_ (a phone, a web bubble, a cron run with nobody watching), for gitea
_what tooling exists on this machine_. Those are different axes and one field cannot be both. Decide
the axis before adding the field — and note this codebase has already been bitten by a field that
validated itself and was never consumed (`unauthenticatedPaths`, see `plugins/public-paths.ts`).
- **Drift.** The rule is "declared where implemented", but a hand-authored markdown file in a repository
is not that — `ChatImage` could stop rendering images and no file would notice. Accepted for now
because prose that reaches every harness beats a registry that reaches one; revisit if it bites.