gather: deterministic context-pack tool (aft_gather) - #152
Conversation
There was a problem hiding this comment.
4 issues found across 7 files
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
f8e7fa9 to
cf09b9b
Compare
|
Follow-up on the two maintainability notes from the Greptile summary (they weren't separate review threads, so noting here) — both addressed in
Matched/rendered text is byte-identical; 23/23 gather tests green. |
1d068c9 to
d2e15e6
Compare
5364eac to
f42c5ec
Compare
f42c5ec to
66e05e2
Compare
66e05e2 to
d20f276
Compare
|
Rebased onto current The rename covers the advertised tool name and the agent-facing error strings only. The wire command stays Rebase conflicts were confined to Verified: Still one commit. Two notes on the full plugin suite, both pre-existing on |
b06cd3b to
a4b6aae
Compare
|
Opened #192 to ask the design question separately from the diff: whether you want one-call context packs in aft at all, and in what shape. Answer there and I'll rework this to match — or close it if the answer is no. Current state here: green on |
a4b6aae to
500f1a9
Compare
|
Rebased onto v0.49.3 ( CI: 13 green, one red — Scenario 1's
Same job, same assertions, on the two nearest green runs:
So the same code path passes on the parent commit and on the previous revision of this branch. The delta between them is a 90s wall clock on a cold Windows runner, not the diff — nothing in this change is reachable from bridge spawn or plugin boot. I can't re-run it — Separately: #192 asks whether you want this capability at all, which is the more useful question than another rebase. |
500f1a9 to
9bb5af3
Compare
|
Rebased onto
The four new commits didn't touch any file this branch touches, so the rebase was clean and only the governed manifests needed regenerating for the new base. |
Normalize path separators before comparison so absolute and relative spellings of the same file dedupe on Windows. Path::is_absolute() is false for rooted-no-drive paths there, and strip_prefix emits backslashes, so the same symbol previously rendered twice in one pack. Backslash is a legal filename character on Unix, so a file literally named 'a\b.rs' now normalizes to 'a/b.rs' and renders a not-found stub. Accepted: matches normalize_path_for_compare, fails gracefully.
9bb5af3 to
a486e48
Compare
gather: deterministic context-pack tool (aft_gather)
What
One new tool —
aft_gather— assembles a bounded "context pack" (ranked, deduped, budgeted verbatim code evidence) in a single call. It replaces the multi-turnsearch → outline → zoom → callgraphread chain an agent otherwise runs to build context around a question or a symbol.Two modes (mutually exclusive):
question: "how does X work?"— seeds fromhandle_semantic_search(same pipeline asaft_search, all lanes/fallbacks)symbol+filePath— seeds from the callgraph (impactdepth-1 callers +call_treedepth-1 callees)Seeds expand one hop through the callgraph, dedupe by canonicalized (file, symbol) with seeds winning, and render via
render_symbol_within_budgetuntil a hard line budget (default 400, cap 800) is spent. Everything past the cut appears as one-line stubs under## Beyond budget (zoom to expand)— nothing is silently dropped.Why
Agents burn serial turns assembling context: search, then outline the hits, then zoom the symbols, then chase callers. Each turn round-trips through the model. A pack returns evidence (verbatim bodies with file:line headers), not conclusions — the agent reasons over it directly, ready to attach to a subagent dispatch.
Measured on a real config repo (3 questions, one call each vs. the manual chain):
used=283/400. Manual baseline: 5-6 tool calls.budget=200.Independently reproduced on the aft codebase itself:
question: "how does bash output compression dispatch pick a compressor"→seeds=15, used=226/400, one pack assembling the full dispatch chain (compress→ gate →compress_with_registry_exit_code20-compressor array →Compressortrait → install path → subc mirror) that otherwise takes a 4–5-call search→zoom chain.Honest degradation
The pack never lies about its own quality:
file:line (no containing symbol)stubs and flags the header withdegraded=semantic-index-building (partial results — retry when index ready)— detected via the response'ssemantic_statusfield, cleared as soon as one real seed resolves. No blocking or retry inside the tool.{file, line_text, line}, no symbol name) resolve to their containing symbol by line containment — definitions, call sites, and comment hits all upgrade to the enclosing symbol. Hits with no containing symbol stay visible as stubs.(N unresolved external calls omitted)) instead of drowning the stub list; unresolved seeds and callers are never suppressed.Implementation
crates/aft/src/commands/gather.rs— Rust-side composition: callshandle_semantic_search/impact_result/call_tree_result/render_symbol_within_budgetdirectly (shared&AppContext, no bridge round-trips, no parallel reimplementation of search).main.rsdispatch arm,subc_translate.rsmapping, TS factorypackages/opencode-plugin/src/tools/gather.ts+ registration (same tier asaft_callgraph— depends on the callgraph store).protocol.rsResponsedoc-comment):success:false+codefor un-performable calls (e.g.invalid_requeston a bad mode combo),success:truewith a visible degraded/stub pack for partial results — never a bare empty success.Tests
22 unit tests in
gather.rs, including red-checked regressions (each confirmed to fail against pre-fix code): mid-codepoint truncation panic, duplicate-symbol line-anchored resolution, abs/rel path dedupe, containing-symbol resolution via a realTreeSitterProvider, callee-only stub suppression driven through the productionbuild_packpath, and degradation-flag presence/absence/mixed cases.Limitations (deliberate scope)
aft_callgraph.Need help on this PR? Tag
/codesmithwith what you need. Autofix is disabled.Summary by cubic
Adds
aft_gather_context, a single-call, deterministic context-pack builder that returns ranked, deduped, verbatim code within a fixed line budget, replacing the multi-step search→outline→zoom→callgraph chain. Exposed asgatherin Rust andaft_gather_contextin@opencodeon the “all” surface.New Features
question(semantic seeds) orsymbol+path(impact callers + call-tree callees); 1-hop expansion with(file, symbol)dedupe and seeds winning.degraded=semantic-index-buildingandneighbors=skipped(callgraph-unavailable)./and renders a clean “file not found” stub.@opencodewith XOR schema validation andpath→filePathtransport mapping, covered by a transport-invariance test; gated to the “all” surface.Migration
{ question }or{ symbol, path }; optionalbudget1–800.Written for commit a486e48. Summary will update on new commits.
Greptile Summary
This PR introduces
aft_gather_context, a single-call context-pack builder that replaces a multi-turnsearch → outline → zoom → callgraphchain. It seeds from either a natural-language question (semantic search) or asymbol+pathpair (callgraph impact/call-tree), expands one hop through the callgraph, deduplicates by(file, symbol), and renders verbatim code within a hard line budget with honest degradation flags.gather.rs, 1,642 lines): two dispatch paths (handle_gather_question/handle_gather_symbol), sharedbuild_packwith budget accounting, grep-fallback symbol resolution, callee-only suppression of unresolved external callees, and 22 unit tests including regression guards.gather.ts) wired into the"all"tool surface viatool-registration.ts, withpath→filePathtransport mapping and client-side XOR validation mirroring the Rust-side checks.main.rs,translate_gatherinsubc_translate.rswith path resolution, and documentation updates acrossARCHITECTURE.md,README.md, andSTRUCTURE.md.Confidence Score: 4/5
normalize_file_pathstrips the project root, the relative seed path is passed toimpact_resultandcall_tree_result, both of which index files by absolute path. The mismatch is swallowed silently, so every question-mode pack ships withneighbors=0regardless of callgraph availability. The 1-hop expansion advertised as the core value-add of the tool only functions in symbol mode until this is fixed.Important Files Changed
Flowchart
%%{init: {'theme': 'neutral'}}%% flowchart TD A[Agent calls aft_gather_context] --> B{Mode?} B -->|question| C[handle_gather_question] B -->|symbol plus path| D[handle_gather_symbol] C --> E[handle_semantic_search top_k=15] E --> F[resolve grep-fallback hits to containing symbol] F --> G[sort seeds by score] G --> H[collect_callgraph_neighbors] H --> H1["impact_result with relative seed.file - BUG: store expects absolute"] H --> H2["call_tree_result with relative seed.file - BUG: store expects absolute"] H1 --> I[neighbors always empty in question mode] H2 --> I D --> J[validate_path returns absolute path] J --> K[impact_result with absolute path] J --> L[call_tree_result with absolute path] K --> M[callers collected] L --> N[callees collected] I --> O[build_pack] M --> O N --> O O --> P[dedup by file and name seeds win] P --> Q[render per_symbol_budget with stubs for overflow] Q --> R[build header last with exact used count] R --> S[Return pack text to agent]Reviews (20): Last reviewed commit: "gather: deterministic context-pack tool ..." | Re-trigger Greptile