Open
Conversation
Claude Code v2.1+ changed the input prompt character from "> " to "❯". This was causing refinery and other agents to timeout during startup when waiting for the runtime prompt to appear. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
kingb
pushed a commit
that referenced
this pull request
Feb 18, 2026
Three fixes to eliminate stale hooks from dead polecats blocking re-sling: 1. sling.go: When a bead is already hooked, check if the assignee tmux session is dead. If dead, auto-force the re-sling instead of requiring --force. This eliminates the #1 friction in convoy feeding. 2. stale_hooks.go: Check session liveness for ALL hooked beads regardless of age. Previously only checked beads older than MaxAge (1h). Now unhooks immediately when the agent session is confirmed dead. 3. manager.go: Kill any lingering tmux session for an allocated name before returning it, preventing session already running errors when reusing names from dead polecats. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
kingb
pushed a commit
that referenced
this pull request
Mar 5, 2026
steveyegge#2075) * feat(quota): context-preserving rotation via keychain token swap Replace config-dir-switching with macOS Keychain token swapping. Instead of changing CLAUDE_CONFIG_DIR (which breaks /resume), swap the OAuth token from an available account into the rate-limited account's keychain entry and respawn with the same config dir. This lets /resume naturally find previous session transcripts. - Add keychain.go: read/write/swap macOS Keychain credentials - Add keychain_stub.go: no-op stubs for non-darwin builds - Add config dir grouping: one keychain swap per shared config dir - Add interactive /resume after respawn for context recovery - Add backup/rollback support for keychain operations - Persist detected rate-limit statuses during rotate (#1) - Return structured JSON in all rotate exit branches (steveyegge#2) - Tighten default scan patterns to avoid false positives (steveyegge#4) - Consolidate ExpandHome duplicates via util.ExpandHome (steveyegge#6) - Add state persistence and dry-run plan tests (steveyegge#7) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(quota): add --from and --idle flags for preemptive rotation Add preemptive rotation support so sessions can be swapped to a fresh account before hitting rate limits, targeting only idle agents to avoid disrupting active work. - --from <handle>: rotate all sessions using the specified account regardless of rate-limit status - --idle: only rotate sessions at the idle prompt (❯), skip busy agents - Add Tmux.IsIdle() for point-in-time idle detection - Add PlanRotation fromAccount parameter for account-based targeting - Skip marking accounts as "limited" for preemptive rotations Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(quota): --continue flag was never injected into restart command strings.Replace looked for "exec claude " but runtimeCmd doesn't contain "exec" — that prefix is added later in the Sprintf. The replacement silently matched nothing, so agents always started fresh sessions instead of resuming. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(quota): swap oauthAccount identity + fix misleading unassignable message Two fixes: 1. Keychain swap was only changing the auth token but not the account identity stored in .claude.json's oauthAccount field. Claude Code sends accountUuid/organizationUuid with API requests, so the server still rate-limited based on the original account. Now SwapOAuthAccount also copies the source's oauthAccount into the target's .claude.json. 2. "not enough available accounts" message was shown for sessions with unknown accounts (scanner couldn't resolve their CLAUDE_CONFIG_DIR). Now distinguishes between "account unknown" and genuinely having no available accounts to rotate to. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(quota): scanner false positives from stale rate-limit messages Only check the bottom 10 lines of captured pane output for rate-limit patterns instead of all 30. When a rate limit is resolved (e.g., /login), subsequent output pushes the message up and out of the check window. Previously, old rate-limit messages anywhere in the 30-line capture caused false positives on sessions that had already recovered. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(quota): rotate unknown-account sessions using their CLAUDE_CONFIG_DIR Sessions with unresolvable accounts (e.g., la-witness) were skipped entirely during rotation. Now the scanner captures CLAUDE_CONFIG_DIR for every session, and PlanRotation includes unknown-account sessions in the plan as long as they have a config dir to swap. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(quota): use ~/.claude default when CLAUDE_CONFIG_DIR not set Sessions using Claude Code's default config dir (no CLAUDE_CONFIG_DIR env var) had empty ConfigDir, making them unrotatable. Now falls back to ~/.claude so the keychain swap can target the correct entry. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(quota): fall back to ~/.claude in executeKeychainRotation too The scanner had the ~/.claude fallback but executeKeychainRotation still failed when CLAUDE_CONFIG_DIR wasn't set in the tmux environment. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(quota): skip oauthAccount swap when .claude.json doesn't exist Default config dir (~/.claude) may not have .claude.json. The keychain token is what authenticates; oauthAccount is just cached identity metadata. Skip silently instead of warning. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(quota): add SwapOAuthAccount stub for non-darwin builds Linux/Windows CI failed because SwapOAuthAccount and RestoreOAuthAccount are defined in keychain.go (darwin-only). Added stubs to keychain_stub.go. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(quota): detect OAuth token revoked/expired in scanner After keychain swap, the old account's token may be revoked or expire. Added patterns for "OAuth token revoked" and "OAuth token has expired" so these sessions get picked up by gt quota rotate. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(quota): validate source tokens before keychain swap Prevents rotating to accounts with expired or revoked OAuth tokens. ValidateKeychainToken checks token validity via JSON expiry, JWT exp claim, or lightweight HTTP probe before the swap occurs. Accounts with bad tokens are skipped with instructions to run /login. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(quota): don't mark from-account as limited during preemptive rotation --from targets sessions for preemptive rotation but the account isn't actually rate-limited. Marking it limited in state removed it from the available pool and prevented rotation back to it later. Also fixes misleading "rate-limited" message when using --from, and surfaces skipped accounts (invalid tokens) when no accounts are available. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(quota): exclude from-account from available pool in preemptive rotation When using --from, the source account should never be a candidate for rotation targets. Previously it appeared in the available pool, got validated, and showed as "skipped" — confusing output. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(quota): remove HTTP token validation that rejects valid OAuth tokens Claude Code uses OAuth tokens that authenticate through a different flow than Bearer tokens against the Anthropic API. Sending them as Bearer to api.anthropic.com/v1/messages always returns 401, causing every account to be skipped as "token rejected by API" even after fresh login. Strategies 1 (JSON expires_at) and 2 (JWT exp) still validate expiry when the token format supports it. For opaque tokens, assume valid if present in keychain. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(quota): widen scanner check window to full capture (30 lines) The 10-line check window was too aggressive — rate-limit messages scrolled out of view when agents continued working (running bash commands, /login, etc.), causing rotate to miss sessions that scan had detected moments earlier. The tightened patterns (e.g., "limit · resets" instead of bare "resets") already prevent false positives from stale messages, so the reduced window is no longer needed. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(quota): don't let scan detections poison available account pool Two bugs fixed: 1. PlanRotation marked accounts as limited in-memory based on scan results before computing available accounts. Stale sessions (e.g., parked rigs with old rate-limit messages in the pane) would mark their accounts as limited, shrinking the pool and blocking rotation of sessions that actually need it. Removed in-memory state update — available accounts now come from persisted state only. 2. Assignment loop didn't re-read candidate after skipping same-account. After `availIdx++` to skip, `candidate` still held the old value, so a limited session could be assigned back to its own account. This was masked by bug #1 (the account was removed from the pool). Also removed the caller's automatic state persistence from scan detections — account state should only be updated after confirmed rotation execution, not from potentially stale scan results. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
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.
Summary
ready_prompt_prefixfor Claude provider from"> "to"❯"Problem
Agents (refinery, witness, polecats) were timing out during startup with:
The gastown startup detection was looking for
"> "but Claude Code now displays"❯"as its prompt indicator.Solution
Simple one-line fix to update the default prompt prefix. Users on older Claude Code versions can override via
settings/config.jsonif needed.Test plan
🤖 Generated with Claude Code