Skip to content

[REFACTOR] the advertised surface is the implemented surface - #1

Merged
gradyzhuo merged 2 commits into
mainfrom
refactor/surface-matches-implemented
Sep 30, 2026
Merged

gradyzhuo merged 2 commits into
mainfrom
refactor/surface-matches-implemented

Conversation

@gradyzhuo

@gradyzhuo gradyzhuo commented Sep 25, 2026 •

Copy link
Copy Markdown
Member

The method set now contains only what orrery actually projects: tool/describe, tool/listIdentities, tool/showIdentity.

Why

AIToolStateTransfer was designed, tested and documented — and orrery never called it. ToolCapability.stateTransfer has zero call sites in orrery's Sources, while initialize advertised tool/copyLoginState and tool/copyNonLoginSettings to every plugin author who asked what they could implement.

A capability is a promise that the host calls it. Advertising one that nothing redeems means someone implements those two methods, ships them, and is never invoked.

The protocol version does not move. A host that never called these cannot notice, and a plugin that implemented them was already talking to nobody.

This is not "orrery does not need state transfer." orrery copies login state on every account setup — ToolSetupRunner calls ToolFlow.copyLoginState / copyNonLoginSettings, with real implementations in ClaudeFlow, CodexFlow and GeminiFlow. That feature exists and works; it is simply not projected onto the plugin boundary. The method set is the projection of what orrery routes through a plugin, and state transfer is not routed that way yet. When it is, the method comes back — with a call site.

The README was wrong in both directions

This is the more useful half of the change.

  • It documented tool/accountInfo and tool/credentialCopy — neither ever existed under those names.
  • It drew accountInfo as a member of AITool, where identity has never lived. That separation is deliberate: a tool that cannot answer must be visibly non-conforming rather than silently returning nothing.
  • Meanwhile AIToolIdentityReporting and LoginIdentity — the two types actually in the surface — appeared in no diagram at all.

Both diagrams now name what the code names.

Reads degrade, writes abort keeps its section and loses its example. The rule decides the shape of the first write that gets added, so deleting it would throw away the reasoning; illustrating it with a method that does not exist is the thing being removed. It now states plainly that every method in the surface today is a read.

Drift protection

The old capabilities test asserted tool/describe was present, which no addition could ever fail. A new test pins the advertised set whole:

#expect(Set(caps.keys) == [
    "tool/describe",
    "tool/listIdentities",
    "tool/showIdentity",
])

Verified by temporarily adding a method and watching it fail, then removing it — a test that cannot fail is decoration.

Verification

74 tests pass. Net −409 lines.

Downstream

orrery pins exact: "0.0.1-dev.9", which still has the protocol, so nothing breaks until the pin moves. Once this lands and a 0.0.1-dev.10 tag exists, orrery's own dead plumbing comes out in a separate PR: RemoteAITool.stateTransfer, its initialize capability check, and ToolCapability.stateTransfer. That must land after orrery 3.5.5, which is mid-release and pins dev.9.

🤖 Generated with Claude Code

`AIToolStateTransfer` was designed, tested and documented, and orrery never
called it: `ToolCapability.stateTransfer` has zero call sites in orrery's
Sources, while `initialize` advertised `tool/copyLoginState` and
`tool/copyNonLoginSettings` to every plugin author who asked. A capability is a
promise that the host calls it. Advertising one nothing redeems means someone
implements those two methods, ships them, and is never invoked.

So the method set now contains only what orrery projects today: `tool/describe`,
`tool/listIdentities`, `tool/showIdentity`. The protocol version does not move —
a host that never called these cannot notice, and a plugin that implemented them
was already talking to nobody.

The README was wrong in both directions, which is the more useful half of this.
It documented `tool/accountInfo` and `tool/credentialCopy`, neither of which ever
existed under those names, and drew `accountInfo` as a member of `AITool` — where
identity has never lived, precisely so that a tool which cannot answer can be
seen not to conform. Meanwhile `AIToolIdentityReporting` and `LoginIdentity`, the
two types actually in the surface, appeared in no diagram at all. Both diagrams
now name what the code names.

`Reads degrade, writes abort` keeps its section and loses its example. The rule
decides the shape of the first write that gets added, so deleting it would throw
away the reasoning; illustrating it with a method that does not exist is the
thing being removed. It now says plainly that every method in the surface is a
read.

A test pins the advertised set whole rather than by presence. The old one
asserted `tool/describe` was there, which no addition could ever fail. Verified
by adding a method and watching it fail, then removing it again.
The exactness test added here uses an identity-only fixture, so it cannot see
the account methods at all — a seventh could be added and nothing would fail.
This pins the set an account-owning tool advertises.

Verified it discriminates: adding a capability fails it immediately. A test that
cannot fail is decoration, and this one exists precisely so that adding a method
means updating a test, which is where documenting it gets remembered.
@gradyzhuo
gradyzhuo force-pushed the refactor/surface-matches-implemented branch from 8bc402a to eeea146 Compare September 29, 2026 07:29
@gradyzhuo
gradyzhuo merged commit 13850ab into main Sep 30, 2026
2 checks passed
@gradyzhuo
gradyzhuo deleted the refactor/surface-matches-implemented branch September 30, 2026 01:28
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