Skip to content

what orrery installed, written down - #75

Merged
gradyzhuo merged 2 commits into
mainfrom
feat/plugin-registry-config
Sep 30, 2026
Merged

gradyzhuo merged 2 commits into
mainfrom
feat/plugin-registry-config

Conversation

@gradyzhuo

Copy link
Copy Markdown
Member

The prerequisite for deleting the Tool enum, which is why it comes first.

PluginDiscovery.locate(toolID:) is a lookup — give it an id, it finds orrery-<id>. The only thing supplying ids today is Tool.allCases, so the enum cannot go until something else answers which tools exist. This is that something.

Not a scan

orrery does not look in $ORRERY_HOME/tools or on PATH to see what might be there. It installs a plugin, runs its registration, and writes the result down.

A scan would pay for discovery on every invocation and — the real objection — would make "what is installed" an inference from the filesystem instead of something orrery decided. A binary in the tools directory that was never registered is not a plugin; it is a file, and a test says so.

Capabilities, not presence

initialize already advertises per method, so an entry records "claude is installed and can do these things", not merely that it exists.

That settles a question that looked open: what must a plugin implement to be manageable at all? Nothing in particular. It implements what it can, the rest is recorded as unavailable, and orrery can answer can("tool/addAccount") without spawning anything.

A vanished binary is marked, never forgotten

"Never installed" and "installed, then something removed it" need different fixes — the first needs installing, the second means an interrupted upgrade or a half-applied uninstall. Deleting the entry collapses them into a message nobody can act on.

The capability set stays recorded too: the binary went, the fact of what it could do did not.

isAvailable stats on read rather than storing a flag, because a stored flag is a second copy of something the filesystem already knows and goes stale the moment anything touches the file.

Two failure modes chosen deliberately

Config missing empty list — a fresh install has registered nothing, and that is not an error
Config present but unparseable throws

Empty means fresh install. Reading a damaged config as empty would silently drop every installed tool and hand a fresh-install experience to someone whose install is merely broken.

Verification

10 new tests; full suite green (42 + 654 + 45).

The availability test was checked for discrimination — hardcoding isAvailable to true fails the vanished-binary case. A test that cannot fail is decoration.

Also included

The design this follows from, as docs/superpowers/specs/2026-09-25-plugins-own-accounts.md: plugins own accounts, Account lives in the framework, the Tool enum goes, gemini is dropped deliberately for now, and orrery-magi is refactored afterwards rather than in step. It supersedes three sections of 2026-09-18-plugin-model.md, including the rule that orrery's inventions never cross the boundary — that rule is withdrawn for accounts.

🤖 Generated with Claude Code

The prerequisite for deleting the `Tool` enum, which is why it comes first.

`PluginDiscovery.locate(toolID:)` is a lookup: give it an id and it finds
`orrery-<id>`. The only thing that supplies ids today is `Tool.allCases`, so the
enum cannot go until something else answers "which tools exist". This is that
something.

Not a scan, and that is the design rather than an optimisation. orrery does not
look in `$ORRERY_HOME/tools` or on `PATH` to see what might be there: it installs
a plugin, runs its registration, and writes the result down. A scan would pay for
discovery every invocation and — the real objection — would make "what is
installed" an inference from the filesystem instead of something orrery decided.
A binary in the tools directory that was never registered is not a plugin; it is
a file, and a test says so.

What gets recorded is capabilities, not presence. `initialize` already advertises
per method, so an entry says "claude is installed and can do these things" rather
than merely that it exists. That settles what a plugin must implement to be
manageable at all: nothing in particular. It implements what it can, the rest is
recorded as unavailable, and orrery can answer without spawning anything.

A vanished binary is marked, never forgotten. "Never installed" and "installed,
then something removed it" need different fixes — the first needs installing, the
second means an interrupted upgrade or a half-applied uninstall — and deleting
the entry would collapse them into a message nobody can act on. The capability
set stays recorded too: the binary went, the fact of what it could do did not.

`isAvailable` stats on read rather than storing a flag. A stored flag is a second
copy of something the filesystem already knows, and goes stale the moment
anything touches the file. Verified the test catches it: hardcoding the property
to `true` fails the vanished-binary case.

A config that exists but cannot be parsed throws instead of reading as empty.
Empty means fresh install, and handing a fresh-install experience to someone
whose install is merely damaged would be the worst available answer.

Includes the design this follows from, as a spec: plugins own accounts, the enum
goes, gemini is dropped deliberately for now, and magi is refactored afterwards
rather than in step.
An earlier draft had workspace kept out of `Account` and held as a table the
plugin keys by workspace. That needs two methods and a second notion of
membership; carried on the account it needs one, and a host wanting a
workspace's accounts filters a listing it already has.

It also records why pinning stays separate from `setCurrent` rather than
scoping the pair: `current(in:)` leaves "which account is current" with no
answer until the caller also says where, and that plain question is the one a
host asks.
@gradyzhuo
gradyzhuo merged commit 38e9073 into main Sep 30, 2026
1 check passed
@gradyzhuo
gradyzhuo deleted the feat/plugin-registry-config branch September 30, 2026 01:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant