feat(index): derive a per-repo namespace on first index; add namespaces command - #239
Conversation
…es command `--namespace` defaults to `search`, so indexing unrelated repositories piled them into one shared index. That degrades retrieval: the semantic and keyword legs are fused and reranked across everything in the namespace, so an unrelated repo's chunks compete with the ones the user wants. Multi-repo namespaces are a legitimate feature, but they should be a deliberate opt-in rather than what happens when you type `codesearch index .`. The choice is also effectively immutable — a namespace's embedding model and dimensions are fixed by its first index and validated on every open — so the old default silently committed the user to a shared embedding config too. On a first index the namespace is now derived from the git remote as `owner/repo`, created if needed, and printed with the override hint. The host is dropped so every clone protocol of the same repo derives the same name; the owner is kept so `one/api` and `two/api` do not collide. Falls back to the directory basename without a remote, then to `search`. Backwards compatible by construction: derivation only runs when `resolve_repo_context` finds no existing index, so an already-indexed repo keeps its namespace and nothing is migrated. Explicit `--namespace` still wins, including `--namespace search`. Verified end to end for all four paths. A `/` in the name is safe, and the issue's concern about it does not apply: each namespace is backed by a generated `ns_<uuid>` schema token, and the user-facing name is only ever stored and matched as data through bound parameters — never interpolated as an identifier or used to build a filename. Confirmed live against DuckDB, FTS included. Adds `codesearch namespaces` (text and json), which the skill guidance depends on: nothing could previously enumerate namespaces, since `list` and `stats` are both namespace-scoped. It reads the global tables read-only before the container is built, so no embedding model is loaded, and succeeds with an empty list on a fresh install — the signal that there is no namespace choice to make. Skill files gain a Phase 0 first-index runbook: check whether the repo is indexed, survey namespaces, ask only when another namespace exists, and wait for indexing to finish rather than querying a half-built index. The "auto-resolves the namespace" claim is corrected for the first-index case, and the essentials are mirrored into the MCP skill. Fixes #232
|
Warning Review limit reached
Next review available in: 43 minutes You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (9)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
The problem
--namespacedefaults tosearch, so indexing unrelated repositories piles them into one shared index. Search fuses a semantic and a keyword leg across the whole namespace and then reranks, so an unrelated repo's chunks can only dilute the ranking — and with a shared default, "unrelated repos in one namespace" is the normal case rather than the exception.The choice is also effectively immutable: a namespace's embedding model and dimensions are fixed by its first index and validated on every open, where a mismatch is a deliberate hard error. So the old default silently committed the user to a shared embedding configuration too.
Multi-repo namespaces are a legitimate feature. They should be a deliberate opt-in, not what happens when you type
codesearch index ..Part 1 — per-repo namespace by default
On a first index the namespace is derived from the git remote as
owner/repo, created if needed, and announced:The host is dropped so every clone protocol of the same repo derives the same name — otherwise re-cloning over https would strand the index under a new namespace. The owner is kept so
one/apiandtwo/apido not collide. Falls back to the directory basename without a remote, then tosearch.This reuses
git_remote, which already normalises every clone form and is already the key used to auto-resolve which namespace a repo was indexed under — so deriving the name from the same value keeps resolution consistent by construction.Backwards compatibility
Derivation only runs when
resolve_repo_contextfinds no existing index, so an already-indexed repo keeps its namespace and nothing is migrated. Explicit--namespacestill wins, including--namespace search.Verified end to end, not just by reading:
git@github.com:acme/widget-service.gitacme/widget-service--namespace shared index …sharedThe
/question, resolvedThe issue flagged
owner/repoas needing verification against schema creation and the FTS PRAGMA. It does not apply. Each namespace is backed by a generatedns_<uuid>schema token; the user-facing name is only ever stored and matched as data via bound parameters, never interpolated as an identifier or used to build a filename. The FTS PRAGMA interpolates the token, not the name.Confirmed live against DuckDB with FTS —
owner/reporound-trips cleanly, so the preferred naming rule is the one that shipped rather than theowner-repofallback.Part 1b —
codesearch namespacesThe Phase 0 guidance depends on enumerating namespaces, and nothing could:
listandstatsare both namespace-scoped.-F jsontoo. It reads the globalnamespace_configandrepositoriestables read-only before the container is built, so no embedding model is loaded — same reasoning ascreate. Succeeds with an empty list on a fresh install, which is the signal that there is no namespace choice to make.Most of this already existed:
DuckdbVectorRepository::list_namespacesbacks the management HTTP endpoint and just had no CLI surface. The new code is the command, the formatting, and arepositories_by_namespaceread.Part 2 — skill files
The issue's branch note is out of date: it says the skill files live on an unmerged
skills/*branch, but they are tracked onmain(3 files) and match the referenced line numbers. So this lands in one PR rather than two.codesearch createincluding--no-embeddings.Tests
Seven unit tests on
derive_namespace, includinghost_is_dropped_so_clone_protocol_does_not_matter(all four clone forms → one name),same_repo_name_under_different_owners_does_not_collide(why the owner is kept), both fallbacks, andderived_names_pass_namespace_validation.The backwards-compatibility paths were verified by running the binary against a scratch database, as tabled above — that behaviour lives in
main.rscommand dispatch, which the test harness does not reach.cargo fmt— cleancargo clippy --all-targets— cleancargo test— 21 suites, 0 failuresNote on scope
This changes the observable behaviour of a bare
codesearch index .for new repositories, so it wants a release-note line.--namespace searchis the escape hatch and keeps working exactly as before.Fixes #232