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>
3.4 KiB
3.4 KiB
theme, heading, kind
| theme | heading | kind |
|---|---|---|
| affordances-runtime | Affordances (2026-08-19) | 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, withofficer/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
clauderun in a terminal on the host gets nothing either, since it never passes throughclaude-manager. Would need a generated file, which is the thinguser-instance.tsrefuses to write for the owner. Plugin contribution— built 2026-08-19 (affordances/publish.ts). Anaffordances/directory in the plugin repository is copied tocapabilities/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,
publishAffordancesgeneralises rather than gaining siblings. officer/coreandofficerdev/<plugin>are both correct — do not normalise them. The path usespublisherverbatim, so a plugin published by officerdev lands underofficerdev/.officer/coreis 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
surfacefield 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, seeplugins/public-paths.ts). - Drift. The rule is "declared where implemented", but a hand-authored markdown file in a repository
is not that —
ChatImagecould 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.