Budgeted evidence selection
For demarkus maintainers: the design for the roadmap slice that makes one budgeted lookup answer a multi-document question. Status: active (2026-10-10; branch feat/graph-context-selection from main at a8d037c0). Roadmap entry: budgeted evidence selection. Design rationale: graph focus.
What the before arms say
The OpenCode 2 before arms (r2, 2026-10-10) spend about 11% of model tokens on tool results and about 85% on the prompt replayed per turn. Cost tracks model turns, not evidence size: a three-turn attempt costs about 5,800 tokens, a seven-turn one about 17,000. Every attempt is a lookup followed by one to four fetches, with mark_explore in between for lineage, supersession and dependency questions. Budgeted expansion fired on 7 of 43 lookups because the reader policy says to omit it by default, and even then the reader fetched afterwards.
So the lever is turns. A budgeted lookup that already holds the direct passage and the one typed hop the question needs removes the explore call and one or two fetches.
Decision
Keep mark_lookup and its budget argument. Change what the shared expansion (client/lookupexpand, used by the local MCP and the gateway) puts inside the budget, in four parts.
- The budget covers the whole result. The table and envelope already printed count against it; expansion gets the remainder. When the table alone exceeds the budget the result says so and names the expansion path (raise the budget or fetch a row), instead of silently adding nothing.
- Overlapping and duplicate evidence coalesces. Rows on one document expand once per byte range: a later row whose section lies inside, or encloses, an emitted section is reported as covered by that block rather than printed again. A section whose text is byte-identical to one already printed, from another document or anchor, prints as a one-line block naming the first location, so distinct provenance and its version pin survive without a second copy. Rank order is kept; the strongest direct match always prints first.
- Typed one-hop additions come from the fetched document's own
rel-*metadata. Every expanded document prints one line per declared relation right after its block, subject, predicate, object. After every direct row has had its turn, up to three targets ofsupersedes,superseded-by,depends-on,implementsandderived-fromon the same authority expand under the remaining budget, whole when small, else the sections holding the query terms, else the outline, each headed with the relation that brought it in; a related document prints its own relation lines but is not followed further. Targets already in the table are skipped. Targets on another authority keep the line and never expand: the caller's scope and token never widen. No graph store is consulted, so the hop is fresh by construction and identical on both surfaces. Incoming relations known only to the store are a measured follow-on. - Omissions are visible. Rows that did not fit are listed with their size, capped, so the reader can fetch the one it wants. The closing note counts rows and related documents expanded.
Fetch count stays bounded by MaxFetches across direct and related documents. Body links are navigation, not evidence, and stay out of expansion.
Not in this slice
- Any new tool or default behavior. A lookup without
budgetis byte-identical to today. - Relation edges from the graph store (incoming typed relations, seeded rows). Added only if measured misses justify a store dependency inside lookup.
- Semantic deduplication. Only byte-identical text coalesces.
- Shortest paths, centrality and clustering remain evidence-gated follow-ons.
Evaluation
Three arms per frozen world (latebit v6, music v6), 16 attempts each, OpenCode 2.0.23 private-server reader:
- A: current software, policy
graph-section-first-v1. Recorded as the r2 before arms. - B: current software, new policy
graph-budget-first-v1: identical to A except the reader passesbudget1500 on its first lookup and fetches only what the expansion did not include. Tells how much the existing expansion buys on its own. - C: this slice's
demarkus-mcp, policygraph-budget-first-v1.
compare B against C (same RunSpec; server, proxy, runner and OpenCode hashes equal; mcp hash differs) is the software result. ComparePolicies A against B is the policy ablation. A against C is reported side by side, not as a compare. The harness gains relation_expansions (typed related blocks inside a lookup result) so the feature-exercised gate accepts an after arm that no longer needs mark_explore. Acceptance follows the roadmap: no direct-answer regression, multi-document categories at equal or better correctness with fewer tokens per correct answer.
Each arm is a paid run and needs approval before it starts.