# v0.36.0: Persisted call graph, leaner bash output, and sharper code search

The call graph now lives in a persisted, incremental store instead of being rebuilt from scratch on demand. That removes the file-count limit that capped dead-code analysis on large repositories, makes `aft_callgraph` queries resolve from disk, and improves edge accuracy across more languages. This release also stops shell pipes from hiding test failures, steers agents toward `aft_search` for code discovery, and fixes a handful of crashes and papercuts.

## Persisted call graph (and the end of the file-count cap)

`aft_callgraph` and `aft_inspect`'s dead-code analysis used to build a whole-project call graph in memory, re-parsing every file on demand. That build was capped at `lsp.max_callgraph_files` (5000) to avoid the multi-core CPU spikes reported in #86, which meant dead-code analysis simply turned off above that size.

The call graph is now a persisted SQLite store, built once in the background and updated incrementally as files change. Queries read from disk, so callers/callees/impact/trace return without rebuilding anything, and the cold build reuses the symbol cache instead of re-parsing. On this repo the cold build is ~14s; on a 5,700-file TypeScript repo ~13s, after which updates are incremental.

The `lsp.max_callgraph_files` cap is gone for the call-graph store. Dead-code analysis runs on large repositories again. The cold-build's parsing pass is bounded to half your cores so it can't monopolize the machine while it runs.

## More accurate call edges

The store resolves method calls more precisely. It infers receiver types (for `self`/`Self`, parameters, and common bindings in Rust, Java, Kotlin, and C++) to produce exact method-dispatch edges, falls back to name matching when a type can't be resolved, and applies a standard-library denylist so calls like `.expect()` or `.map()` don't generate thousands of spurious edges. Call extraction now also covers C, C++, C#, and Zig.

## Bash output stops hiding failures

Piping a test or build command through a filter, like `bun test | grep fail` or `cargo test | grep -A3 FAILED`, used to strip the output down before AFT's compressor ran, which often threw away the failure detail and summary you actually wanted. When output compression is on, AFT now recognizes that pattern, runs the command without the trailing filter, and lets the compressor keep the failures and summary. This also works through a leading `cd <dir> && ...` prefix. Pass `compressed: false` to keep your pipe verbatim.

## Sharper code search steering

Agents reach for `grep`/`rg`/`find` in `bash` out of habit even when AFT's indexed search is faster and better ranked. Workflow guidance and the per-command hints now steer code discovery to `aft_search` and the other `aft_*` tools, run in parallel, and when `aft_search` is available the grep-rewrite footer points there instead of suggesting the grep tool.

## Status bar accuracy

The `E`/`W` (errors/warnings) counts in the agent status bar are now filtered to the same build membership `aft_inspect` and `tsc` use, so files excluded from your `tsconfig` (test files, build output) no longer inflate the count with diagnostics that your compiler would never report.

## Fixes

- **`foreground_wait_window_ms` had no effect (#102):** a small `timeout` passed to a foreground `bash` call collapsed the wait window and could kill the command almost immediately. A foreground `timeout` below the wait window is now ignored (the command runs to the normal window, then promotes to background), while an explicit `background: true` task still honors a short timeout as a real kill cap.
- **`aft_delete` and `aft_safety checkpoint` could crash on a mistyped argument:** when a host delivered the `files` array as a bare string or a JSON-encoded string, the tool threw `inputs.map is not a function` before doing anything. The argument is now coerced at the boundary, so it works or returns a clear error instead of crashing.
- **Trailing newline** is preserved in compressed terminal output instead of being dropped from the last line.
- **Call-graph lookups no longer show as tool errors** in the UI when a symbol isn't found or the store is still building; they return a normal result.
- **Semantic index** no longer hammers a slow embedding backend with fixed-interval retries; refresh retries back off and trip a circuit breaker.

Thanks to @de-flandres (#102, pinpointing the wait-window override).

[Join our Discord](https://discord.gg/DSa65w8wuf)
