Summary
Gas City needs an account registry and quota rotation system so that agents can be rotated to a different provider account when one hits rate limits. This is critical for sustained multi-agent operation — without it, a rate-limited account blocks all agents using it until the limit resets.
Gastown implements this across two subsystems: an account registry (gt account) and a quota rotation engine (gt quota). Gas City should provide equivalent infrastructure.
Gastown Reference
Account Registry
Commands: gt account list|add|default|status|switch
Data model:
- Accounts stored in
<town>/mayor/accounts.json
- Each account has:
email, description, config_dir (path to CLAUDE_CONFIG_DIR)
- One account is marked
default
- Account directories live at
~/.claude-accounts/<handle>/
~/.claude is a symlink to the active account's config dir
Resolution priority: GT_ACCOUNT env > --account flag > default from config > fallback
Session wiring: The resolved account's config_dir is set as CLAUDE_CONFIG_DIR in the tmux session environment before the agent binary launches.
Quota State
File: <town>/mayor/quota.json (flock-protected)
Per-account state:
status: available | limited | cooldown
limited_at: RFC3339 timestamp
resets_at: human-readable reset time (parsed from provider output)
last_used: RFC3339 timestamp (for LRU assignment)
Rate-Limit Detection
Pattern-based scanning of tmux pane content (last 30 lines):
You've hit your limit
resets \d+[:\d]*(am|pm)\b
Stop and wait for limit to reset
Add funds to continue with extra usage
Maps session → account handle via CLAUDE_CONFIG_DIR environment variable in the tmux session.
Quota Rotation
Commands: gt quota status|scan|rotate|clear
Rotation algorithm:
- Scan all sessions for rate-limit patterns
- Load quota state (flock held for entire operation)
- Mark detected limited accounts in state
- Get available accounts sorted by LRU (least-recently-used first)
- Assign all limited sessions to best available account
- For each session: resolve new config dir → update tmux env → kill pane → respawn with new
CLAUDE_CONFIG_DIR
- Single atomic save after all rotations
Key design decisions:
- Manual trigger model —
gt quota rotate must be called explicitly (a patrol formula could automate this)
- LRU assignment — all limited sessions drain to the same account (least recently used)
- Atomic execution — flock held for entire rotation, single state save at end
- Session resume support — optional via
SessionLinker callback, falls back to fresh start
Gas City Design Considerations
What maps cleanly
- Account registry → could be a
[accounts] section in city.toml or a separate accounts.toml
gc account list|add|default → straightforward CLI commands
CLAUDE_CONFIG_DIR wiring → already have ConfigDirEnv concept in runtime config
- Quota state →
.gc/quota.json with flock
What needs Gas City adaptation
- Provider-agnostic patterns: Gastown only handles Claude. Gas City supports multiple providers — rate-limit patterns should be configurable per provider preset, not hardcoded
- Config field: Need an
account field on [[agent]] config (and AgentPatch/AgentOverride/pool deep-copy per CLAUDE.md conventions)
- Per-sling account:
gc sling --account <handle> to override which account the spawned agent uses (for quota rotation on dispatch)
- No symlink management: Gas City should set
CLAUDE_CONFIG_DIR per-session, not manage a global symlink. Multiple cities may run concurrently with different accounts
- Controller integration: Rate-limit scanning could be a health patrol check rather than a separate command, fitting the existing health monitoring infrastructure (Level 6 capability)
Progressive activation
- Level 0-1:
account field on agent config, gc account commands, CLAUDE_CONFIG_DIR wiring
- Level 6+: Quota scanning as health check, automatic rotation via patrol formula or order
Acceptance Criteria
References
- Gastown account CLI:
gastown/internal/cmd/account.go
- Gastown account types:
gastown/internal/config/types.go (AccountsConfig, Account)
- Gastown account resolution:
gastown/internal/config/loader.go (ResolveAccountConfigDir)
- Gastown quota engine:
gastown3/internal/quota/ (state, scan, executor, rotate)
- Gastown rate-limit patterns:
gastown3/internal/constants/constants.go
- Gas City feature parity audit: item 2.2
wasteland:
type: feature
priority: 2
effort: large
Summary
Gas City needs an account registry and quota rotation system so that agents can be rotated to a different provider account when one hits rate limits. This is critical for sustained multi-agent operation — without it, a rate-limited account blocks all agents using it until the limit resets.
Gastown implements this across two subsystems: an account registry (
gt account) and a quota rotation engine (gt quota). Gas City should provide equivalent infrastructure.Gastown Reference
Account Registry
Commands:
gt account list|add|default|status|switchData model:
<town>/mayor/accounts.jsonemail,description,config_dir(path toCLAUDE_CONFIG_DIR)default~/.claude-accounts/<handle>/~/.claudeis a symlink to the active account's config dirResolution priority:
GT_ACCOUNTenv >--accountflag > default from config > fallbackSession wiring: The resolved account's
config_diris set asCLAUDE_CONFIG_DIRin the tmux session environment before the agent binary launches.Quota State
File:
<town>/mayor/quota.json(flock-protected)Per-account state:
Rate-Limit Detection
Pattern-based scanning of tmux pane content (last 30 lines):
Maps session → account handle via
CLAUDE_CONFIG_DIRenvironment variable in the tmux session.Quota Rotation
Commands:
gt quota status|scan|rotate|clearRotation algorithm:
CLAUDE_CONFIG_DIRKey design decisions:
gt quota rotatemust be called explicitly (a patrol formula could automate this)SessionLinkercallback, falls back to fresh startGas City Design Considerations
What maps cleanly
[accounts]section incity.tomlor a separateaccounts.tomlgc account list|add|default→ straightforward CLI commandsCLAUDE_CONFIG_DIRwiring → already haveConfigDirEnvconcept in runtime config.gc/quota.jsonwith flockWhat needs Gas City adaptation
accountfield on[[agent]]config (andAgentPatch/AgentOverride/pool deep-copy per CLAUDE.md conventions)gc sling --account <handle>to override which account the spawned agent uses (for quota rotation on dispatch)CLAUDE_CONFIG_DIRper-session, not manage a global symlink. Multiple cities may run concurrently with different accountsProgressive activation
accountfield on agent config,gc accountcommands,CLAUDE_CONFIG_DIRwiringAcceptance Criteria
gc account list|add|defaultcommandsaccountfield on[[agent]]config wired through to sessionCLAUDE_CONFIG_DIR--accountflag ongc slingfor per-dispatch account override.gc/quota.jsonwith flock)gc quota status|scan|rotate|clearcommandsReferences
gastown/internal/cmd/account.gogastown/internal/config/types.go(AccountsConfig,Account)gastown/internal/config/loader.go(ResolveAccountConfigDir)gastown3/internal/quota/(state, scan, executor, rotate)gastown3/internal/constants/constants.go