clikae Docs

Issue #25 — agy Parallel Execution on Same Account (Design Note)

Status: Design Discussion, Unresolved. The conclusion leans towards "do not implement multi-account parallel execution (proven unfeasible); same-account multi-worker parallel execution is technically possible but has limited value and low priority." Scope: Only concerns agy (Antigravity CLI). Parallel execution for claude / codex is a separate topic. Relevant code: lib/commands/antigravity.sh, lib/targets/antigravity.sh, docs/dogfood-agy-headless.md, HANDOFF.md (OPEN — clikae auto / dropped-parallel-task relay).


1. Problem Restatement

Issue #25 asks: Can clikae support parallel execution of multiple agy workers to fan-out large parallelizable tasks (e.g. translating a dictionary, batch summarization) to speed up execution, while preserving clikae's core value of "burning across tanks without affecting the main budget"?

There are actually two distinct propositions here that must be separated, otherwise we risk mixing up a dead end with a viable path:

  • Proposition A: Multi-account parallel execution — Running multiple agy workers simultaneously, each logged into a different Google account, burning their respective subscription pools. This is intuitively the most attractive option (throughput ×N, quota ×N).
  • Proposition B: Same-account multi-worker execution — Running multiple agy workers simultaneously, all sharing the same account, isolating their states (sessions, working directories, logs) via $HOME, but burning the same subscription pool.

The core conclusion of this note: A is technically impossible on agy (proven unfeasible by testing, do not pursue); B is technically possible but since quota is shared, the benefit of "parallelism" is heavily compromised, making it a low-priority edge case.


2. Why "Multi-Account Parallelism" (Prop A) is Impossible — Technical Root Cause

This is not a guess; it's a conclusion based on local testing and code analysis (see agy-gemini-shared-quota memory, lib/commands/antigravity.sh comments, and lib/targets/antigravity.sh header).

2.1 The root cause in one sentence: agy's authentication is machine-wide global, not bound to $HOME

clikae's standard model for switching accounts is "point each shell to a per-profile config dir" (CLIKAE_CONFIG_DIR, CODEX_HOME, etc.). This requires that the CLI's identity can be redirected by an environment variable or flag. agy does not conform to this:

  • The agy state directory does follow $HOME (os.UserHomeDir~/.gemini), so overriding $HOME isolates sessions, logs, and work states.
  • However, agy's authentication (Google OAuth) is stored in a single machine-wide macOS Keychain item (service gemini, account antigravity), not in ~/.gemini. No environment variable (like ANTIGRAVITY_EXECUTABLE_DATA_DIR, GEMINI_HOME) or flag can redirect this login.

Proven conclusion (must be documented, as we once mistakenly thought it could run in parallel but had to retract): "Overriding $HOME only moves 'state', not 'auth'; auth is globally single and only recognizes the last logged-in account." So even if you open three shells with different $HOME paths, all three agy processes will read the same Keychain login = the same account.

2.2 How clikae currently handles "multi-account" — Global exclusive switching, not parallel execution

The multi-account mode in lib/commands/antigravity.sh (opt-in, taking over ~/.gemini) works around this limitation by using a global, one-at-a-time approach:

  • Switching a tank = pointing the ~/.gemini symlink to that tank's slot directory, and copying that tank's token from clikae's Keychain namespace back to the canonical Keychain item (_agy_kc_stash / _agy_kc_restore, Keychain-to-Keychain, token never hits disk).
  • Because there is only one canonical Keychain item, this switch is GLOBAL: only one agy tank is active at any time, across all terminal windows. The code even contains a _agy_assert_not_running guard: it refuses to switch the symlink if agy is currently running, to avoid corrupting the active session.

In other words, clikae's official account switching is inherently machine-wide exclusive. This is the opposite of "multi-account parallelism": parallelism requires multiple active canonical logins, but agy only gives you one slot.

2.3 Conclusion: Prop A is a dead end

  • Opening multiple agy processes with different $HOME paths → all use the same account (globally single auth).
  • Using different accounts → requires rewriting that single Keychain login in turns → inherently sequential, not parallel.
  • Therefore, multi-account parallelism for agy is impossible under its current design. Do not pursue this; we can only wait for Google to officially support per-profile auth.

Comparison: claude and codex can run in parallel across multiple accounts (each shell has its own CLIKAE_CONFIG_DIR or CODEX_HOME; the "burn-all-tanks" mode in HANDOFF.md is true parallel execution for claude and codex). agy is the only engine blocked by this limitation.


3. "Same-Account Multi-Worker" (Prop B) Feasibility Analysis

Since multi-account parallelism is out, the only parallelism left is B: the same account, multiple workers, isolating state with $HOME, sharing the same quota.

3.1 Technical Feasibility — possible, but requires bypassing clikae's existing guards

  • State isolation is fine: $HOME override gives each worker its own ~/.gemini (sessions, work directories, and logs are isolated). One worker going off-track or hang-killing doesn't affect the others. Technically sound.
  • Shared login is fine but fragile: Multiple workers read the same canonical Keychain login. Reading the token is parallelizable (read-only), so in theory, N agy instances can run concurrently using the same account.
  • ⚠️ Bypassing the global model: clikae's multi-account mode treats ~/.gemini as a single symlink and _agy_assert_not_running explicitly refuses to touch the symlink when agy is running. To implement B, we must not use the existing symlink-swap mechanism, and instead create a separate path where we launch agy directly using HOME=... without touching the ~/.gemini symlink. This means B and the existing multi-account mode are two entirely separate paths and cannot be mixed.
  • Authentication prerequisite: The account used by the workers must be the one currently in the canonical Keychain item (the last logged-in one). You cannot use two different accounts simultaneously in B. So the prerequisite for B is "switch to the desired tank first using clikae, verify the login, and then fan-out N same-account workers."

3.2 Consequences of Shared Quota — The fatal drawback of B

The N workers in B burn the same subscription pool. Consequently:

  • No quota multiplier. The advantage of multi-account parallelism (multiplying quota by N) does not exist in B. You are just burning the same pool of fuel faster.
  • Faster rate limiting. The dry rate of agy requests is already very fast (dry-run tests show 23–25k tokens per request). Running N workers in parallel means the pool is drained at N× speed, hitting RESOURCE_EXHAUSTED (429) Individual quota reached much sooner (the dry signal for agy is found in ~/.gemini/antigravity-cli/cli.log as an E-level line).
  • Quota is bound to the account, not the process. So parallel execution doesn't give you more total capacity, it just hits the wall faster, with the added complexity of coordinating multiple workers hitting limits at the same time.

In short: B trades "wall-clock throughput" for "earlier rate limiting + coordination complexity", without gaining any additional quota.

3.3 Does B have any value?

Yes, but very narrow, and mostly replaceable by simpler solutions:

  • The only real benefit is wall-clock reduction: If the task is naturally partitionable (N independent pieces) and the quota pool is large enough to not dry out, N parallel workers are indeed faster than sequential execution. The agy spawning 5 subagents in parallel for translation in docs/dogfood-agy-headless.md fits this shape—but note that this was internal subagent spawning within agy, and clikae didn't need to manage separate $HOME paths.
  • Counterexamples (where B's value is negated):
    • Small tasks → Sequential is fast enough; not worth the overhead of fan-out.
    • Large tasks that dry out the account → Parallel execution just hits the limit faster, leaving you to coordinate a "half-finished, dried-out task" (like the mid-run dry task in the HANDOFF dogfood).
    • Offloading main thread budget → This is achieved by sequential offloading (already proven in dogfood), no parallelism required.

4. Recommendations

4.1 Multi-Account Parallelism (Prop A): Do not implement. Mark as known unfeasible.

  • Explicitly document in agy docs (docs/) and issue #25: agy cannot do multi-account parallel execution due to the technical root cause: "auth is globally single-account and does not follow $HOME". This prevents future agents or humans from trying to reinvent this dead end.
  • Mark the agy row in clikae tanks / board as non-parallel / global-single (which also resolves the mismatch between env agy and tanks list noted in HANDOFF § "Grunt-dispatch friction").

4.2 Same-Account Multi-Worker (Prop B): Do not implement by default; consider only under strict conditions, low priority.

Do not recommend implementing this now. Reasons: narrow value (only saves wall-clock), no quota multiplier, requires bypassing the symlink model, and adds complex rate-limit coordination—poor ROI.

If implemented in the future, satisfy all prerequisites first:

  1. A frequently occurring real-world task shape that is large, partitionable, and has enough quota pool. Otherwise, it is over-engineering.
  2. The task must be idempotent + verified by artifact (so a dropped piece can be safely re-fired, as concluded in HANDOFF).
  3. Implement agy dry detection (parsing 429 lines in cli.log) to halt all workers when the account hits a rate limit, preventing useless retries (note: agy -p exits with 0 and empty output on limit, so exit code cannot be trusted).
  4. Design it as completely independent of the existing multi-account symlink model to avoid polluting the _agy_* global switching logic.

4.3 Higher-Priority Alternatives (better ROI than B)

  • In-process subagents for agy: For parallel translations/batches, let the single agy agent spawn subagents internally (dogfood proved 5-way parallelism works), so clikae doesn't need to manage $HOME. Spend effort on writing clean headless recipes (non-exploratory, tight boundaries, long timeout) instead of multi-worker orchestration.
  • Cross-engine parallelism: To get true fan-out + quota multipliers, distribute parallel chunks across multiple claude/codex tanks (which support true multi-account parallelism). agy can act as a single sequential worker in that pool (matching the global-single role it played in the HANDOFF run).

5. If B is to be implemented — Future Reference Sketch

Provided for future reference; do not implement now.

5.1 Shape

Add an independent subcommand (do not bundle with cmd_antigravity's symlink flow), e.g. clikae agy --fan-out N -- <task-template>, or delegate to the burn/pool layer.

# Conceptual sketch (non-executable): N workers on the same account, separate $HOME, sharing quota.
# Prerequisite: User has already switched the canonical login to the target account via `clikae agy <tank>`.

_agy_fanout() {
  local n="$1"; shift            # number of workers
  local task_dir="$1"; shift     # directory for in/out artifacts (use /tmp to avoid iCloud sync lag)

  # ⚠️ Do not touch the ~/.gemini symlink or call _agy_kc_stash/_agy_kc_restore.
  # Each worker gets a temporary HOME to isolate state; auth reads the global canonical Keychain.
  for i in $(seq 1 "$n"); do
    local whome; whome="$(mktemp -d)"
    # Setup authentication for the worker:
    #   macOS: Auth is in the global Keychain (machine-wide), worker reads it natively -> no copy needed.
    #   Linux: Auth is in ~/.gemini file -> copy the active ~/.gemini to $whome/.gemini.
    (
      HOME="$whome" \
      agy -i "$(render_task "$i")" \
          --dangerously-skip-permissions --add-dir "$task_dir" \
          --print-timeout 900s \
        > "$task_dir/worker-$i.out" 2>&1 &
    )
  done
  wait

  # Post-run: verify artifact for each worker; if missing, inspect its out log / global cli.log for 429.
  # On rate limit (exit 0 but empty output / RESOURCE_EXHAUSTED in log) -> mark the pool dry, report failed chunks to orchestrator.
}

5.2 Critical Implementation Details

  • macOS vs Linux Auth Behavior: macOS auth is in the global Keychain, shared natively across N workers; Linux auth is in the ~/.gemini file, requiring each $HOME worker to copy the active auth file first (sharing it read-only—never log in concurrently from workers to avoid race conditions).
  • Avoid reusing _agy_assert_not_running guard: The guard is "refuse to touch symlink if agy is running." Since B does not touch the symlink, it doesn't conflict—but it means B cannot run concurrently with a normal interactive agy session (as they would conflict on the canonical login's session state). Document this constraint: the account is exclusively locked for fan-out.
  • Dry Detection: tail -F the cli.log of each worker (located under each worker's $HOME) because agy redirects its log per run.
  • Artifact-first: Workers must write to files rather than stdout (dogfood lesson: large stdout under agy -p can hang). Use /tmp to avoid iCloud sync latency.
  • Constraint exploration + long timeout: Prompt must use tight boundaries ("do not explore git, just translate") otherwise agy's agentic behavior will run off-topic and hit --print-timeout.

6. Summary

agy multi-account parallelism = dead end (global single auth), do not pursue. agy same-account multi-worker = technically possible but trades earlier rate limiting and coordination complexity for wall-clock gains, without adding total quota; do not implement by default. For parallel execution, use agy's internal subagents, or distribute tasks across multi-account claude/codex tanks.