## v0.26.1

Audit-hardening patch release. 55 fixes across 6 subsystems on top of v0.26.0, plus a callgraph correctness fix for monorepos with nested lockfiles.

## Callgraph correctness

`aft_navigate callers` and `aft_navigate impact` now resolve cross-package consumers in monorepos correctly, regardless of nested per-package lockfiles. Previously a `bun.lock` or `package-lock.json` inside an individual workspace package stopped the upward walk to the real workspace root, leaving some consumers invisible to the call graph.

Workspace resolution received broader improvements too:

- **PNPM monorepos** are now recognized via `pnpm-workspace.yaml` instead of being silently unsupported.
- **Glob workspace patterns** with negations (`!apps/legacy`) and recursive globs (`packages/**`, `apps/*/pkg`) now resolve correctly.
- **TypeScript path aliases** (`compilerOptions.paths`, `baseUrl`) resolve before falling back to workspace-package lookup.
- **`WORKSPACE_PACKAGE_CACHE`** is now invalidated on file changes — adding, renaming, or removing a workspace package no longer requires an AFT restart.

## Call edge coverage

The callgraph now indexes call edges that were previously missing entirely:

- **JSX components** (`<Foo />`, `<Foo.Bar />` in TSX) now register as calls to the component.
- **Constructor calls** (`new ClassName()`) now register as calls to the class.
- **Computed string member calls** (`obj["method"]()` when the key is a literal) now register against the property name.
- **Re-export alias chains** (`export { foo as bar } from "./mod"` plus `import { bar }`) now follow through to the real symbol, both for the export-side alias and import-side alias.
- **Default re-exports** (`export { default } from "./mod"`) now resolve to the target file's actual default-export symbol instead of a synthetic ghost.
- **Same-file `call_tree` traversal** now descends into local calls instead of dropping them as unresolved leaves.
- **Source-less export aliases** (`export { foo as bar }`) record `bar` as exported, not `foo`.
- **`callers` and `call_tree`** prechecks now find non-exported leaf symbols correctly (previously private leaf functions with real callers returned `symbol_not_found`).
- **Self-call filter** no longer drops legitimate external calls that happen to share the enclosing function's short name (e.g. `function add() { return math.add(...) }`).

## LSP diagnostics honesty + installer security

- **Pre-edit snapshot freshness** — file-mode push-fallback diagnostics now require version match or epoch advancement past a pre-sync snapshot, instead of a wall-clock test that could accept late publishes for stale file states.
- **Unversioned servers** no longer count as fresh on epoch advancement alone — AFT now treats their diagnostics as pending until a stronger causal fence resolves.
- **Multi-server coverage** — directory-mode diagnostics now track coverage per `(server, file)` pair. If `.ts` files have both TypeScript LS and Biome registered but only Biome is active, the missing tsserver coverage is reported in `unchecked_files` instead of being hidden behind `complete: true`.
- **Partial workspace pulls** are now reported as `complete: false` with a dedicated `workspace_pull_partial` status instead of being treated as complete.
- **LSP child cleanup** closes the spawn-track gap, adds Linux parent-death tracking (`PR_SET_PDEATHSIG`), and adds a Windows console-control handler. LSP wrapper grandchildren no longer leak on bridge SIGKILL or unhandled signals.
- **GitHub LSP installer** rejects tar archives containing hardlink entries before extraction — closing a hardlink escape path that symlink-only validation missed.
- **Cached install validation** now uses `binarySha256` for steady-state TOFU checks (the previous code compared extracted binary hash against archive hash, causing valid caches to fail revalidation and get quarantined).

## Safety/backup correctness

Six P0/P1 fixes around `aft_safety undo` and recursive delete:

- **Tampered backup index rejected** — disk-loaded backup metadata is now validated against the active project root and rejected if it contains absolute or out-of-root paths. Closes a path-traversal hole that could have turned `aft_safety undo` into an arbitrary file overwrite primitive.
- **Atomic restore now actually atomic** — failed mid-batch writes correctly roll back the failing file too (previously only previously-completed writes were restored, leaving the failing file partially written).
- **Per-file rollback covers the in-flight file** in `ast_grep_replace`, glob `edit_match`, and `aft_refactor move` to new destinations.
- **Tombstones for create-only operations** — `write` to a new path, `edit appendContent` to a new path, multi-file `transaction` creates, and `aft_refactor move` to a new destination all now record tombstones so `aft_safety undo` can delete the created file. Previously these operations had no undo history.
- **Tombstone undo deletes instead of writing empty content** — per-file undo on a moved destination now removes the file (and any parent directories the operation created) instead of leaving an empty file.
- **Failed-rollback backup cleanup** — when an operation rolls back, its op_id-tagged backup is also popped, so the next `aft_safety undo` targets the previous successful operation instead of a no-op.
- **`aft_move` records destination tombstone before moving**, not after — tombstone capture failure now rolls the move back instead of leaving disk mutated with no undo metadata.
- **Recursive delete refuses non-regular entries** (FIFOs, sockets, device nodes, hard-linked files) explicitly, instead of silently deleting them with no backup.
- **Restart-safe latest-op selection** — `aft_safety undo` now uses a persisted monotonic ordering field instead of `(second_timestamp, in-process counter)`, so restart-within-the-same-second no longer scrambles which operation is "latest."
- **External-modification warnings** now fire for both in-memory and disk-fallback undo paths.

## Background bash wake delivery

Three P0s and three P1s in the completion-delivery state machine:

- **Drain is now a peek**; a new `bash_ack_completions` RPC persists `completion_delivered=true` only after the plugin actually appends or wakes. Plugin death in the drain→deliver window no longer loses the completion.
- **Push-delivered completions are now acked** explicitly. Previously the push path never marked tasks delivered, leaving them undelivered on disk forever and uncollectable by GC.
- **Replay-orphaned completions now wake the agent** — after configure replays terminal tasks, the plugin forces one drain per session so wakes fire even when the original task is no longer tracked.
- **Wake retries cap at 5 attempts** with exponential backoff capped at 1s, then surface a hard failure. Permanent failures (missing `promptAsync`, runtime always throwing) no longer create an infinite retry loop.
- **Replay-synthesized terminals now insert into the in-memory registry** so subsequent ack persistence works correctly.
- **Schema-version validation on read** — incompatible `bash-tasks/*.json` files are quarantined immediately on replay instead of being silently skipped.
- **Post-restart long-running reminder suppression** — rehydrated running tasks no longer fire a fresh `bash_long_running` reminder on the first watchdog tick after restart.
- **`bash_kill` cross-session lookup** mirrors `bash_status` — a resumed session can now kill a background task spawned by an earlier session, not just inspect it.

## Search/semantic cache reuse

One P0 and four P1s around the v0.24 cross-worktree cache reuse:

- **Symbol cache write race fixed** — symbol cache persistence now takes a lock parallel to search/semantic caches and uses unique temp filenames. Two main bridges writing the same project no longer race and corrupt `symbols.bin`.
- **Reused search index marks unverified until freshness check completes** — `grep`/`glob` no longer serve stale state with `success: true` immediately after cold start. Index becomes ready only after `verify_file_mtimes` confirms freshness.
- **HEAD-change refresh runs filesystem freshness** after the `git diff` fast path, using `--name-status -M` to catch renames and pick up untracked + locally-edited files.
- **Hybrid search gates lexical fusion on `index.ready`** — `aft_search` no longer adds stale lexical boosts while the search index is rebuilding.
- **Semantic watcher marks edited files stale** instead of silently dropping them — semantic search now reports stale-or-rebuilding status when invalidated files are still being re-embedded, instead of returning stale embeddings as ready.
- **Cache path validation on read and write** — all three caches (search, semantic, symbol) now reject absolute paths and paths containing `..` at both serialization and load boundaries.

## Plugin transport + aft-bridge

Four P1s and five P2s in the bridge transport layer:

- **Transparent retry on version mismatch fixed** — host plugins hitting a version mismatch on first call now correctly retry against the fresh bridge instead of failing with "Bridge replaced during version check." This was the highest-value bug — it affected every first user call after AFT auto-upgraded the binary.
- **Cached binary probing** — versioned-cache resolution now verifies the cached binary's `--version` output matches the directory tag before returning. Corrupted, wrong-arch, or mislabeled binaries fall through to the next candidate.
- **Null-version npm binaries fall through** — Gatekeeper-killed unsigned binaries on macOS no longer cause the resolver to return an unusable path.
- **Download safeguards** — `downloadBinary()` now has request timeout, advertised-size check, streaming byte cap, and incremental hashing, matching the patterns already used by `onnx-runtime.ts`. Stalled networks no longer hang plugin startup indefinitely.
- **Concurrent upgrade dedup** — version-mismatch upgrades coordinate via shared per-version promises instead of a single boolean flag.
- **Per-version download lock** — concurrent downloads of the same binary serialize through a file lock and unique temp filenames.
- **Stderr ring-buffer logical lines** — stderr lines split across multiple reads no longer fragment into separate ring entries; the 20-entry tail is now 20 logical lines.
- **`BridgePool` forwards `errorPrefix` + `logger` options** — bridge instances now log through host-supplied loggers consistently, not a process-global singleton.
- **Logger calls in event handlers wrapped in try/catch** — a throwing logger no longer escapes a child-process event callback to crash the host.


## Upgrading

Restart your editor or OpenCode/Pi instance to pick up the new bridge binary. No config changes needed.

- npm: `npx --bun @cortexkit/aft setup` (or upgrade your existing install)
- Plugin: `@cortexkit/aft-opencode@0.26.1`, `@cortexkit/aft-pi@0.26.1`
- CLI: `@cortexkit/aft@0.26.1`
- Binary: `agent-file-tools@0.26.1` on crates.io and platform packages on npm

## Test surface

Audit fixes shipped with 50+ new regression tests across Rust integration, plugin e2e, and aft-bridge unit suites. Combined test count: 2,061 passing across all workspaces.
