---
title: code_search
summary: Rollout guidance, benchmark evidence, and current behavior for the native code_search tool.
description: Benchmark-backed rollout guide for native code_search, including fallback behavior, limitations, and when to prefer it over shell search.
owner: team-runtime
status: canonical
lastReviewed: 2026-04-05
appliesTo: main
docType: guide
---

# `code_search`

`code_search` is the native workspace search tool in the Rust runtime. It supports literal and regex correctness, returns structured matches, and always treats live verification against current file contents as authoritative.

## Current verified behavior

- Indexed planning is attempted only for queries eligible for workspace trigram index narrowing: compatible literal queries when a compatible, fresh trigram index exists.
- Regex queries are **supported for correctness and safety**, but trigram index narrowing does **not** support regex in v1.
- Regex planning returns `query_regex_not_supported` before index loading, and execution is labeled `fallback_discovery_live_verification` while continuing through discovery plus live verification.
- `index_unavailable` applies only when an otherwise index-eligible literal query cannot find a compatible trigram index; those runs are labeled `index_unavailable` and continue through discovery plus live verification.
- Final matches always come from live verification of current file contents. Indexed candidates are never treated as authoritative results by themselves.

## What this page is for

This page is the rollout evidence page for Issue #360. It is intentionally separate from Criterion microbenchmarks in `clients/agent-runtime/benches/agent_benchmarks.rs`.

- **Criterion benches**: low-level microbenchmarks only.
- **Rollout benchmark runner**: real shell baseline through `ShellTool`, native no-index, native cold-build, native warm-index, plus canonical line-match parity.

Runner:

```bash
cargo run --manifest-path clients/agent-runtime/Cargo.toml \
  --example code_search_rollout_benchmark -- \
  --workspace both \
  --repo-path /path/to/repo \
  --samples 5 \
  --cold-build-samples 2
```

## Benchmark methodology

### Shell baseline

The shell baseline uses the real `shell` tool path with `NativeRuntime`, which executes `grep` through `sh -c`. This preserves the same shell-tool wrapping and policy checks that current agent flows use.

### Native modes

- `native_no_index`: delete `state/code-search/index.db` before each measured run.
- `native_cold_build`: delete the index, time `refresh_or_rebuild()`, then time the first `code_search` search.
- `native_warm_index`: build or refresh once, then time repeated searches with the reusable index present.

### Parity rules

Parity compares shell and native results as canonical line matches:

```text
file + line + full line content
```

This rollout harness only makes recommendation claims for rows where parity passes.

### Recorded environment

#### Deterministic fixture workspace

- workspace kind: `fixture`
- generated by: `clients/agent-runtime/examples/code_search_rollout_benchmark.rs`
- file count: `4`
- benchmarked at: `2026-04-05T19:34:18.060248+00:00`
- host: macOS / aarch64 / Apple M2 Max
- Rust profile: `debug`

#### Current repo snapshot

- workspace kind: `repo_snapshot`
- workspace root: `<redacted>`
- commit SHA: `82fa4896`
- file count: `234763`
- benchmarked at: `2026-04-05T19:47:11.665525+00:00`
- host: macOS / aarch64 / Apple M2 Max
- Rust profile: `debug`

## Benchmark matrix

The rollout runner records these six representative cases in both workspaces:

| Case | Query kind | Result shape | Notes |
| --- | --- | --- | --- |
| `literal_small_hit` | literal | small-hit | one or few matching lines |
| `literal_large_hit` | literal | large-hit | many matching lines |
| `literal_no_hit` | literal | no-hit | zero-match literal miss |
| `regex_small_hit` | regex | small-hit | regex mode, fallback-labeled |
| `regex_large_hit` | regex | large-hit | regex mode, fallback-labeled |
| `regex_no_hit` | regex | no-hit | regex miss, fallback-labeled |

## Recorded results

### Deterministic fixture workspace

| Case | Mode | Plan mode | Reason | Samples | Median ms | P95 ms | Build median ms | Search median ms | Total median ms | Parity |
| --- | --- | --- | --- | ---: | ---: | ---: | ---: | ---: | ---: | --- |
| literal_small_hit | shell_baseline | — | shell_grep_baseline | 5 | 243 | 244 | — | — | — | baseline |
| literal_small_hit | native_no_index | index_unavailable | index_unavailable | 5 | 14 | 15 | — | 14 | 14 | pass |
| literal_small_hit | native_cold_build | indexed_narrowing | indexed_candidates_complete | 2 | 37 | 37 | 4 | 16 | 37 | pass |
| literal_small_hit | native_warm_index | indexed_narrowing | indexed_candidates_complete | 5 | 33 | 33 | — | 16 | 33 | pass |
| literal_large_hit | shell_baseline | — | shell_grep_baseline | 5 | 244 | 245 | — | — | — | baseline |
| literal_large_hit | native_no_index | index_unavailable | index_unavailable | 5 | 15 | 17 | — | 15 | 15 | pass |
| literal_large_hit | native_cold_build | indexed_narrowing | indexed_candidates_complete | 2 | 36 | 36 | 4 | 16 | 36 | pass |
| literal_large_hit | native_warm_index | indexed_narrowing | indexed_candidates_complete | 5 | 32 | 33 | — | 16 | 32 | pass |
| literal_no_hit | shell_baseline | — | shell_grep_baseline | 5 | 249 | 250 | — | — | — | baseline |
| literal_no_hit | native_no_index | index_unavailable | index_unavailable | 5 | 14 | 15 | — | 14 | 14 | pass |
| literal_no_hit | native_cold_build | indexed_narrowing | indexed_candidates_complete | 2 | 37 | 37 | 4 | 16 | 37 | pass |
| literal_no_hit | native_warm_index | indexed_narrowing | indexed_candidates_complete | 5 | 32 | 33 | — | 16 | 32 | pass |
| regex_small_hit | shell_baseline | — | shell_grep_baseline | 5 | 251 | 251 | — | — | — | baseline |
| regex_small_hit | native_no_index | fallback_discovery_live_verification | query_regex_not_supported | 5 | 14 | 15 | — | 14 | 14 | pass |
| regex_small_hit | native_cold_build | fallback_discovery_live_verification | query_regex_not_supported | 2 | 18 | 18 | 4 | 13 | 18 | pass |
| regex_small_hit | native_warm_index | fallback_discovery_live_verification | query_regex_not_supported | 5 | 14 | 14 | — | 14 | 14 | pass |
| regex_large_hit | shell_baseline | — | shell_grep_baseline | 5 | 245 | 246 | — | — | — | baseline |
| regex_large_hit | native_no_index | fallback_discovery_live_verification | query_regex_not_supported | 5 | 14 | 14 | — | 14 | 14 | pass |
| regex_large_hit | native_cold_build | fallback_discovery_live_verification | query_regex_not_supported | 2 | 18 | 18 | 4 | 14 | 18 | pass |
| regex_large_hit | native_warm_index | fallback_discovery_live_verification | query_regex_not_supported | 5 | 14 | 14 | — | 14 | 14 | pass |
| regex_no_hit | shell_baseline | — | shell_grep_baseline | 5 | 244 | 244 | — | — | — | baseline |
| regex_no_hit | native_no_index | fallback_discovery_live_verification | query_regex_not_supported | 5 | 15 | 16 | — | 15 | 15 | pass |
| regex_no_hit | native_cold_build | fallback_discovery_live_verification | query_regex_not_supported | 2 | 18 | 18 | 4 | 13 | 18 | pass |
| regex_no_hit | native_warm_index | fallback_discovery_live_verification | query_regex_not_supported | 5 | 13 | 14 | — | 13 | 13 | pass |

### Current repo snapshot

| Case | Mode | Plan mode | Reason | Samples | Median ms | P95 ms | Build median ms | Search median ms | Total median ms | Parity |
| --- | --- | --- | --- | ---: | ---: | ---: | ---: | ---: | ---: | --- |
| literal_small_hit | shell_baseline | — | shell_grep_baseline | 5 | 237 | 239 | — | — | — | baseline |
| literal_small_hit | native_no_index | index_unavailable | index_unavailable | 5 | 29 | 29 | — | 29 | 29 | pass |
| literal_small_hit | native_cold_build | indexed_narrowing | indexed_candidates_complete | 2 | 27897 | 27897 | 27239 | 326 | 27897 | pass |
| literal_small_hit | native_warm_index | indexed_narrowing | indexed_candidates_complete | 5 | 654 | 656 | — | 326 | 654 | pass |
| literal_large_hit | shell_baseline | — | shell_grep_baseline | 5 | 246 | 257 | — | — | — | baseline |
| literal_large_hit | native_no_index | index_unavailable | index_unavailable | 5 | 33 | 33 | — | 33 | 33 | pass |
| literal_large_hit | native_cold_build | indexed_narrowing | indexed_candidates_complete | 2 | 27706 | 27706 | 27039 | 338 | 27706 | pass |
| literal_large_hit | native_warm_index | indexed_narrowing | indexed_candidates_complete | 5 | 662 | 667 | — | 337 | 662 | pass |
| literal_no_hit | shell_baseline | — | shell_grep_baseline | 5 | 249 | 251 | — | — | — | baseline |
| literal_no_hit | native_no_index | index_unavailable | index_unavailable | 5 | 28 | 28 | — | 28 | 28 | pass |
| literal_no_hit | native_cold_build | indexed_narrowing | indexed_candidates_complete | 2 | 28071 | 28071 | 27385 | 342 | 28071 | pass |
| literal_no_hit | native_warm_index | indexed_narrowing | indexed_candidates_complete | 5 | 655 | 656 | — | 327 | 655 | pass |
| regex_small_hit | shell_baseline | — | shell_grep_baseline | 5 | 244 | 246 | — | — | — | baseline |
| regex_small_hit | native_no_index | fallback_discovery_live_verification | query_regex_not_supported | 5 | 35 | 36 | — | 35 | 35 | pass |
| regex_small_hit | native_cold_build | fallback_discovery_live_verification | query_regex_not_supported | 2 | 27751 | 27751 | 27708 | 42 | 27751 | pass |
| regex_small_hit | native_warm_index | fallback_discovery_live_verification | query_regex_not_supported | 5 | 36 | 36 | — | 36 | 36 | pass |
| regex_large_hit | shell_baseline | — | shell_grep_baseline | 5 | 248 | 250 | — | — | — | baseline |
| regex_large_hit | native_no_index | fallback_discovery_live_verification | query_regex_not_supported | 5 | 32 | 32 | — | 32 | 32 | pass |
| regex_large_hit | native_cold_build | fallback_discovery_live_verification | query_regex_not_supported | 2 | 27997 | 27997 | 27957 | 39 | 27997 | pass |
| regex_large_hit | native_warm_index | fallback_discovery_live_verification | query_regex_not_supported | 5 | 32 | 33 | — | 32 | 32 | pass |
| regex_no_hit | shell_baseline | — | shell_grep_baseline | 5 | 248 | 323 | — | — | — | baseline |
| regex_no_hit | native_no_index | fallback_discovery_live_verification | query_regex_not_supported | 5 | 28 | 28 | — | 28 | 28 | pass |
| regex_no_hit | native_cold_build | fallback_discovery_live_verification | query_regex_not_supported | 2 | 26974 | 26974 | 26939 | 35 | 26974 | pass |
| regex_no_hit | native_warm_index | fallback_discovery_live_verification | query_regex_not_supported | 5 | 29 | 29 | — | 29 | 29 | pass |

## Rollout guidance

### SHOULD prefer native `code_search`

Prefer native `code_search` for measured regex runs when parity passes and execution stays in `native_no_index` or `native_warm_index` fallback mode. Do **not** extend this recommendation to `native_cold_build`, where the current measured repo-snapshot rows still favor shell because index construction dominates the run.

Why:

- every published recommendation row passed canonical parity,
- the supported regex rows are explicitly labeled as fallback (`query_regex_not_supported` → discovery plus live verification), not as regex-aware trigram index narrowing,
- the measured `native_no_index` and `native_warm_index` regex rows were materially faster than the shell baseline in this local debug run, while `native_cold_build` remained the exception.

### MAY prefer native `code_search`

Use native `code_search` for literal searches when one of these is true:

- you already have a warm reusable index for the workspace,
- you care more about structured output and verified offsets than raw shell latency,
- the workspace is small enough that index build and warm reuse costs stay low.

### MAY keep shell / grep

Shell remains a reasonable choice for one-off literal searches in a large repo snapshot when you do **not** already have a reusable index.

Why:

- the repo-snapshot cold-build rows are dominated by full-workspace index construction in this debug-profile run,
- repo-snapshot warm-index literal rows (`654`-`662 ms`) were still slower than the shell baseline (`237`-`249 ms`) for the measured scoped literal cases,
- this change does **not** deprecate shell search and does **not** claim native preference for unmeasured cases.

## Limitations that matter for rollout

- Regex candidate narrowing is **not** implemented in v1.
- `query_regex_not_supported` is a planner fallback reason, not a search error.
- The current rollout runner rebuilds the index for the whole workspace, so scoped literal searches in very large repos can make cold-build evidence look worse than shell.
- These recorded numbers are from a local `debug` profile run. Re-running in another environment may change the absolute numbers, but the docs should keep the plan-mode and parity labels intact.

## Future optimizations (non-v1)

These are **not** required for the current rollout recommendation:

- regex-aware indexed candidate narrowing,
- case-insensitive or whole-word-aware index narrowing,
- workspace-scope-aware partial index builds,
- release-profile reruns for published rollout refreshes,
- machine-readable benchmark artifact export in addition to the markdown tables above.

Those items are future optimization work, not blockers for the current benchmark-backed rollout guidance.
