Skip to content

feat(index): derive a per-repo namespace on first index; add namespaces command - #239

Merged
ArtemisMucaj merged 1 commit into
mainfrom
feat/per-repo-namespace-by-default
Aug 14, 2026
Merged

ArtemisMucaj merged 1 commit into
mainfrom
feat/per-repo-namespace-by-default

Conversation

@ArtemisMucaj

Copy link
Copy Markdown
Owner

The problem

--namespace defaults to search, 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:

Indexing into namespace 'acme/widget-service'.
Use --namespace <name> to index into a shared namespace instead.

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/api and two/api do not collide. Falls back to the directory basename without a remote, then to search.

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_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, not just by reading:

Scenario Result
first index, git@github.com:acme/widget-service.git acme/widget-service
re-index of the same repo stays put, no derivation message, no new namespace
--namespace shared index … shared
non-git directory basename

The / question, resolved

The issue flagged owner/repo as needing verification against schema creation and the FTS PRAGMA. It does not apply. Each namespace is backed by a generated ns_<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/repo round-trips cleanly, so the preferred naming rule is the one that shipped rather than the owner-repo fallback.

Part 1b — codesearch namespaces

The Phase 0 guidance depends on enumerating namespaces, and nothing could: list and stats are both namespace-scoped.

Namespaces:

  acme/widget-service
    Embeddings: sentence-transformers/all-MiniLM-L6-v2 (onnx, 384 dimensions)
    Repositories: myrepo

-F json too. It reads the global namespace_config and repositories tables read-only before the container is built, so no embedding model is loaded — same reasoning as create. 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_namespaces backs the management HTTP endpoint and just had no CLI surface. The new code is the command, the formatting, and a repositories_by_namespace read.

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 on main (3 files) and match the referenced line numbers. So this lands in one PR rather than two.

  • Phase 0 — First index, before Phase 1: check whether the repo is indexed; survey namespaces; ask only when another namespace exists (a second repo inside the namespace this repo would derive is not a competing option); propose a contextual match rather than an open question; document codesearch create including --no-embeddings.
  • Indexing is long-running — wait for it, in both Phase 0 and the setup reference. Querying a half-built index returns empty results that read like "no matching code".
  • Corrected the "auto-resolves the namespace" claim, which was misleading for the first-index case.
  • MCP skill gets a short equivalent that links to the CLI runbook rather than duplicating it.

Tests

Seven unit tests on derive_namespace, including host_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, and derived_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.rs command dispatch, which the test harness does not reach.

  • cargo fmt — clean
  • cargo clippy --all-targets — clean
  • cargo test21 suites, 0 failures

Note on scope

This changes the observable behaviour of a bare codesearch index . for new repositories, so it wants a release-note line. --namespace search is the escape hatch and keeps working exactly as before.

Fixes #232

…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
@coderabbitai

coderabbitai Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

@ArtemisMucaj, you've reached your PR review limit, so we couldn't start this review.

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 @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: ab676e15-ca83-445e-8fdd-4991138d519e

📥 Commits

Reviewing files that changed from the base of the PR and between 4f2b5ae and fb51e67.

📒 Files selected for processing (9)
  • .claude/skills/codesearch-cli/SKILL.md
  • .claude/skills/codesearch-mcp/SKILL.md
  • src/application/git_remote.rs
  • src/cli/mod.rs
  • src/connector/api/mod.rs
  • src/connector/api/repo_resolver.rs
  • src/connector/api/router.rs
  • src/lib.rs
  • src/main.rs

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@ArtemisMucaj
ArtemisMucaj merged commit 8fd50ed into main Aug 14, 2026
2 checks passed
@ArtemisMucaj
ArtemisMucaj deleted the feat/per-repo-namespace-by-default branch August 14, 2026 10:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(index): derive a per-repo namespace by default; ask the user on first index; skills must wait for indexing

1 participant