# Usage metrics — how "saved" is calculated

`cl_usage` reports per-tool call counts, bytes served, and an **estimated**
context-window saving. The saving is an *estimate*, not a measurement — this
page explains exactly how it's computed and its limits.

## Which tools are tracked

Only the agent's **retrieval + context-management** tools are tracked as
"usage" — the ones that represent the agent actually using CodeLens to
find/read/save code:

| Tool | tracked | calls | bytes_served | bytes_saved |
|------|---------|-------|--------------|-------------|
| `cl_search` | ✅ | ✅ | ✅ | ✅ (discovery) |
| `cl_explore` | ✅ | ✅ | ✅ | ✅ (discovery) |
| `cl_related` | ✅ | ✅ | ✅ | ✅ (discovery) |
| `cl_impact` | ✅ | ✅ | ✅ | ✅ (discovery) |
| `cl_expand` | ✅ | ✅ | ✅ | 0 (it *is* the scoped read step) |
| `cl_save` / `cl_load` | ✅ | ✅ | ✅ | 0 (context management) |

**Operational tools are NOT tracked** (they're maintenance, not usage):
`cl_refresh`, `cl_doctor`, `cl_stats`, `cl_prune`, `cl_drop`, `cl_current`,
`cl_usage`. So `cl_refresh` (building the index) does not appear in the usage
report, and checking `cl_usage` never inflates the numbers.

Only the **discovery** tools (`cl_search`, `cl_explore`, `cl_related`, `cl_impact`) accrue `bytes_saved` —
the ones that replace "grep + read a bunch of files" with compact indexed context.

## The formula (refined — actual file sizes)

For a discovery call, CodeLens computes savings from the **real sizes of the
files in the results**, which it already indexes (`files.size`):

```
saved = max(0, Σ(distinct result files' indexed sizes) − bytesServed)
```

- `distinct result files` = the unique paths among the returned handles/files
  (`cl_search`/`cl_related` carry `results[].path`; `cl_explore` carries
  `files[].path`; `cl_impact` carries paths in target/candidates/callers/callees/affected lists).
- `indexed sizes` = `files.size` for those paths in the current branch index.
- `bytesServed` = bytes of the JSON actually sent to the model.
- **Capped at 50 distinct files** so a `cl_related` result returning 100
  importers doesn't inflate the total (the agent wouldn't read all 100
  without the tool).

**Counterfactual being modeled:** without the index, the agent would `grep` +
`read` the relevant files (whole-file reads at their real sizes); with the tool
it got compact handles. The difference is the saved context.

### Fallback (flat proxy)

If the size lookup fails (e.g. index not ready, paths not yet indexed), CodeLens
falls back to a flat proxy:

```
saved = max(0, handles × 4096 − bytesServed)
```

where `4096` is a rough average file size and `handles` = number of result
entries. This is the older, less-accurate estimate, kept only as a graceful
fallback.

## Honest limits

1. **It's a counterfactual estimate, not a measurement.** We don't know exactly
   which files the agent *would have* read without the tool — we assume the
   result files, whole.
2. **Whole-file assumption.** We credit the full file size, but a raw `read`
   might be partial, or the agent might read only the relevant part — so the
   estimate can overstate for large files with small relevant regions.
3. **No grep-output cost.** It ignores the bytes `grep` itself would have dumped
   into context, which would make the real saving *larger*.
4. **No multi-round exploration.** It counts one call, not the exploration
   rounds the agent avoids.
5. The 50-file cap is a judgment call to curb broad relationship/impact results.

Net: treat `saved(est)` as an order-of-magnitude indicator of how much context
the index tools are sparing, not an audited figure.

## Storage

Usage is **global** (`~/.codelens/usage.db`), aggregated across all repos, with
a `(tool, repo_id)` key so `cl_usage` can break it down per repo. It survives
restarts and core-index rebuilds (it's separate from the per-branch index DBs).

Reset with `cl_usage` is not exposed as a tool; from code, `UsageTracker.reset()`
clears it.