Reply diet: semantics once per session, provenance scoped to the answer, grep-parity line text (zero-fallback) #557

Closed
opened 2026-11-09 12:29:10 +01:00 by buildagent · 9 comments
Member

Part of the zero-fallback epic (#555). Cause 3: a reply must not cost more than the shell answer it replaces.

Observed (lead session 2026-11-09, 6 linked trees)

  • Every reply of every tool carries evidence_gaps with its full semantics paragraph, index_snapshot, and answer_provenance.indexed_trees listing ALL linked worktrees (6 entries), even when the call targeted one project. That is roughly 1 KB of fixed envelope per reply (observed, not yet measured exactly; sub-issue 1 measures it).
  • search_text hits carry snippet_origin_semantics (a paragraph) per reply. find_callers carries confidence.verification, graph_semantics and symbol_shape_excluded_semantics paragraphs every time.
  • A one-line grep -n answer costs a fraction of that.

Proposal: say each thing once per session, and only what changed

  1. Semantics by reference: each *_semantics/verification paragraph ships the first time it appears in a session, after that as a short key ("semantics_ref": "evidence_gaps/v3"), resolvable via a resource (code-index://docs/..., which already exists for cursors and ref kinds). Disclosure is preserved, because the agent saw it once and can re-read it.
  2. Provenance scoped to the answer: answer_provenance.indexed_trees lists only the projects that answered, plus any tree whose state is abnormal (restarted, reconciling, wrong-root suspicion). Others are summarised as a count.
  3. Zeros stay explicit, noise does not: evidence_gaps omitted when this reply names no partial source AND none could affect it. Keep the field when it matters (a count is a floor).
  4. grep parity: search_text returns line text for the matched lines by default (bounded), because that is what grep -n gives and why agents switch.
    Every removal keeps the honesty contract: a disclosure that changes the meaning of THIS answer is never dropped.

Acceptance

  • On the sub-issue-1 benchmark, median reply bytes for search_symbols/search_text/find_callers/read_code drop by ≥ 40% with no lost disclosure. A registry test lists every semantics key and proves each is served once per session and resolvable.
  • Mutation: always inline the semantics → the byte ceiling test goes red.
Part of the zero-fallback epic (#555). Cause 3: **a reply must not cost more than the shell answer it replaces.** ## Observed (lead session 2026-11-09, 6 linked trees) - Every reply of every tool carries `evidence_gaps` with its full `semantics` paragraph, `index_snapshot`, and `answer_provenance.indexed_trees` listing ALL linked worktrees (6 entries), even when the call targeted one project. That is roughly 1 KB of fixed envelope per reply (observed, not yet measured exactly; sub-issue 1 measures it). - `search_text` hits carry `snippet_origin_semantics` (a paragraph) per reply. `find_callers` carries `confidence.verification`, `graph_semantics` and `symbol_shape_excluded_semantics` paragraphs every time. - A one-line `grep -n` answer costs a fraction of that. ## Proposal: say each thing once per session, and only what changed 1. **Semantics by reference:** each `*_semantics`/`verification` paragraph ships the first time it appears in a session, after that as a short key (`"semantics_ref": "evidence_gaps/v3"`), resolvable via a resource (`code-index://docs/...`, which already exists for cursors and ref kinds). Disclosure is preserved, because the agent saw it once and can re-read it. 2. **Provenance scoped to the answer:** `answer_provenance.indexed_trees` lists only the projects that answered, plus any tree whose state is abnormal (restarted, reconciling, wrong-root suspicion). Others are summarised as a count. 3. **Zeros stay explicit, noise does not:** `evidence_gaps` omitted when this reply names no partial source AND none could affect it. Keep the field when it matters (a count is a floor). 4. **grep parity:** `search_text` returns line text for the matched lines by default (bounded), because that is what `grep -n` gives and why agents switch. Every removal keeps the honesty contract: a disclosure that changes the meaning of THIS answer is never dropped. ## Acceptance - On the sub-issue-1 benchmark, median reply bytes for `search_symbols`/`search_text`/`find_callers`/`read_code` drop by ≥ 40% with no lost disclosure. A registry test lists every semantics key and proves each is served once per session and resolvable. - Mutation: always inline the semantics → the byte ceiling test goes red.
Author
Member

Input from worker dogfood (codex-531): change_impact(limit=0) is documented as "count-only" for the three evidence lists, but test_role_files is still answered in full, which was hundreds of paths on busy.rs/index.rs seeds. That is by the tool's own contract, but an agent asking for counts pays for a path list. Proposal for this issue: limit=0 (or a test_role_files_limit) makes the reply truly count-only, with the full list one call away.

Input from worker dogfood (codex-531): `change_impact(limit=0)` is documented as "count-only" for the three evidence lists, but `test_role_files` is still answered in full, which was hundreds of paths on `busy.rs`/`index.rs` seeds. That is by the tool's own contract, but an agent asking for counts pays for a path list. Proposal for this issue: `limit=0` (or a `test_role_files_limit`) makes the reply truly count-only, with the full list one call away.
Author
Member

The lever already exists, but no agent sees it (lead measurement 2026-11-09)

envelope: "minimal" ("no prose, same fields") is implemented and works.

  • Measured: an identical search_symbols("detect_root", concise) returned about 2,300 chars with the default full envelope and about 1,100 chars with minimal (about −50%). It drops only answer_provenance, index_snapshot and every *.semantics paragraph. It keeps every field and state (partial_sources_in_index, index_state: reconciling) and points to code-index://docs/envelope.
  • Why nobody uses it: the option is named in the LAST line of the server instructions (server.rs ~4154), and clients truncate the instructions (this session's copy ends in [truncated], see #521). Five worker sessions and the lead never used it.
  • The default is pinned to full on purpose: tool_ux_e3_e2e.rs the_default_envelope_is_unchanged_and_a_bad_mode_is_refused (FINDING 5: "default == explicit full, byte for byte").

Interim (done, integration-1109 824e65d5): CLAUDE.md and AGENTS.md tell every agent to pass envelope: "minimal" + response_format: "concise".

Proposal for this issue (replaces item 1 of the body): make the session pay for prose ONCE.

  • The first reply in a session that carries a given semantics key ships it in full. Later replies ship that key minimal with semantics_ref.
  • answer_provenance ships only when it changed since the last reply in this session, or is abnormal (restart, reconcile, wrong-root suspicion).
  • envelope: "full" stays available per call. A server flag or env var can force the old default for clients that cannot keep state.
  • Move the envelope hint to the FIRST lines of the instructions until then.

Acceptance: the FINDING 5 test is replaced by a session test (first reply full, second minimal, identical fields), and the median reply bytes on the #556 benchmark drop ≥ 40%.

## The lever already exists, but no agent sees it (lead measurement 2026-11-09) `envelope: "minimal"` ("no prose, same fields") is implemented and works. - **Measured:** an identical `search_symbols("detect_root", concise)` returned about 2,300 chars with the default full envelope and about 1,100 chars with `minimal` (**about −50%**). It drops only `answer_provenance`, `index_snapshot` and every `*.semantics` paragraph. It keeps every field and state (`partial_sources_in_index`, `index_state: reconciling`) and points to `code-index://docs/envelope`. - **Why nobody uses it:** the option is named in the LAST line of the server instructions (`server.rs` ~4154), and clients truncate the instructions (this session's copy ends in `[truncated]`, see #521). Five worker sessions and the lead never used it. - **The default is pinned to full on purpose:** `tool_ux_e3_e2e.rs` `the_default_envelope_is_unchanged_and_a_bad_mode_is_refused` (FINDING 5: "default == explicit full, byte for byte"). Interim (done, `integration-1109` `824e65d5`): CLAUDE.md and AGENTS.md tell every agent to pass `envelope: "minimal"` + `response_format: "concise"`. **Proposal for this issue (replaces item 1 of the body):** make the session pay for prose ONCE. - The first reply in a session that carries a given semantics key ships it in full. Later replies ship that key `minimal` with `semantics_ref`. - `answer_provenance` ships only when it changed since the last reply in this session, or is abnormal (restart, reconcile, wrong-root suspicion). - `envelope: "full"` stays available per call. A server flag or env var can force the old default for clients that cannot keep state. - Move the `envelope` hint to the FIRST lines of the instructions until then. Acceptance: the FINDING 5 test is replaced by a session test (first reply full, second minimal, identical fields), and the median reply bytes on the #556 benchmark drop ≥ 40%.
Author
Member

Two more measured inputs for the reply diet (codex-512 dogfood, D4 and D10):

  • A batched read_code can exceed a useful output budget. Several ranges come back in full, even when the caller only needed a few lines of each.
  • A concise changed_symbols reply is still bulky for a cross-cutting edit. Each touched symbol repeats per-symbol detail that a per-file summary would cover.
Two more measured inputs for the reply diet (codex-512 dogfood, D4 and D10): - A batched `read_code` can exceed a useful output budget. Several ranges come back in full, even when the caller only needed a few lines of each. - A `concise` `changed_symbols` reply is still bulky for a cross-cutting edit. Each touched symbol repeats per-symbol detail that a per-file summary would cover.
Author
Member

Done on branch agy-557 (877ca038, Gemini 4) and merged into integration-1109 (f9b9fa64). Nothing is pushed yet.

  • The session envelope is the new default. Each (json_pointer, prose) pair ships in full the first time it appears in an MCP session. Later replies drop it and carry semantics_ref: "code-index://docs/envelope" instead. Prose whose text changes ships again.
  • answer_provenance ships only when it changed (head, uncommitted changes, root, build) or is abnormal: a daemon restart, build skew, unavailable or unmeasured tree state, a reconciling index, or a measured-absence error.
  • Prose that is always kept: symbol_shape_excluded_semantics; evidence_gaps.semantics when an unmeasured population, unfollowed name fallback or verdict downgrade is present; context_pack, which measures its own budget.
  • Opt-outs: envelope: "full" per call, or CODE_INDEX_ENVELOPE=full for clients that keep no state. envelope: "minimal" is unchanged. The minimal hint is now line 1 of the instructions, and docs/envelope covers every semantics key.
  • Measured on the second call of a session:
Tool Before (bytes) After (bytes) Change
search_symbols 1049 596 −43%
search_text 980 527 −46%
find_callers 1478 796 −46%
read_code 1082 895 −17%
  • Tests: tool_ux_e3_e2e, with session dedup and a provenance change or restart. Mutations ran red: "always minimal", "never minimal", "always omit provenance" and "omit after call 1".
  • Rules fix (75c3a12c, CLAUDE.md and AGENTS.md): response_format: "concise" is now passed only where a tool offers it. The blanket rule made read_code and project_overview refuse the first call (worker dogfood D1).
Done on branch `agy-557` (`877ca038`, Gemini 4) and merged into `integration-1109` (`f9b9fa64`). Nothing is pushed yet. - **The session envelope is the new default.** Each `(json_pointer, prose)` pair ships in full the first time it appears in an MCP session. Later replies drop it and carry `semantics_ref: "code-index://docs/envelope"` instead. Prose whose text changes ships again. - **`answer_provenance`** ships only when it changed (head, uncommitted changes, root, build) or is abnormal: a daemon restart, build skew, unavailable or unmeasured tree state, a reconciling index, or a measured-absence error. - **Prose that is always kept:** `symbol_shape_excluded_semantics`; `evidence_gaps.semantics` when an unmeasured population, unfollowed name fallback or verdict downgrade is present; `context_pack`, which measures its own budget. - **Opt-outs:** `envelope: "full"` per call, or `CODE_INDEX_ENVELOPE=full` for clients that keep no state. `envelope: "minimal"` is unchanged. The minimal hint is now line 1 of the instructions, and `docs/envelope` covers every semantics key. - **Measured** on the second call of a session: | Tool | Before (bytes) | After (bytes) | Change | |---|---:|---:|---:| | `search_symbols` | 1049 | 596 | −43% | | `search_text` | 980 | 527 | −46% | | `find_callers` | 1478 | 796 | −46% | | `read_code` | 1082 | 895 | −17% | - **Tests:** `tool_ux_e3_e2e`, with session dedup and a provenance change or restart. Mutations ran red: "always minimal", "never minimal", "always omit provenance" and "omit after call 1". - **Rules fix (`75c3a12c`, CLAUDE.md and AGENTS.md):** `response_format: "concise"` is now passed only where a tool offers it. The blanket rule made `read_code` and `project_overview` refuse the first call (worker dogfood D1).
Author
Member

codex-531 dogfood D4: change_impact with count-only + concise still returned total_affected: 1621 plus 239 test-role file paths. A real counts-only shape is wanted: totals only, plus a resource link for the run set.

codex-531 dogfood D4: `change_impact` with count-only + `concise` still returned `total_affected: 1621` plus 239 test-role file paths. A real counts-only shape is wanted: totals only, plus a resource link for the run set.
Author
Member

Status (local integration-1109 @ 60368c0d, full gate run green): payload ratchets (codex-f2).

  • The review_diff description is trimmed, which brings the startup reserve to 627 ≥ 622.
  • The overview budget test forces full envelopes and uses Reserve::Required{64}.
  • The warmup test asserts that prose arrives on first appearance.
  • The coverage census in EXTENSION_SEMANTICS now includes eligible walk-only extensions.
Status (local `integration-1109` @ 60368c0d, full gate run green): payload ratchets (codex-f2). - The review_diff description is trimmed, which brings the startup reserve to 627 ≥ 622. - The overview budget test forces full envelopes and uses `Reserve::Required{64}`. - The warmup test asserts that prose arrives on first appearance. - The coverage census in `EXTENSION_SEMANTICS` now includes eligible walk-only extensions.
Author
Member

Released in v0.41.48 (source PR #564). Shipped: disclosure prose once per MCP session, semantics_ref on later replies, answer_provenance omitted when unchanged and unremarkable, and envelope: "full" / CODE_INDEX_ENVELOPE=full to restore the full text.

🤖 Generated with Claude Code

Released in [v0.41.48](https://git.h-dv.de/h-dv/code-index/releases/tag/v0.41.48) (source PR #564). Shipped: disclosure prose once per MCP session, `semantics_ref` on later replies, `answer_provenance` omitted when unchanged and unremarkable, and `envelope: "full"` / `CODE_INDEX_ENVELOPE=full` to restore the full text. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Author
Member

One item tried in the v0.41.50 train was withdrawn before release: emptying change_impact's test_role_files at limit: 0. The agent task benchmark asks its which-tests questions with exactly that call and lost one hand-verified answer on each of two repositories. limit: 0 behaves as in v0.41.49 and additionally reports witness_paths_total. A diet for that list needs a different shape (for example a cap with a total, as review_diff has) and a benchmark plan that still gets the run-set in one call.

One item tried in the v0.41.50 train was withdrawn before release: emptying `change_impact`'s `test_role_files` at `limit: 0`. The agent task benchmark asks its which-tests questions with exactly that call and lost one hand-verified answer on each of two repositories. `limit: 0` behaves as in v0.41.49 and additionally reports `witness_paths_total`. A diet for that list needs a different shape (for example a cap with a total, as `review_diff` has) and a benchmark plan that still gets the run-set in one call.
Author
Member

Merged into #521.
Session prose/provenance dedup shipped in v0.41.48; tool_ux_e3_e2e.rs grades session behavior.
Remaining: Search evidence and filters; Reply budgets and disclosures, preserving one-call which-tests answers.

Merged into #521. Session prose/provenance dedup shipped in v0.41.48; tool_ux_e3_e2e.rs grades session behavior. Remaining: Search evidence and filters; Reply budgets and disclosures, preserving one-call which-tests answers.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Reference
h-dv/code-index#557
No description provided.