# v0.33.0

This release adds `aft_inspect` — a single codebase-health view for agents — and reworks how diagnostics reach the agent. It also renames `aft_navigate` to `aft_callgraph` and lands a large batch of `aft_search` correctness fixes.

## aft_inspect — codebase health at a glance

`aft_inspect` gives an agent the "open the project and see what's wrong" view in one call, instead of stitching together greps and separate tools. One call returns a summary across categories; pass `sections` to drill into any of them.

Categories:

- **diagnostics** — compile/type errors from language servers
- **metrics** — file count, lines of code, symbol count
- **todos** — TODO/FIXME/HACK/XXX/BUG markers with file, line, and author
- **dead_code** — exported symbols with no reachable caller
- **unused_exports** — exported symbols nothing imports
- **duplicates** — near-identical code blocks across files

Treat `dead_code` as a hint, not an authority: reachability is call-based, so symbols reached only through method dispatch (`receiver.method()`) or referenced only in type position can still appear as false positives. Verify before deleting.

It runs in tiers. Metrics and todos return synchronously from cache. dead_code, unused_exports, and duplicates run as background scans on session idle; calls return cached results immediately (marked `stale` when a refresh is pending) rather than blocking. Results persist to disk per project, so they survive restarts.

Options:

- `sections` — one category or an array; `all` for everything; omit for summary-only
- `scope` — restrict to a file or directory
- `topK` — cap drill-down items per category

### Scan results persist across restarts

Tier-2 scan results are stored on disk. When you reopen a project after editing files, `aft_inspect` serves the last completed scan immediately (flagged `stale`) while a fresh scan runs in the background, instead of showing nothing until the rescan finishes.

## Diagnostics now flow through aft_inspect

Diagnostics no longer arrive automatically after every edit. Previously `edit`, `write`, and `apply_patch` waited on language-server diagnostics inline on each call. That work moved into `aft_inspect`, which an agent runs after a batch of edits and before tests or a commit. The result is faster edits and a single deliberate point to check the codebase.

- The standalone `lsp_diagnostics` tool is removed; its capability is part of `aft_inspect`'s `diagnostics` category.
- A new `lsp.diagnostics_on_edit` config option (default off) restores per-edit diagnostics for users who want them.
- `aft_inspect` diagnostics are build-authoritative: TypeScript/JavaScript files excluded by `tsconfig.json` (`files`/`include`/`exclude`, following `extends`) are skipped, so results match a real `tsc` run instead of reporting phantom errors on excluded files.
- Scoped diagnostics on a single file wait briefly for a cold language server to start and publish, so the result arrives in one call rather than requiring a re-run. Scopes whose file types have no registered or applicable server report a terminal `no_server` status instead of waiting forever.

`aft_inspect` diagnostics are a fast checkpoint, not the project-wide authority — a clean `tsc` / `cargo check` / `pyright` run remains the real gate.

## aft_navigate is now aft_callgraph

The call-graph tool is renamed from `aft_navigate` to `aft_callgraph`. The new name matches what the tool does — answer code-relationship questions (callers, call tree, impact/blast radius, execution paths, data flow) from a real call graph — and is the term models reliably reach for. All operations (`callers`, `impact`, `call_tree`, `trace_to`, `trace_to_symbol`, `trace_data`) are unchanged.

The Rust call graph now resolves cross-file callers. Calls through `crate::`, `super::`, `self::`, a workspace crate name (e.g. a binary's `mycrate::module::func(...)`), and `use`-imported short names now resolve to the right definition. Previously only same-file callers were found, so `callers`/`impact` on a Rust symbol returned far too few results and `dead_code` over-reported. Liveness also now flows through private intermediary functions, so handlers reached only via a private router are no longer flagged dead.

## aft_search correctness

A batch of fixes to the unified `aft_search` shipped in v0.32:

- **Disabled vs. unavailable.** With `semantic_search: false`, a natural-language query now returns a clear "not enabled" result instead of silently degrading to a lexical scan. The degraded lexical fallback is reserved for when semantic is enabled but temporarily building or unavailable.
- **Truthful routing.** `interpreted_as` reports the lane that actually ran (`lexical` when the semantic lane was skipped), not the lane that was requested.
- **Result provenance.** Each result carries the lane that produced it plus a `hybrid_boosted` flag, and `more_available` now accounts for engine caps and semantic overflow so "more results exist" is accurate.
- **Quoted phrases.** Surrounding quotes on a literal query (`"was not found on PATH"`) are stripped before matching, matching `rg -F` / code-search behavior, instead of searching for the quote characters.
- **Classifier routing.** A sentence-final `?` no longer forces regex mode; a query mixing natural language with an error code routes correctly; bare escape sequences route to regex while Windows-path-shaped queries stay exempt.
- **No hidden truncation.** Lexical candidates beyond the old internal cap are no longer dropped silently before fusion.

## Edits no longer echo back to the agent

`edit`, `write`, and `apply_patch` were returning unnecessary diff bytes back to LLM. The agent-facing result is now minimal (success plus added/removed line counts). 

## Bug fixes

Editing and refactoring:

- **`aft_safety` undo history no longer lost on restart.** The first edit to a previously-backed-up file after a restart could overwrite that file's entire on-disk undo history (and reuse a backup id). Prior history is now hydrated before the new snapshot is appended.
- **`apply_patch` pure-insertion hunks** insert at the resolved context line instead of end-of-file.
- **`aft_transform add_member`** on a generic `impl<T> Foo<T>` block now inserts methods into the impl block, not the struct body.
- **`aft_refactor` move** no longer adds a spurious import when a parameter or local variable shadows the moved symbol's name.
- **`aft_import organize`** correctly handles Rust nested `use` trees (`a::{b::{c, d}, e}`) without corrupting them.
- **`aft_zoom`** no longer raises spurious mutual-exclusion errors when a model sends empty or empty-content `targets`/`symbols` parameters.

Search and indexing:

- The semantic index verifies per-file freshness strictly and uses canonical paths for watcher invalidation, so edits refresh the right entries and large files aren't downgraded to a weaker freshness check.
- `aft_outline` accepts JSON registry/package URLs and outlines their top-level keys.
- URL fetching for `aft_outline`/`aft_zoom` retries transient connect/transport failures and now runs entirely in Rust.

Reliability and security:

- The worker bridge stays alive through transient starvation timeouts and only escalates to a kill on a genuine hang, so a busy bridge no longer drops unrelated requests.
- SSRF protection on URL fetches now blocks RFC 6598 (CGNAT, `100.64.0.0/10`) and RFC 2544 (`198.18.0.0/15`) ranges, including their IPv4-mapped IPv6 forms.
- Windows hardening: validated `LoadLibraryExW` ONNX Runtime loading, NuGet cache scanning, storage directory creation, and a binary-replacement retry (contributed in #66/#69).
- Killing a PTY-mode background task on Windows now finalizes reliably instead of hanging, so `bash_kill` and the polling that waits on it always return.
- LSP and formatter "not installed" warnings no longer re-fire on every session, and synthetic notifications preserve the active model and variant.

Join us on Discord: https://discord.gg/DSa65w8wuf
