# 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.

```sh
# 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.**
