Repository navigation
what orrery installed, written down - #75
Merged
Merged
Conversation
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The prerequisite for deleting the
Toolenum, which is why it comes first.PluginDiscovery.locate(toolID:)is a lookup — give it an id, it findsorrery-<id>. The only thing supplying ids today isTool.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/toolsor onPATHto 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
initializealready 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.
isAvailablestats 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
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
isAvailabletotruefails 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,Accountlives in the framework, theToolenum goes, gemini is dropped deliberately for now, andorrery-magiis refactored afterwards rather than in step. It supersedes three sections of2026-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