> ## Documentation Index
> Fetch the complete documentation index at: https://docs.reasonblocks.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Persist agent findings with CodebaseMemory

> Store, recall, and invalidate per-codebase findings using CodebaseMemory — a transport-resilient client for the ReasonBlocks findings store.

`CodebaseMemory` is a thin client over the rb-api `/findings/...` endpoints. It lets an agent persist observations during a run — bug locations, behavioral notes, architectural patterns — and recall them semantically on future runs. All transport errors are caught and surface as empty results / `None` / `0`, so a missing rb-api never breaks the agent loop.

## Initialize

```python theme={null}
from reasonblocks import CodebaseMemory

memory = CodebaseMemory(
    codebase_id="pydantic/pydantic",   # any stable identifier for the repo
    api_key="rb_live_...",
    base_url=None,                      # defaults to the hosted API
    timeout=None,                       # defaults to the SDK timeout
)
```

Constructor signature:

<ParamField path="codebase_id" type="str" required>
  Required. Identifier the findings are scoped under. Common patterns: `org/repo`, `repo@sha:abc123`. All findings stored under the same id are searchable together.
</ParamField>

<ParamField path="api_key" type="str" required>
  Sent as `Authorization: Bearer <api_key>` on every request.
</ParamField>

<ParamField path="base_url" type="str" default="DEFAULT_BASE_URL">
  Override to point at a self-hosted rb-api. Trailing slashes are stripped.
</ParamField>

<ParamField path="timeout" type="float" default="DEFAULT_TIMEOUT">
  HTTP timeout passed to the underlying `httpx.Client`.
</ParamField>

<Tip>
  Pass the same `codebase_id` to `CodebaseMemory` and to `rb.middleware(codebase_id=...)` if you also use the middleware — the middleware uses it to scope E1 retrieval.
</Tip>

## Store and recall

### Store a finding

```python theme={null}
finding_id = memory.store(
    content="BaseModel.validate raises ValidationError on extra=forbid when unknown fields are present",
    file_path="pydantic/main.py",
    finding_type="behavior",  # bug | behavior | pattern | note
    metadata={"reviewer": "alice", "severity": "low"},
)
```

Returns the finding id on success or `None` on transport error. Content is truncated server-side at 8000 chars; `file_path` at 512; `finding_type` at 64.

### Recall by query

```python theme={null}
results = memory.recall(
    "validator error handling",
    top_k=5,
    score_threshold=0.25,
)

for r in results:
    print(r["file_path"], r["score"], r["content"])
```

Returns a list of `{content, file_path, finding_type, score, ...}` dicts ordered by descending score. Raise `score_threshold` for tighter matches; lower it for more recall.

### Pre-formatted recall (for tool outputs)

`format_recall` combines `recall` with a human-readable renderer. Use it when a tool needs to return findings as a string.

```python theme={null}
digest = memory.format_recall(
    "validator error handling",
    top_k=5,
    score_threshold=0.25,
    max_len=300,    # truncate each finding's content
)
print(digest)
# Found 2 relevant prior findings:
#
# --- [behavior] pydantic/main.py  (relevance 0.82) ---
# BaseModel.validate raises ValidationError on extra=forbid ...
```

Returns `"(no relevant findings in cache yet)"` when nothing matches.

## Other operations

### List every finding

```python theme={null}
all_findings = memory.list_all()
```

Useful for auditing the cache before a run.

### Batch store

```python theme={null}
count = memory.store_many([
    {"content": "...", "file_path": "pydantic/main.py", "finding_type": "bug"},
    {"content": "...", "file_path": "pydantic/validators.py", "finding_type": "behavior"},
])
```

Each dict needs at least `content`. `file_path`, `finding_type`, and `metadata` are optional. Returns the count actually stored (entries that fail or have empty content are skipped).

### Coverage check

`coverage_for` returns the fraction of a file list that has at least one stored finding. A soft suffix-match means a stored `pydantic/main.py` covers a query for bare `main.py`.

```python theme={null}
files = ["pydantic/main.py", "pydantic/validators.py", "pydantic/fields.py"]
coverage = memory.coverage_for(files)

if coverage < 0.5:
    print("cache is sparse - do a full review")
else:
    print(f"cache covers {coverage:.0%} of files")
```

### Invalidate stale findings

When files change, drop their findings:

```python theme={null}
deleted = memory.invalidate(["pydantic/main.py", "pydantic/validators.py"])
```

Returns the number deleted server-side.

### Clear the entire cache

```python theme={null}
memory.clear()  # drops every finding for the codebase_id - irreversible
```

<Warning>
  `clear()` cannot be undone. Use `invalidate()` for targeted cleanups.
</Warning>

### Release the HTTP client

```python theme={null}
memory.close()
```

`CodebaseMemory` holds a persistent `httpx.Client` to keep TCP connections warm across calls. Call `close()` when you're done if your process needs to release sockets cleanly.

## Pair with ImportGraph for blast-radius invalidation

When a file changes, you usually want to invalidate every file that imports it. `ImportGraph` (see [Import graph](/api-reference/import-graph)) gives you the blast radius:

```python theme={null}
from reasonblocks import ImportGraph

graph = ImportGraph()
graph.build_from_files({path: open(path).read() for path in py_files})

changed_files = ["pydantic/main.py"]
affected = graph.blast_radius(changed_files, depth=1)

deleted = memory.invalidate(list(affected))
print(f"invalidated {deleted} findings across {len(affected)} files")
```

<Note>
  `ImportGraph.build_from_files` requires `networkx`. Install with `pip install networkx`.
</Note>

## Wire into your agent

Each framework has a tool factory wrapping `CodebaseMemory` (and optionally `ImportGraph`). See:

* [LangChain tools](/guides/langchain#add-codebasememory-tools) via `make_langchain_tools`
* [OpenAI Agents tools](/guides/openai-agents#add-codebasememory-tools) via `make_openai_tools`
* `reasonblocks.integrations.claude_tools.make_claude_tools` for the Anthropic Messages API
