Context
PR #302 added a fail-closed guard to _gh_wrapper_sync_identity for the case where GH_TOKEN overrides the keyring identity that gh auth switch selects. That guard needs to know which identity the token authenticates as.
It resolves that in two ways:
CLAUDE_GH_TOKEN_LOGIN, if set — the test fixtures set this directly.
- Otherwise, a
gh api user --jq .login call.
In production, CLAUDE_GH_TOKEN_LOGIN is unset, so path 2 is what runs.
The problem
_gh_wrapper_sync_identity runs on every gh invocation. So every gh command now costs one extra network round trip to resolve an identity that does not change within a session.
This was called out as a known follow-up when the guard was written and deliberately left unbuilt, to keep the change to the cheap tier.
Rough shape of a fix
Cache the resolved login for the life of the shell session. The token itself is fixed per session (exported once by claude-wrapper/lib/credentials.sh), so the resolved login cannot drift underneath the cache while the session lives.
Worth deciding as part of the fix:
- Cache in a shell variable versus a file under
${TMPDIR}. A shell variable dies with the session, which matches the token's lifetime exactly; a file needs an invalidation story.
- Whether the cache key should incorporate the token value, so a mid-session
GH_TOKEN change is not served a stale identity. Cheap insurance, since the comparison is what the guard's correctness rests on.
- Failure behavior on a cache miss when the network is down. The guard currently fails closed when it cannot resolve the login, which is the right default — caching must not quietly turn that into a fail-open.
Not in scope
The full per-org token router (CLAUDE_GH_TOKEN_ROUTER) stays deferred. It needs a second PAT for andrewmrich and reverses a documented product decision in claude-wrapper/README.md.
Context
PR #302 added a fail-closed guard to
_gh_wrapper_sync_identityfor the case whereGH_TOKENoverrides the keyring identity thatgh auth switchselects. That guard needs to know which identity the token authenticates as.It resolves that in two ways:
CLAUDE_GH_TOKEN_LOGIN, if set — the test fixtures set this directly.gh api user --jq .logincall.In production,
CLAUDE_GH_TOKEN_LOGINis unset, so path 2 is what runs.The problem
_gh_wrapper_sync_identityruns on everyghinvocation. So everyghcommand now costs one extra network round trip to resolve an identity that does not change within a session.This was called out as a known follow-up when the guard was written and deliberately left unbuilt, to keep the change to the cheap tier.
Rough shape of a fix
Cache the resolved login for the life of the shell session. The token itself is fixed per session (exported once by
claude-wrapper/lib/credentials.sh), so the resolved login cannot drift underneath the cache while the session lives.Worth deciding as part of the fix:
${TMPDIR}. A shell variable dies with the session, which matches the token's lifetime exactly; a file needs an invalidation story.GH_TOKENchange is not served a stale identity. Cheap insurance, since the comparison is what the guard's correctness rests on.Not in scope
The full per-org token router (
CLAUDE_GH_TOKEN_ROUTER) stays deferred. It needs a second PAT forandrewmrichand reverses a documented product decision inclaude-wrapper/README.md.