gather: deterministic context-pack tool (aft_gather)#152
Open
iceteaSA wants to merge 1 commit into
Open
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
7ed6af0 to
f8e7fa9
Compare
One call assembles a bounded context pack — ranked, deduped, budgeted verbatim code evidence — replacing the serial search → outline → zoom → callgraph read chain. - Two modes (XOR): question (seeds via handle_semantic_search, same pipeline as aft_search) or symbol+filePath (callgraph impact depth-1 callers + call_tree depth-1 callees) - 1-hop neighbor expansion; dedupe by canonicalized (file, symbol), seeds win - Hard line budget (default 400, cap 800) via render_symbol_within_budget; over-budget candidates emit as visible stubs — nothing silently dropped - Grep-fallback hits resolve to their containing symbol by line containment; no-symbol hits stay visible stubs - Semantic-index-building degradation flagged in the pack header (semantic_status), suppressed when any symbol-level seed resolves - Unresolved external callees collapse to one summary line; unresolved seeds/callers never suppressed - Follows the tri-state honest-reporting convention (protocol.rs Response doc-comment) - 22 unit tests incl. red-checked regressions; no new dependencies, no LLM calls, no caching
f8e7fa9 to
cf09b9b
Compare
Author
|
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. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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, a single-call context-pack builder that returns ranked, deduped, verbatim code within a fixed line budget, replacing the search→outline→zoom→callgraph chain. Supports question-seeded orsymbol+filePathcallgraph-seeded packs with 1‑hop neighbors and clear degradation/availability notices.New Features
question(semantic seeds) orsymbol+filePath(impact callers + call-tree callees); 1‑hop expansion; dedupe by(file, symbol)with seeds winning.render_symbol_within_budget; overflow appears under “Beyond budget”; unresolved external callees collapse to a single summary line; seeds/callers remain visible stubs.semantic-index-buildingdegradation and skipped neighbors when the callgraph store is unavailable.gatherin Rust dispatch withtranslate_gather; addedaft_gathertool in@opencode(tier: “all”). No new dependencies. 22 tests.Migration
{ question: "how does X work?" }or{ symbol: "foo", filePath: "src/foo.rs" }; optionalbudget1–800 (default 400).symbolmode. Inquestionmode, neighbors are skipped with a header notice if unavailable.Written for commit cf09b9b. Summary will update on new commits.
Greptile Summary
This PR introduces
aft_gather, a one-call context-pack builder that replaces the multi-turnsearch → outline → zoom → callgraphchain by assembling ranked, deduped, verbatim code evidence within a configurable line budget. The implementation is a new Rust command (gather.rs), wired into the dispatch table andsubc_translate, plus a TypeScript tool definition in theopencode-pluginpackage.question(semantic search → 1-hop callgraph expansion) andsymbol+filePath(impact callers + call-tree callees) — feed into a sharedbuild_packpath that deduplicates by(file, name), renders viarender_symbol_within_budget, and lists overflow candidates as stubs. The callgraph store correctly normalizes relative paths via its ownproject_root, so seeds from semantic search (normalized to repo-relative form) are handled correctly.used=post-processing splice has been addressed in this revision.Confidence Score: 5/5
Safe to merge — no correctness defects on any call path; all findings are non-blocking style and dead-code observations.
The callgraph store's
nodes_forcorrectly normalizes relative paths via its ownproject_root, so the seed paths from semantic search (normalized to repo-relative form incollect_callgraph_neighbors) resolve correctly. Budget accounting, deduplication, degradation flagging, and stub suppression are all correct and backed by 22 unit tests including regression cases. The only issues are an unreachable neighbor loop, a misleading comment on an unused field, and a missing lower bound on the Rust-side budget — none affect runtime behavior.No files require special attention.
gather.rswarrants a second look only for the dead orphan-neighbor loop at lines 479–486.Important Files Changed
hop_distancefield with misleading comment).Sequence Diagram
%%{init: {'theme': 'neutral'}}%% sequenceDiagram participant Agent participant gather.ts participant handle_gather (Rust) participant handle_semantic_search participant CallGraphStore participant render_symbol_within_budget Agent->>gather.ts: "aft_gather({ question | symbol+filePath, budget })" gather.ts->>handle_gather (Rust): dispatch "gather" alt question mode handle_gather (Rust)->>handle_semantic_search: top_k=15 semantic query handle_semantic_search-->>handle_gather (Rust): results[] + semantic_status handle_gather (Rust)->>handle_gather (Rust): resolve containing symbols (grep-fallback hits) handle_gather (Rust)->>CallGraphStore: impact_result per seed (1-hop callers) handle_gather (Rust)->>CallGraphStore: call_tree_result per seed (1-hop callees) else symbol mode handle_gather (Rust)->>CallGraphStore: impact_result (callers of symbol) handle_gather (Rust)->>CallGraphStore: call_tree_result (callees of symbol) end handle_gather (Rust)->>handle_gather (Rust): dedupe by (file, name), seeds win loop each candidate (budget remaining) handle_gather (Rust)->>render_symbol_within_budget: render within per_symbol_budget render_symbol_within_budget-->>handle_gather (Rust): content + status (Complete|Truncated|Menu) end handle_gather (Rust)->>handle_gather (Rust): build header (used=N), append stubs section handle_gather (Rust)-->>gather.ts: { text: pack } gather.ts-->>Agent: verbatim pack with file:line headers%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%% sequenceDiagram participant Agent participant gather.ts participant handle_gather (Rust) participant handle_semantic_search participant CallGraphStore participant render_symbol_within_budget Agent->>gather.ts: "aft_gather({ question | symbol+filePath, budget })" gather.ts->>handle_gather (Rust): dispatch "gather" alt question mode handle_gather (Rust)->>handle_semantic_search: top_k=15 semantic query handle_semantic_search-->>handle_gather (Rust): results[] + semantic_status handle_gather (Rust)->>handle_gather (Rust): resolve containing symbols (grep-fallback hits) handle_gather (Rust)->>CallGraphStore: impact_result per seed (1-hop callers) handle_gather (Rust)->>CallGraphStore: call_tree_result per seed (1-hop callees) else symbol mode handle_gather (Rust)->>CallGraphStore: impact_result (callers of symbol) handle_gather (Rust)->>CallGraphStore: call_tree_result (callees of symbol) end handle_gather (Rust)->>handle_gather (Rust): dedupe by (file, name), seeds win loop each candidate (budget remaining) handle_gather (Rust)->>render_symbol_within_budget: render within per_symbol_budget render_symbol_within_budget-->>handle_gather (Rust): content + status (Complete|Truncated|Menu) end handle_gather (Rust)->>handle_gather (Rust): build header (used=N), append stubs section handle_gather (Rust)-->>gather.ts: { text: pack } gather.ts-->>Agent: verbatim pack with file:line headersReviews (3): Last reviewed commit: "gather: deterministic context-pack tool ..." | Re-trigger Greptile