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. |
a486e48 to
c72bcfd
Compare
c72bcfd to
0ff5b89
Compare
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.
0ff5b89 to
5eb5870
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).@opencodewith XOR schema validation andpath→filePathmapping; covered by a transport-invariance test; gated to the “all” surface.Bug Fixes
/and renders a clean “file not found” stub.Written for commit 5eb5870. Summary will update on new commits.
Greptile Summary
This PR introduces
aft_gather_context, a single-call deterministic context-pack builder that replaces the multi-turn search→outline→zoom→callgraph chain. It seeds from semantic search (question mode) or callgraph impact/call-tree (symbol mode), expands 1 hop, deduplicates by(file, symbol), renders verbatim code within a hard line budget, and lists overrun candidates as visible stubs.gather.rs(Rust): composition layer callinghandle_semantic_search,impact_result,call_tree_result, andrender_symbol_within_budget; includes 22 unit tests covering mid-codepoint truncation, abs/rel path dedupe, callee suppression, and degradation flag behavior.gather.ts(TypeScript): tool definition with XOR schema validation,path→filePathtransport rename, and budget coercion; verified by a transport-invariance test.main.rsdispatch arm,subc_translate.rsmapping with path resolution,tool-registration.tsregistration gated to the "all" surface (same tier asaft_callgraph).Confidence Score: 4/5
impact_resultcall insidecollect_callgraph_neighborshardcodesfalseforinclude_testsinstead of forwarding the caller-supplied value. When an agent passesincludeTests: true, test-file callers are silently excluded from the neighbor list while callees are correctly included — an asymmetry that makes the flag partially ineffective. Symbol mode is unaffected (it callsimpact_resultwith the correct value). Everything else — deduplication, budget accounting, stub rendering, path normalization, degradation flags, and transport wiring — is well-implemented and thoroughly tested.Important Files Changed
include_testsis hardcoded tofalseincollect_callgraph_neighborsfor theimpact_result(callers) path, causingincludeTests: trueto be silently ignored for test callers in question mode while callees are correctly filtered. Symbol mode is unaffected. 22 unit tests cover regressions thoroughly.Sequence Diagram
sequenceDiagram participant Agent participant TS as gather.ts participant Translate as subc_translate participant Rust as gather.rs participant Search as semantic_search participant CG as Callgraph Store Agent->>TS: aft_gather_context with question or symbol+path TS->>TS: XOR mode validation and path to filePath rename TS->>Translate: toolCall gather with filePath Translate->>Translate: XOR validation and resolve filePath from project root Translate->>Rust: RawRequest command gather alt question mode Rust->>Search: "handle_semantic_search query top_k=15" Search-->>Rust: results with symbol seeds and grep-fallback hits Rust->>Rust: resolve_containing_symbol for grep-fallback hits Rust->>Rust: sort seeds by score and assign seed_ordinal Rust->>CG: collect_callgraph_neighbors 1-hop callers and callees CG-->>Rust: neighbor candidates else symbol mode Rust->>CG: "impact_result callers depth=1" CG-->>Rust: callers Rust->>CG: "call_tree_result callees depth=1" CG-->>Rust: callees end Rust->>Rust: dedupe by file+name with seeds winning Rust->>Rust: build_pack render each candidate within per_symbol_budget Rust->>Rust: build header with exact used count and append stubs Rust-->>TS: text with gather pack and verbatim bodies TS-->>Agent: context pack stringReviews (23): Last reviewed commit: "gather: deterministic context-pack tool ..." | Re-trigger Greptile