Assembling context around a question runs the same chain every time: aft_search → aft_outline the hits → aft_zoom the symbols → aft_callgraph for the neighbors. Four to six calls, each one a model round-trip, with the intermediate results held in context while the agent decides what to fetch next. The individual tools are sharp. The composition is what costs.
One call can do it deterministically, because every ingredient already exists in-process: handle_semantic_search for seeding, impact_result / call_tree_result for one-hop neighbors, render_symbol_within_budget for output.
Against this repo, just now:
aft_gather_context(question: "how does bash output compression dispatch pick a compressor")
→ seeds=15 neighbors=0 budget=400 used=264
That single pack carried the whole dispatch chain: compress → the experimental_bash_compress gate → compress_with_registry_exit_code → DispatchTarget pipeline peeling → the 20-entry compressors_in_dispatch_order() array → the Compressor trait → install_bash_compressor mirroring it for subc actors. The subc mirror is the piece a hand-built chain usually misses.
neighbors=0 because the callgraph store wasn't ready. The pack said so in its own header instead of pretending.
Shape
Two modes, mutually exclusive:
question: "how does X work?" — semantic-search-seeded
symbol + path — callgraph-seeded (depth-1 callers plus depth-1 callees)
Seeds expand one hop, dedupe by canonicalized (file, symbol) with seeds winning ties, then render verbatim bodies with file:line headers until the line budget is spent (default 400, cap 800). Everything past the cut becomes a one-line stub under ## Beyond budget (zoom to expand), so the agent can see what it didn't get.
Degradation is visible rather than silent. Semantic index still building and no symbol seed resolved: the header carries degraded=semantic-index-building. Grep-fallback hits with no containing symbol stay as file:line (no containing symbol) stubs. Unresolved external callees collapse to one summary line instead of flooding the stub list.
Deliberately out of scope: multi-hop expansion (the budget math and ranking get worse and the packs get noisier), token-counted budgets (lines match the existing zoom/outline idiom), and any caching or LLM call.
Where it is
#152, open since July 7. One commit: 797 lines of implementation in crates/aft/src/commands/gather.rs plus 845 of tests, wiring in main.rs and subc_translate.rs, the TS factory and registration, and the regenerated v0.49 manifests. CI is green on a4b6aaee — 14/14, Windows jobs included.
The ask
Do you want this capability in aft at all?
If yes, I'd rather hear the shape you want before another rebase. The name, the mode split, the budget semantics, and whether composition like this belongs in a tool or stays the agent's job are all yours to call, and I'll rework the PR to match.
If no, say so and I'll close it and carry it in the fork. That's a fine answer and cheaper for both of us than leaving it open.
Either way a direction now beats a verdict later: it's been rebased 13 times since July 8, and each upstream sync costs a rebase and a rebuild.
Assembling context around a question runs the same chain every time:
aft_search→aft_outlinethe hits →aft_zoomthe symbols →aft_callgraphfor the neighbors. Four to six calls, each one a model round-trip, with the intermediate results held in context while the agent decides what to fetch next. The individual tools are sharp. The composition is what costs.One call can do it deterministically, because every ingredient already exists in-process:
handle_semantic_searchfor seeding,impact_result/call_tree_resultfor one-hop neighbors,render_symbol_within_budgetfor output.Against this repo, just now:
That single pack carried the whole dispatch chain:
compress→ theexperimental_bash_compressgate →compress_with_registry_exit_code→DispatchTargetpipeline peeling → the 20-entrycompressors_in_dispatch_order()array → theCompressortrait →install_bash_compressormirroring it for subc actors. The subc mirror is the piece a hand-built chain usually misses.neighbors=0because the callgraph store wasn't ready. The pack said so in its own header instead of pretending.Shape
Two modes, mutually exclusive:
question: "how does X work?"— semantic-search-seededsymbol+path— callgraph-seeded (depth-1 callers plus depth-1 callees)Seeds expand one hop, dedupe by canonicalized
(file, symbol)with seeds winning ties, then render verbatim bodies withfile:lineheaders until the line budget is spent (default 400, cap 800). Everything past the cut becomes a one-line stub under## Beyond budget (zoom to expand), so the agent can see what it didn't get.Degradation is visible rather than silent. Semantic index still building and no symbol seed resolved: the header carries
degraded=semantic-index-building. Grep-fallback hits with no containing symbol stay asfile:line (no containing symbol)stubs. Unresolved external callees collapse to one summary line instead of flooding the stub list.Deliberately out of scope: multi-hop expansion (the budget math and ranking get worse and the packs get noisier), token-counted budgets (lines match the existing zoom/outline idiom), and any caching or LLM call.
Where it is
#152, open since July 7. One commit: 797 lines of implementation in
crates/aft/src/commands/gather.rsplus 845 of tests, wiring inmain.rsandsubc_translate.rs, the TS factory and registration, and the regenerated v0.49 manifests. CI is green ona4b6aaee— 14/14, Windows jobs included.The ask
Do you want this capability in aft at all?
If yes, I'd rather hear the shape you want before another rebase. The name, the mode split, the budget semantics, and whether composition like this belongs in a tool or stays the agent's job are all yours to call, and I'll rework the PR to match.
If no, say so and I'll close it and carry it in the fork. That's a fine answer and cheaper for both of us than leaving it open.
Either way a direction now beats a verdict later: it's been rebased 13 times since July 8, and each upstream sync costs a rebase and a rebuild.