# v0.26.0

Post-audit hardening release. 32 fixes from 13 parallel audit lanes plus 3 follow-up dogfood-bug fixes, all verified live. No new public surface — every change is a correctness, honesty, or robustness improvement on top of v0.25.2.

## Highlights

- **Multi-file undo now works.** `aft_safety undo` is one operation: deleting `["a","b"]` and undoing restores both. `aft_move` undo removes the destination AND restores the source (new backup tombstone API). `move_symbol` and `ast_replace` are now operation-scoped too. Symlinks are rejected before mutation in single-file delete (directory delete already had this guardrail).
- **`aft_navigate callers` resolves workspace package imports.** `import { foo } from "@your-pkg/bar"` now correctly maps to source files in monorepo siblings, including when `package.json` `main` points at `dist/` but the source lives in `src/`. Top-level call sites (e.g. inside `describe()`/`test()` blocks) are now indexed.
- **`bash` `find` rewrite no longer drops the path.** `find /tmp/foo -name "*.ts"` now correctly passes the absolute path through to glob instead of embedding it in the pattern.
- **Tri-state response contract enforced end-to-end.** `read` reports real `total_lines` and returns `complete: false` on partial reads. The edit family omits `syntax_valid` when validation didn't run instead of falsely returning `true`. `inline_symbol` correctly matches multiline calls by start-line. `lsp_diagnostics` directory mode reports partial workspace pulls honestly.
- **Bash background tasks survive restart by default.** Replay now runs with the inferred storage_dir, so `bash background:true` completions are delivered after an OpenCode restart even without explicit `storage_dir` config. Detached PID liveness recovery handles externally-killed children.
- **`aft doctor` is now read-only.** Plain `aft doctor` runs inspection without mutating config or running install commands. Use `aft doctor --fix` for the previous auto-remediate behavior. ONNX is only flagged as a problem when `semantic_search` is enabled. Issue title sanitization, JSONC comment preservation, and streaming log tail are in.
- **Out-of-project navigate paths return an honest error.** Calling `aft_navigate` on a path outside `project_root` now returns `path_outside_project_root` with a clear message instead of misleadingly reporting 0 results.

## Detailed changes

### Safety and undo
- Operation-scoped backup IDs for multi-file `aft_delete`, `aft_move`, `move_symbol`, `ast_replace`
- Backup tombstone API for `aft_move` (undo removes destination + restores source atomically)
- `delete_file` rejects symlinks before mutation
- Session marker handling: markerless session dirs are skipped instead of being collapsed into `__default__`
- Backup paths resolve against `project_root` consistently regardless of process CWD
- `storage_dir` reset cleans stale checkpoint directories

### Navigate / callgraph
- Workspace package imports (`@org/pkg`) resolve to monorepo siblings
- `main: "dist/..."` falls through to `src/...` when source exists alongside compiled output
- Top-level call sites (e.g. inside `describe`/`test` blocks) indexed as `<top-level>` callers
- `callers`, `impact`, `trace_to`, `trace_data` reject out-of-project paths with `path_outside_project_root`

### Edit / write / read honesty
- `read` returns real file length in `total_lines` (continues scanning past requested range)
- Partial reads return `complete: false` instead of falsely claiming complete
- Batch / edit_match / edit_symbol / extract / inline omit `syntax_valid` when validation didn't run
- `inline_symbol` matches multiline calls by start-line
- `apply_patch` all-failed path throws (UI shows error state) instead of returning misleading success

### LSP
- Watched-files dynamic registration via `client/registerCapability` (LSP 3.17 protocol-correct)
- `workspace/diagnostic` honors caller timeout with `$/cancelRequest`
- Centralized Windows URI helper handles `\\?\`, `\\?\UNC\server\share`, and drive paths consistently across manager / position / client
- Directory mode reports `WorkspaceDiagnosticReportResult::Partial` as `complete: false`

### Compression
- `toml_filter` `[shortcircuit]` regex no longer multi-line by default (previously, `when = "^\\s*$"` could match any blank line and collapse real output to `make: ok`)
- `compress_tsc` preserves top-level errors like `TS18003: No inputs found in config file` instead of dropping them

### Bash
- Background tasks replay on default storage_dir (completions delivered across restart automatically)
- Detached PID liveness check distinguishes externally-killed children from running tasks
- `find` rewrite routes absolute paths through glob's `path` arg instead of embedding in pattern

### Parser / extract / imports
- Symbol cache invalidates by content_hash on mtime collision (fixes false-cache hits on dev cycles)
- TS `export { foo }` and `export default foo` correctly detected as exports
- Default imports resolve to the real symbol name + metadata
- Namespace imports (`import * as ns`) preserved through `aft_import organize` (previously degraded to side-effect import)
- `extract` is scope-aware: detects enclosing function correctly (not the first `const x = ...`)
- `extract` preserves nested indentation in the extracted body
- `extract` emits `let`/`var` at call-site when caller already had `let`/`var`
- `extract` substitution is scope-aware: nested callback parameters shadowing the same name aren't renamed

### OpenCode plugin parity
- `aft_bash`, `bash_status`, `bash_kill` registered with `aft_` prefix when host bash hoisting is disabled
- `client.session.get` shape matches current SDK
- Transaction edit, delete, legacy `aft_edit` throw on Rust failure (consistent with the rest of the tool surface)
- `onVersionMismatch` migrated to coordinated-retry callback shape

### Pi plugin parity
- LSP auto-install uses npm (not Bun; Pi runs under Node)
- Version mismatch reads stderr (Pi v0.74 emits version to stderr in RPC mode)
- Hot-swap path: replaceBinary returns new path; bridge retries in-flight request
- AST grep / replace schema hints surface server-provided guidance
- `aft_delete` throws on Rust failure (was silently returning `success: false`)
- `onVersionMismatch` migrated to coordinated-retry callback shape

### CLI / doctor
- Plain `aft doctor` is read-only (use `--fix` for remediation; `--force` aliased for back-compat)
- ONNX `compatible: false` only flagged as problem when `semantic_search` is enabled
- Issue title sanitization (strips usernames/paths from `--issue` bundle title)
- JSONC comment preservation through config rewrites
- Binary version probe before extracting cached archives
- Streaming log tail for `--issue` bundle

### Security
- `url-fetch` SSRF check runs at both cache-check time AND fetch time (prevents a URL fetched once with `allowPrivate=true` from being readable later with `allowPrivate=false`)
- Version-mismatch handling no longer fire-and-forget; the in-flight request is coordinated with the hot-swap and retried transparently

### CI / release
- `tests.yml` now triggers on changes to `scripts/**` and release workflows (previously could merge with no CI run if only those paths changed)
- All npm publish jobs idempotent — preflight `npm view` skips already-published versions as success rather than failing the rerun
- macOS E2E hard-fails on missing artifacts or silent `npm install` failures (previously masked by hardcoded `"0.19.5"` fallback)
- `scripts/wait-release.sh` fails fast on `gh` errors instead of polling forever

## Upgrade

```
npx --bun @cortexkit/aft@latest doctor
```

If your plugin or binary is older than 0.26.0, restart OpenCode after upgrade so the new bridge spawns.
