Repository navigation
v0.56.2
AFT 0.56.2 is a patch release for aft doctor reporting healthy OpenCode installs as broken, for natural-language aft_search stalling on machines where the local embedding model is slow, and for a set of smaller defects found by the new daemon health sentinel while it was being built.
Fixes
aft doctorno longer flags a working OpenCode V1 install. It expected a./serverload path that V1 never uses (V1 always loads the package root) and required an exact version pin where@latestis the correct V1 registration; the exact pin now applies to OpenCode 2 hosts only. On a machine where OpenCode is running,doctoralso crashed with "host probe changed the operator database": the probe's isolation canary compared the liveopencode.dbbefore and after and could not tell the running host's writes from its own. The strict canary is kept for CI; in production the probe proves its isolation positively and tolerates a live host.- Natural-language
aft_searchqueries no longer stall when the local embedding model is slow. The query-time budget (3 s, then lexical results with a note) applied to remote embedding backends but not to the bundled ONNX one, so a slow inference held the whole search for as long as it took. The local embed now runs under the same budget on its own worker, a query that exceeds it falls back to lexical search, the late vector is kept for the next identical query, and ONNX worker threads are capped by the container's CPU quota instead of the visible core count. Identifier-shaped queries were unaffected, which is why single-word searches kept working. (#319) - Retrying a search after a timeout no longer fails with "re-verification of live memo entry is rejected". The exact-match memo marked a query verified before its verification ran; an abandoned attempt burned the epoch and the identical retry was refused. Verification is single-flight per query: a concurrent request joins it, an abandoned or failed one leaves the query retryable. (#319)
- Multi-word exact matching skips files that cannot contain the phrase. Every candidate file was scanned for one- to three-line windows even when one of the query's tokens was absent; a 2,000-file corpus went from 1.6 s to 0.33 s warm on that path.
readand other tool calls no longer wait behind a longaft_inspecton the standalone bridge. Wrapped tool calls that the bare-command path already deferred (inspect, read, bash, LSP navigation) ran on the request loop when invoked throughtool_call, so a 35 s inspect timed out every sibling request and OpenCode killed the bridge. (#317)- Embedding builds against llama.cpp survive an oversized chunk. Dense rows the character-to-token estimate undercounted returned HTTP 400 and aborted the whole build; the backend is now the tokenization authority: an overflow reply bisects the batch, shrinks the offending row toward its signature, and skips it with a count if it still does not fit. (#318)
globwith apathargument matches a bare filename directly under that path.{path: "src", pattern: "a.rs"}matched against the project-relativesrc/a.rsand returned nothing; the pattern is now evaluated relative topathon both the indexed and fallback routes, in one path form on Windows.- A deleted file no longer blocks dead-code analysis for its project. Its stale callgraph row survived every refresh because nothing re-evaluated a path that no longer existed, and the projection waited on it forever; refresh now retires rows for proven-absent paths and the health report counts what remains.
- A fresh git worktree excludes
target/andnode_modules/from the file watcher from its first bind. The eight exclusion slots were filled in.gitignoreorder, so on this repository the firstcargo buildin every worktree overflowed the event stream; slots are now seeded by expected volume, and the observed ranking is shared across worktrees of one repository. Windows gets its own watcher backend with overflow reporting. - A Tier-2 dead-code pass that overruns its deadline releases its build slot even when the scanner ignores cancellation; a pass that ran for 95 minutes on one worktree had starved index builds fleet-wide. Cancellation is also checked at the scanner's file and rollup boundaries so a cooperative pass returns.
- Background-task garbage collection no longer quarantines a task whose metadata is being rewritten. A metadata file replaced under every open attempt is a live writer, not a damaged layout, and replay now finishes recovering a session before the detached sweep can see it.
- Stale inspect caches for projects not seen in seven days are swept; they had accumulated without any retention.
- Governed
ghcalls that time out after the request was sent report "outcome unknown, check before retrying" with exit status 87 instead of claiming the command was not run; retrying on that claim posted a duplicate comment.
Changes
aft_searchranks identifier-shaped tokens inside natural-language queries through the exact lane, puts declarations first for bare identifiers, renderspath:lineand the matching line on lexical rows, excludes oversized and binary files from exact matching using the indexer's own eligibility rule, and returns one row per file. On the real-query benchmark, mean reciprocal rank rose from 0.154 to 0.188 with exact and concept recall unchanged.aft_searchtopKis capped at 50. Ranks past 50 were opened in about one percent of searches while producing more than half of all search output tokens;offsetpages beyond the first fifty.- Old background-task rows in
aft.dbare pruned in small batches (terminal, delivered, no watches, over 30 days, layout gone). - Health reports carry process I/O counters, the cold-build limiter's slot holders, and Tier-2 pass timings;
aft profile --memoryno longer labels allocator slack as reclaimable.
Internal
- A launchd health sentinel for the daemon (
scripts/sentinel/) with per-detector fingerprints and cooldowns. - Content-addressed index views remain opt-in (
views.enabled); this release retires the legacy callgraph refresh on views-on roots after first publication and makes materialization incremental.