# v0.39.4: clearer signals, no surprise blocks, tighter listings

A patch focused on closing gaps where AFT did the right thing but withheld the information needed next: a sync wait that turned a busy bridge into a red failure, diagnostics that reported a count without the message, and formatter reflows that left a follow-up edit anchored on stale text. Plus a clean fix for external-path access under `restrict_to_project_root`, and folded `ls`/`find` output that keeps the entry that matters.

## `bash_watch` no longer fails on a busy bridge

A sync `bash_watch` polls the bridge while it waits. Each poll carried a 30-second transport timeout, so if the bridge was briefly busy (a background scan, a parallel tool call), one poll could time out and abort the whole watch — surfacing a red "bash_status timed out after 30000ms" even though the task was fine and the bridge stayed warm. Re-issuing the watch hit the same window, repeating the failure. A busy-bridge poll timeout now means "retry," not "the watch failed": the wait continues to its own deadline, and only reports a graceful "bridge stayed busy" if it genuinely can't read status in time. A real command failure still surfaces immediately.

## `aft_inspect` shows the diagnostic, not just a count

`aft_inspect` reported `diagnostics: 1 errors` but kept the message and location behind the `sections` parameter, like the other categories. A bare count isn't actionable — there's no fixing "1 error" without knowing what and where. Diagnostics detail (message + `file:line`) is now always included, capped by `topK`. It self-suppresses when there are no diagnostics, so a clean inspect costs nothing extra. The other categories (dead code, unused exports, duplicates) stay behind `sections` — their detail can run to hundreds of rows and a count there is a useful at-a-glance signal.

## Edits surface formatter reflows

When the project formatter reflows an edit — rustfmt splitting a one-line call across four lines, Prettier re-wrapping an object — the file on disk no longer matches the submitted text. A follow-up edit anchored on the original text could then fail to match. Edit results now show the reflowed region's on-disk text when the formatter changed the edit, so the next edit can re-anchor on what's actually there. It only appears when a reflow happened; a no-op format adds nothing.

## `restrict_to_project_root` blocks cleanly (#125)

With `restrict_to_project_root: true`, reading or globbing a path outside the project failed *after* you granted the permission prompt — the grant never reached the isolation boundary, so access was approved and then refused. `restrict_to_project_root` is AFT's full-isolation knob and is no longer tied to the host permission system: an out-of-project path is blocked up front with a clear explanation (and a note on how to allow external paths), instead of a prompt that can't be honored. With the setting off (the default), external paths work exactly as before.

## `ls` and `find` keep the entry that matters

Long `ls`/`find` output was compressed by truncation, which dropped entries from the middle of the listing — including the specific file being searched for. Runs of similar names now fold to a single summary line (`module_*.ts — 200 files (module_000.ts … module_199.ts)`) while any structurally distinct entry stays in the listing verbatim. A 200-file directory collapses to a few lines, and the one distinct outlier survives.

## Smaller refinements

`aft_zoom` suggests close symbol names when a requested symbol isn't found. `aft_callgraph` marks edges resolved by name alone with a trailing `~`, so a possibly-wrong same-name match is distinguishable from an exact one; the marker is explained in the tool description.
