# v0.40.0: groundwork for Subconscious

This release lays the foundation for AFT to run under **Subconscious**, the CortexKit daemon: a single shared service per machine that hosts code intelligence for every coding agent you use, instead of each agent spawning its own copy. Today AFT runs one process per project per agent, which means duplicate file watchers, indexes, and language servers when you have more than one agent open on the same code. Under the daemon, that becomes one shared index, one watcher, and one config per project.

The daemon integration itself is dormant in this release — **nothing changes in how you run AFT today**. What lands now is the groundwork: a single harness-agnostic configuration location, the internal substrate the daemon needs (path identity, per-project isolation, concurrent request handling), all behaving exactly as before when AFT runs standalone. Alongside it, this release closes a class of bugs where AFT could turn a real command failure into a clean-looking summary, and makes the top search result a full, ready-to-edit symbol.

## Configuration moves under `.cortexkit/`

The most visible piece of the groundwork. Config moves to one harness-agnostic location:

- Global: `~/.config/cortexkit/aft.jsonc`
- Project: `<project>/.cortexkit/aft.jsonc`

This happens automatically on first launch. AFT moves your existing config (`~/.config/opencode/aft.jsonc`, `~/.pi/agent/aft.jsonc`, `<project>/.opencode/aft.jsonc`, `<project>/.pi/aft.jsonc`) to the new path and leaves an `aft.jsonc.MOVED_READPLEASE` marker at each old location naming the new path, so a later edit to an old file is not silently ignored. If your OpenCode and Pi configs differ, the harness you open first wins and the other is preserved alongside it as `aft.jsonc.<harness>_OLD`. Your settings are never dropped to defaults, so the semantic index is not rebuilt.

Project-supplied TOML compression filters also move, from `<project>/.aft/filters/` to `<project>/.cortexkit/aft/filters/`. These are not auto-migrated: if you have trusted project filters, relocate them once with `git mv .aft/filters .cortexkit/aft/filters`. You do not need to re-grant trust (it is keyed by project root, so it carries over). Filters left under `.aft/filters/` are no longer read.

## Bash output never hides a failure

AFT compresses noisy test/build/lint output to save tokens. A multi-model audit found cases where that compression, or AFT's pre-execution rewriting of piped commands, could hide a real failure, so an agent proceeded on a false "all clear". This release closes that class:

- **Piped commands run exactly as written.** AFT used to rewrite `cargo test | grep -v fail` into a bare `cargo test` before running it (to compress the runner's full output), which silently changed the pipeline's exit status and hid failures expressed through the filter. AFT no longer rewrites the command: your pipeline runs verbatim, its real exit status is reported, and AFT's compression applies only to commands without a top-level pipe.
- **Cancelled work reports as failed.** A killed or aborted background task now carries a non-zero exit, so a `cargo test` you stop mid-failure is never compressed to a passing summary.
- **No clean summary over a failing exit.** On a non-zero exit, if the compressed text carries no failure signal AFT falls back to the raw output instead of trusting a tool summary. Compile, linker, and lifecycle errors (Go, Cargo, Bun, pnpm, pytest) are preserved rather than stripped, and `eslint: no issues` / `biome: no diagnostics` can no longer survive a failing run.
- **Truncation keeps the error.** Output capping now keeps each stream's tail, so a late stdout error after a large stderr is no longer dropped, and oversized output returns head plus tail instead of empty.

## Search: the top result is the full symbol

- The top `aft_search` result now renders the complete symbol (signature, body, leading doc and decorators), zoom-quality, capped at 250 lines, with a note that it is current on disk so you do not need to re-read or zoom before editing.
- Test files (`*.test.ts`, `*_test.rs`, `__tests__/`, and the like) are hidden from results by default. Pass `includeTests: true` to include them.
- Results carry a compact blast-radius annotation (caller count and nearby callers) so you can gauge a symbol's reach at a glance.

## Editing and formatting defaults

- **`format_on_edit` is now off by default.** When you opt in, formatting runs on a 90-second idle debounce in the background rather than synchronously after each edit, so it no longer competes with your turn.
- **Bash defaults to the foreground.** Guidance now runs commands in the foreground (auto-promoted to background after 15 seconds if they run long) instead of steering toward `background: true` plus `bash_watch`.
- **`apply_patch` end-of-file hunks anchor at the end only**, so an EOF-anchored change can never be applied to an earlier matching block.

## /aft-status

The `/aft-status` slash command no longer leaks an error into the TUI or the plugin log when it opens, and it appears once in the slash menu instead of twice.

## Fixes

- `aft_inspect` called directly now returns fresh Tier-2 results (a synchronous incremental recompute) instead of a possibly-stale snapshot, while the background status bar stays async.
- `aft_outline` accepts a JSON-stringified array `target`, and `aft_delete` honors a stringified `"true"` for `recursive`, matching what some hosts and models send.
- The code-search nudge no longer fires for `grep`/`rg` that run outside the project (for example after `cd` into another repository).
- Distinct MCP clients that sanitize to the same name no longer share a storage directory.
- A Windows file-lock contention error during concurrent filter-trust writes is now retried instead of failing.
- The file watcher skips high-churn ignored directories (`target/`, `node_modules/`) before resolving paths, cutting a CPU spike during builds.

## Contributors

Thanks to @tobwen (#139, #140, #141) for the UTF-8 byte-boundary output reads, pipe-aware compression dispatch, and PTY cursor-report detection across read boundaries, which we built on for the bash-output work above.

---

Questions or feedback? Join us on [Discord](https://discord.gg/DSa65w8wuf).
