# cli: add multi-cell fan-out helpers (groupReposByCell, fanOutCells)

`6055b38`→[main](/content/gh/entireio/cli/commits/main/index.html)·

Soph·1w ago·4 files·+457 added/-0 removed

The data plane has no server-side cross-cell aggregator: a query over all
of the caller's repos must be fanned out to each cell hosting them and
merged client-side (the entire.io BFF's code-search pattern). PR #1616
inlines that orchestration into search_cmd.go; this extracts the generic
layer so code search — and any later repo-set command — shares one
implementation:

- groupReposByCell: repo index → per-cell groups, deterministic order.
- resolveCellBaseURLs: cluster-catalog join on ClusterSlug↔Cluster.Slug.
The catalog exposes no cell field, so joining the cell name against
Slug (as #1616 currently does) only works when the two coincide.
- fanOutCells: parallel per-cell calls under a per-cell timeout, one
shared auth.CellClientFactory (subject resolved once, one identity
token per jurisdiction), partial failures isolated per result slot.
Merge semantics stay with the command.

Also documents the three cell-routing shapes in CLAUDE.md.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

## Sessions

842e421e74a8View transcript

## Changes

4

- MCLAUDE.md+29

- cmd/entire/cli
  
  - Acell_fanout.go+199
  
  - Acell_fanout_test.go+227
  
  - Mcell_target_test.go+2

```
505 unmodified lines

### Entire-API Cell Routing (which cell does a data-plane request go to?)

The data plane (entire-api) is deployed per jurisdiction; a repo placement
lives in exactly one cell, user `/me/*` activity is consolidated in the
caller's home cell, and no server-side cross-cell aggregator exists. The CLI
therefore has exactly three routing shapes, mirroring the entire.io BFF:

- **Repo-scoped → one cell**: `resolveRepoCellTarget` (`cell_target.go`) maps
a repo (ULID or owner/repo) to the cell hosting it via mirrors + the cluster
catalog. Best-effort: any failure returns nil and the auth layer falls back
to home-jurisdiction routing. Used by experts
(`NewAuthenticatedEntireAPICellClient` in `api_client.go`).
- **User-scoped `/me` → home cell, never fan out**:
  `auth.NewEntireAPICellClient(ctx, insecure, nil)` routes by the
  `home_jurisdiction` JWT claim; activity/recap use it with a data-API
  fallback (`runAuthenticatedActivityAPI` in `entireapi_client.go`).
- **Repo-set queries → fan out and merge client-side**: `cell_fanout.go` —
  `groupReposByCell` (repo index → per-cell groups; the catalog join key is
  `ClusterSlug`↔`Cluster.Slug`, NOT the cell name, which the catalog does not
  expose), `resolveCellBaseURLs`, and `fanOutCells` (parallel per-cell calls,
  per-cell timeout, partial failures isolated per slot). Merge semantics stay
  with the command.

Token rule: identity tokens are **per-jurisdiction, not per-cell**. Multi-cell
callers must build one `auth.CellClientFactory`
(`NewEntireAPICellClientFactory`) per operation — it resolves the login
subject once and mints at most one token per jurisdiction. `fanOutCells` does
this automatically; do not call `NewEntireAPICellClient` in a loop.

### Session Strategy (`cmd/entire/cli/strategy/`)

The CLI uses a manual-commit strategy for managing session data and checkpoints. The strategy implements the `Strategy` interface defined in `strategy.go`.
