Skip to content

orrery stages a login and the account adopts it - #77

Open
gradyzhuo wants to merge 1 commit into
mainfrom
feat/claude-adopts-its-login
Open

gradyzhuo wants to merge 1 commit into
mainfrom
feat/claude-adopts-its-login

Conversation

@gradyzhuo

@gradyzhuo gradyzhuo commented Sep 30, 2026 •

Copy link
Copy Markdown
Member

orrery add --claude works end to end again. It stopped short after accounts moved to the plugin, because AccountLoginFlow needs orrery's own Account — staging dirs, a Keychain item name — and none of that exists for an account the plugin owns.

The split falls where the knowledge does

stageLogin runs the tool's login against a throwaway directory. Giving a subprocess a terminal is the host's job, and the tool-specific inputs — the login command, the variable pointing at a config directory — come off the tool's own description. orrery needs to know nothing else to do it. Then it hands the directory over and asks nothing.

ClaudeCredentials is the other half, and it is claude knowledge that never was a path:

macOS the credential is a keychain item, under a service name derived from the config directory. The directory only names it — a host copying files would move nothing
elsewhere .credentials.json inside the directory
both .claude.json travels too: an account holding a working credential that cannot say whose it is lists with a blank identity and looks broken

The keychain primitives live in the plugin now rather than being borrowed from the host. The host having them was the arrangement this replaces — it had to know claude's service-name derivation to use them, which is exactly the knowledge that belongs on the other side.

A failed login leaves the account

Created and not logged in is a state orrery list shows honestly, and the person can retry without losing the name they chose. Deleting it behind them would be worse. The staging directory is removed either way, including on failure — it holds a credential.

Dead plumbing goes with dev.12

AIToolStateTransfer was removed upstream, so orrery's side of it goes: RemoteStateTransfer, the capability optional, and ToolCapability.stateTransfer, which never had a call site.

ToolCapabilityTests kept its subject — how a capability is reconciled across the boundary — and changed its example from state transfer to accounts. It gained one case: a plugin advertising six of the seven account methods does not have the capability, because a pool that can be filled and never emptied is worse than no pool.

Verification

671 tests pass.

Checked on a Linux installation as well, driving the plugin over its own protocol with a hand-made staging directory — orrery add launches the tool interactively and cannot run unattended, but the half worth testing is the one that moves the credential.

Before adopting account config dir empty; orrery list shows work with no identity
tool/adoptLogin {"result":{}}
After dir holds .claude.json and .credentials.json; list shows work adopted@example.invalid
After deleting the staging dir identity still there — the account does not depend on it
Adopting from an empty directory fails: no claude login was found in /tmp/…
After that failure the existing login is intact

The last two rows are the point: a login that did not arrive is never reported as done, and a failed attempt does not damage the one already there.

🤖 Generated with Claude Code

`orrery add --claude` works end to end again. It stopped short after accounts
moved to the plugin, because `AccountLoginFlow` needs orrery's own `Account` —
staging dirs, a Keychain item name — and none of that exists for an account the
plugin owns.

The split falls where the knowledge does. `stageLogin` runs the tool's login
against a throwaway directory: giving a subprocess a terminal is the host's job,
and the tool-specific inputs come off the tool's own description, so orrery needs
to know nothing else to do it. Then it hands the directory over and asks nothing.

`ClaudeCredentials` is the other half, and it is claude knowledge that never was
a path. On macOS the credential is a keychain item under a service name derived
from the config directory — the directory only names it, and a host copying files
would move nothing. Elsewhere it is `.credentials.json` inside. `.claude.json`
travels either way: an account holding a working credential that cannot say whose
it is lists with a blank identity and looks broken.

The keychain primitives live here now rather than being borrowed from the host.
The host having them was the arrangement this replaces — it had to know claude's
service-name derivation to use them, which is exactly the knowledge that belongs
on this side.

A failed login leaves the account in place. Created and not logged in is a state
`orrery list` shows honestly, and the person can retry without losing the name
they chose; deleting it behind them would be worse. The staging directory is
removed either way, including on failure — it holds a credential.

dev.12 removed `AIToolStateTransfer`, so orrery's dead plumbing for it goes:
`RemoteStateTransfer`, the capability optional, and `ToolCapability.stateTransfer`,
which never had a call site. `ToolCapabilityTests` kept its subject — how a
capability is reconciled across the boundary — and changed its example to
accounts. It gained one: a plugin advertising six of the seven account methods
does not have the capability, because a pool that can be filled and never emptied
is worse than none.

671 tests pass.

This branch has not been deployed

No deployments
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