cli: add code search with multi-region fan-out (ENT-993)#1616
Conversation
There was a problem hiding this comment.
Pull request overview
Adds a new cmd/entire/cli/codesearch package to provide a small, typed Go client for peregrine’s POST /search/api/search endpoint using the existing authenticated api.Client, including response-size capping and basic HTTP error parsing.
Changes:
- Introduces
codesearch.Search()plus request/response types for peregrine code search. - Implements an 8 MiB response read cap with explicit overflow detection.
- Adds unit tests covering success, API error responses, non-JSON error bodies, and oversized responses.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| cmd/entire/cli/codesearch/codesearch.go | New code search client wrapper around api.Client.Post, including response cap and error parsing. |
| cmd/entire/cli/codesearch/codesearch_test.go | Unit tests for the new client (success + error/edge cases). |
| if apiErr.Message == "" && len(body) > 0 { | ||
| apiErr.Message = string(body) | ||
| } |
1e7a2b3 to
0ef9bc1
Compare
Add `entire search --code <query>` to search code content across all jurisdictions via the peregrine search service, gated behind ENTIRE_CODE_SEARCH=1. Architecture: - codesearch package: thin client for peregrine's search endpoint through the cell's entire-api gateway (GET /api/v1/search/api/search) - Multi-region fan-out: list repos from core → group by jurisdiction → search each cell in parallel with per-cell timeouts → merge results by score, deduplicate, cap to --limit - Per-cell repo ID scoping: each cell only receives ULIDs belonging to its jurisdiction, avoiding cross-jurisdiction leakage - Partial failure resilience: a down region warns but returns results from healthy cells; only all-cells-fail is an error Repo filter formats: - gh/owner/repo, et/proj/repo, bare owner/repo, or raw ULID - Defaults to current repo (derived from git origin), matching checkpoint-search behavior; use --all-repos or repo:* for all repos - Multiple filters supported via --repo flag or inline repo: syntax Output: - Text: grep-style (repo:path:line: context), truncated to 200 chars - JSON: full response with stats, repo_stats, and results (--json) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Group fan-out by cell (not jurisdiction), matching BFF pattern - Resolve cell apiUrl from cluster catalog for direct routing - Align resolveRepoFilters with BFF: only strip gh/, try unstripped filter as full_name fallback, match id on raw filter - Use sort.SliceStable with tiebreaker for deterministic output - Route single-cell path through mergeSearchResults for uniform sort/dedup/truncate - TrimSpace error body fallback in codesearch client - Strengthen TestSearch_NonJSONError assertions - Add searchAllCells test seams and 5 integration tests covering cell grouping, cluster URL resolution, partial failure, filter resolution, and single-cell merge - Document case_sensitive as optimistic (peregrine proto lacks it) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1fdef33 to
8ec2ed5
Compare
The cluster catalog is keyed by Cluster.Slug, which corresponds to RepoIndexEntry.ClusterSlug — not RepoIndexEntry.Cell. Using Cell for the lookup would silently miss baseURL resolution when Cell differs from ClusterSlug, falling back to the home-cell URL. - Add clusterSlug field to cellGroup, populated from r.ClusterSlug - Use cells[i].clusterSlug for slugToCluster lookup - Test ResolvesBaseURLFromClusters now uses distinct Cell/ClusterSlug values to prove the lookup path - All test RepoIndexEntry literals updated with ClusterSlug field Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The BFF uses r.cell with a URL template to resolve cell URLs. No other CLI code path does a Cluster.Slug lookup — the experts resolver matches by ClusterHost instead. Revert to using cell for the cluster catalog lookup to mirror the BFF pattern. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
resolveExpertsCellTarget was experts-named but nothing in it is experts-specific: it maps any repo (ULID or owner/repo) to the entire-api cell hosting it, exactly what every future repo-scoped or fan-out command needs (code search is next, PR #1616). Rename the file and identifiers — resolveRepoCellTarget, cellCoreClient, newCellCoreClient, cellResolveTimeout — and keep experts as the documented example consumer. Mechanical rename; no behavior change. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Entire-Checkpoint: 89b4d13bd2b0
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> Entire-Checkpoint: 842e421e74a8
resolveExpertsCellTarget was experts-named but nothing in it is experts-specific: it maps any repo (ULID or owner/repo) to the entire-api cell hosting it, exactly what every future repo-scoped or fan-out command needs (code search is next, PR #1616). Rename the file and identifiers — resolveRepoCellTarget, cellCoreClient, newCellCoreClient, cellResolveTimeout — and keep experts as the documented example consumer. Mechanical rename; no behavior change. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Entire-Checkpoint: 89b4d13bd2b0
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> Entire-Checkpoint: 842e421e74a8
resolveExpertsCellTarget was experts-named but nothing in it is experts-specific: it maps any repo (ULID or owner/repo) to the entire-api cell hosting it, exactly what every future repo-scoped or fan-out command needs (code search is next, PR #1616). Rename the file and identifiers — resolveRepoCellTarget, cellCoreClient, newCellCoreClient, cellResolveTimeout — and keep experts as the documented example consumer. Mechanical rename; no behavior change. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Entire-Checkpoint: 89b4d13bd2b0
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> Entire-Checkpoint: 842e421e74a8
|
Heads-up: the generic multi-cell plumbing this PR inlines in
Suggested shape for a rebase onto #1641: the |
Summary
cmd/entire/cli/codesearch/— client package for peregrine's/search/api/searchendpoint--codeand--case-sensitiveflags intoentire search, gated behindENTIRE_CODE_SEARCH=1--repofilter is specified (routes to repo's owning cell)How it works
--code query(no filter)ListRepos→ group by cell →NewEntireAPICellClientper jurisdiction--code --repo owner/name queryNewAuthenticatedEntireAPICellClient--code repo:owner/name query--code --all-repos queryFiles
cmd/entire/cli/codesearch/codesearch.go— client types andSearch()functioncmd/entire/cli/codesearch/codesearch_test.go— client tests (success, API error, non-JSON error, response too large)cmd/entire/cli/search_cmd.go— command wiring, fan-out orchestration, output formatterscmd/entire/cli/search_cmd_test.go— command tests (gate, flags, output, groupReposByCell, mergeSearchResults)Test plan
go test ./cmd/entire/cli/codesearch/...passesgo test ./cmd/entire/cli/ -run "TestSearchCmd|TestWriteCodeSearch|TestCodeSearch|TestGroupRepos|TestMergeSearch"passesmise run lintclean🤖 Generated with Claude Code