[PR #1890] [MERGED] fix(android): bound inline search Evidence with retained inventory provenance #4881

Closed
opened 2026-10-31 00:56:23 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/morluto/rea/pull/1890
Author: @akushonkamen
Created: 10/11/2026
Status: ✅ Merged
Merged: 10/11/2026
Merged by: @morluto

Base: main ← Head: fix/1850-android-search-inventory


📝 Commits (1)

  • 5a6c0e8 fix(android): bound inline search Evidence with retained inventory provenance

📊 Changes

5 files changed (+295 additions, -4 deletions)

View changed files

➕ src/application/android/AndroidSearchEvidenceSummary.ts (+120 -0)
📝 src/server/registerAndroidTools.ts (+23 -2)
➕ tests/boundary/process/androidSearchEvidenceBudget.test.ts (+131 -0)
📝 tests/fixtures/android/jadx-mcp.mjs (+11 -1)
📝 tests/fixtures/android/jadx.ts (+10 -1)

📄 Description

Summary

Focused and zero-match search_android_classes responses embedded every examined class name: a single-match search on a 7,096-class APK returned a 382,435-byte tool response because the complete raw provider inventory rode inside the inline Evidence. When the server session retains Evidence, the complete raw-call record is now written to the retained store and the tool response delivers a bounded summary Evidence that keeps the pagination audit, the unchanged normalized matches and truthful coverage metadata, and links the retained complete inventory by Evidence ID (Fixes #1850).

Problem and expected behavior

Smallest trigger: load a 7,096-class APK through the jadx bridge and call search_android_classes with a query matching exactly one class. Prior behavior: the response's inline raw_result contained every list_classes page with all 7,096 class names, because the raw provider call record was captured whole and serialized into every tool response. The violated invariant: a bounded, focused search response should not grow with the size of the unfiltered inventory, and a single match must not cost the full class list on the wire. Expected behavior: the inline Evidence carries the pagination audit (total, offset, limit, per-page items_omitted), an inventory_elided marker, the unchanged normalized matches and coverage: complete / total_classes, and evidence_links + complete_evidence_id pointing at the retained complete record; sessions without retention keep the complete inline delivery, so CLI JSON output is unchanged and no provenance is discarded.

Change and scope

Root cause chain: src/android/JadxSession.ts records each list_classes page's complete MCP envelope into #observedCalls (src/android/JadxSession.ts:220-221); those calls ride into the execution as rawResult through createAnalysisExecution (src/android/JadxSession.ts:145-152); src/application/android/AndroidAnalysisService.ts embeds them into the Evidence (src/application/android/AndroidAnalysisService.ts:44-55); and src/server/contractToolHandler.ts serializes the complete Evidence into every tool response (src/server/contractToolHandler.ts:66-71). The normalized result (src/domain/android/JadxAnalysis.ts:175) is itself bounded; only the raw-call record is not.

Fix: new src/application/android/AndroidSearchEvidenceSummary.ts, applied on the search_android_classes registration path in src/server/registerAndroidTools.ts. When the session retains Evidence, the complete Evidence (with the full raw-call inventory) is recorded first via recordEvidence, then a summary Evidence is delivered with items_omitted counts replacing the full per-page name lists. Sessions without retention skip summarization entirely, preserving the previous complete inline behavior for CLI consumers. Intentionally not included: no change to the normalized search contract, no change to non-search Android tools, and no budgeting for retention-free sessions.

Contract and boundary impact

  • Semantic owner and earliest changed stage: the MCP tool-response boundary in src/server/registerAndroidTools.ts, after the normalized result and raw-call record exist and before serialization.
  • CLI and MCP/tool-catalog contract: the advertised search_android_classes schema is unchanged and the normalized result is byte-identical; the inline raw_result for retaining sessions now elides inventory items behind items_omitted counts plus complete_evidence_id, which is an output-size bound, not a shape change to the advertised contract.
  • Provider, bridge, target-format, or platform compatibility: none; the jadx bridge protocol is untouched (the fixture updates only extend the test protocol fixture to paginate at the same limits as the real bridge).
  • Evidence, artifact, provenance, or reconstruction contract: no provenance is discarded — the complete raw-call record is retained and addressable by Evidence ID through evidence_links; observed vs. elided content remains distinguishable via inventory_elided and items_omitted.
  • Process execution, authorization, cleanup, or containment impact: none; no new process, engine, or target acquisition.
  • Generated metadata (docs/public/product-catalog.json), package, or installation impact: none; no catalog, package, or installation contract changes and no generated outputs committed.

Evidence and regression coverage

  • Tests added or updated: tests/boundary/process/androidSearchEvidenceBudget.test.ts drives a real 7,096-class jadx protocol fixture through the MCP server and asserts (1) with a retaining session: the single-match and zero-match inline raw_result contain no class names, stay under 32 KiB, carry items_omitted, and link a retained complete record whose raw calls hold all 7,096 names; (2) without a retaining session: the complete inventory stays inline.
  • Base reproduction or other evidence: with the src/ change removed (test and fixtures in place) on the rebased base, the retaining-session case fails exactly on the inline-raw assertion — the single-match response contains the full 7,096-name inventory, i.e. the issue symptom.
  • User-visible CLI/MCP output: MCP responses for retaining sessions shrink from ~382 KB to a bounded summary for focused/zero-match searches; complete CLI JSON output is unchanged.
  • Remaining proof gaps: no real-device or real-APK lane was run; the fixture reproduces the real bridge's pagination envelope at the same 4,096 page limit.

For evidence-bearing changes:

  • Observed, derived, and inferred claims remain distinguishable.
  • Artifact identity, source provenance, and failed attempts remain preserved.
  • Unsupported, incomplete, unavailable, or uncertain outcomes remain visible.

Validation performed

Rebased onto origin/main (fa9a7e5e, which touches src/domain/evidence.ts), artifacts refreshed with npm run test:prepare, then:

  • npx vitest run --project process-boundary tests/boundary/process/androidSearchEvidenceBudget.test.ts with the src/ fix removed — 1 failed / 1 passed; the failure is the bounded-Evidence assertion with the full 7,096-name inventory present in a single-match response (base reproduction).
  • Same command with the fix restored — Test Files 1 passed, Tests 2 passed.
  • npx vitest run tests/boundary/android/ tests/boundary/process/ src/server/ src/domain/android/ src/contracts/android/ — 36 files passed, 237 passed + 2 skipped, 0 failed.
  • npx vitest run src/android/ src/server/ — 2 files passed, 12 passed. (Note: npx vitest run src/android/ alone exits 1 with "No test files found"; src/android/ has no test files in this repository.)
  • npx vitest run tests/acceptance/tools/localToolContracts.test.ts tests/boundary/mcp/ — 97 files (95 passed + 2 skipped), 370 passed + 3 skipped, 0 failed.
  • npx tsc --noEmit — exit 0.
  • npx oxlint — 0 errors (the max-params(5) warning on registerAndroidTools is pre-existing at origin/main); npx oxfmt --check src/ tests/ — clean.
  • Real Hopper/Ghidra, browser, or OS lanes: not exercised; the change is covered by the jadx protocol fixture suites above.

Compatibility, safety, and release

  • Breaking changes or migration steps: none. Retaining-session MCP consumers that parsed the full inventory out of the inline raw record should read the retained complete record via evidence_links/complete_evidence_id; CLI output (no retention) is byte-identical in shape.
  • Real Hopper/Ghidra, browser, or OS coverage: not applicable; Android jadx bridge fixture only.
  • Package or release metadata impact: none.
  • Security, privacy, process, or containment review: bounds a response-size amplification (one focused query cost the entire class inventory on the wire); no new network, process, or authority surface.

Review checklist

  • This PR addresses a concrete problem or an agreed enhancement.
  • The PR has one focused outcome and the title follows type(scope): outcome.
  • Related issue is linked, or the reason for not linking one is stated above.
  • Tests cover changed observable behavior and meaningful failure paths.
  • Owning docs, contracts, and generated metadata are updated where needed.
  • User-visible CLI/MCP changes include representative output.
  • I checked the final diff for secrets, unrelated cleanup, and unsupported claims.

🤖 Generated with AI assistance (disclosure voluntary per repo policy)


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/morluto/rea/pull/1890 **Author:** [@akushonkamen](https://github.com/akushonkamen) **Created:** 10/11/2026 **Status:** ✅ Merged **Merged:** 10/11/2026 **Merged by:** [@morluto](https://github.com/morluto) **Base:** `main` ← **Head:** `fix/1850-android-search-inventory` --- ### 📝 Commits (1) - [`5a6c0e8`](https://github.com/morluto/rea/commit/5a6c0e8a4844e96cd0930d204a38c7b3aa999eb8) fix(android): bound inline search Evidence with retained inventory provenance ### 📊 Changes **5 files changed** (+295 additions, -4 deletions) <details> <summary>View changed files</summary> ➕ `src/application/android/AndroidSearchEvidenceSummary.ts` (+120 -0) 📝 `src/server/registerAndroidTools.ts` (+23 -2) ➕ `tests/boundary/process/androidSearchEvidenceBudget.test.ts` (+131 -0) 📝 `tests/fixtures/android/jadx-mcp.mjs` (+11 -1) 📝 `tests/fixtures/android/jadx.ts` (+10 -1) </details> ### 📄 Description ## Summary Focused and zero-match `search_android_classes` responses embedded every examined class name: a single-match search on a 7,096-class APK returned a 382,435-byte tool response because the complete raw provider inventory rode inside the inline Evidence. When the server session retains Evidence, the complete raw-call record is now written to the retained store and the tool response delivers a bounded summary Evidence that keeps the pagination audit, the unchanged normalized matches and truthful coverage metadata, and links the retained complete inventory by Evidence ID (Fixes #1850). ## Problem and expected behavior Smallest trigger: load a 7,096-class APK through the jadx bridge and call `search_android_classes` with a query matching exactly one class. Prior behavior: the response's inline `raw_result` contained every `list_classes` page with all 7,096 class names, because the raw provider call record was captured whole and serialized into every tool response. The violated invariant: a bounded, focused search response should not grow with the size of the unfiltered inventory, and a single match must not cost the full class list on the wire. Expected behavior: the inline Evidence carries the pagination audit (`total`, `offset`, `limit`, per-page `items_omitted`), an `inventory_elided` marker, the unchanged normalized matches and `coverage: complete` / `total_classes`, and `evidence_links` + `complete_evidence_id` pointing at the retained complete record; sessions without retention keep the complete inline delivery, so CLI JSON output is unchanged and no provenance is discarded. ## Change and scope Root cause chain: `src/android/JadxSession.ts` records each `list_classes` page's complete MCP envelope into `#observedCalls` (src/android/JadxSession.ts:220-221); those calls ride into the execution as `rawResult` through `createAnalysisExecution` (src/android/JadxSession.ts:145-152); `src/application/android/AndroidAnalysisService.ts` embeds them into the Evidence (src/application/android/AndroidAnalysisService.ts:44-55); and `src/server/contractToolHandler.ts` serializes the complete Evidence into every tool response (src/server/contractToolHandler.ts:66-71). The normalized result (`src/domain/android/JadxAnalysis.ts:175`) is itself bounded; only the raw-call record is not. Fix: new `src/application/android/AndroidSearchEvidenceSummary.ts`, applied on the `search_android_classes` registration path in `src/server/registerAndroidTools.ts`. When the session retains Evidence, the complete Evidence (with the full raw-call inventory) is recorded first via `recordEvidence`, then a summary Evidence is delivered with `items_omitted` counts replacing the full per-page name lists. Sessions without retention skip summarization entirely, preserving the previous complete inline behavior for CLI consumers. Intentionally not included: no change to the normalized search contract, no change to non-search Android tools, and no budgeting for retention-free sessions. ## Contract and boundary impact - Semantic owner and earliest changed stage: the MCP tool-response boundary in `src/server/registerAndroidTools.ts`, after the normalized result and raw-call record exist and before serialization. - CLI and MCP/tool-catalog contract: the advertised `search_android_classes` schema is unchanged and the normalized result is byte-identical; the inline `raw_result` for retaining sessions now elides inventory items behind `items_omitted` counts plus `complete_evidence_id`, which is an output-size bound, not a shape change to the advertised contract. - Provider, bridge, target-format, or platform compatibility: none; the jadx bridge protocol is untouched (the fixture updates only extend the test protocol fixture to paginate at the same limits as the real bridge). - Evidence, artifact, provenance, or reconstruction contract: no provenance is discarded — the complete raw-call record is retained and addressable by Evidence ID through `evidence_links`; observed vs. elided content remains distinguishable via `inventory_elided` and `items_omitted`. - Process execution, authorization, cleanup, or containment impact: none; no new process, engine, or target acquisition. - Generated metadata (`docs/public/product-catalog.json`), package, or installation impact: none; no catalog, package, or installation contract changes and no generated outputs committed. ## Evidence and regression coverage - Tests added or updated: `tests/boundary/process/androidSearchEvidenceBudget.test.ts` drives a real 7,096-class jadx protocol fixture through the MCP server and asserts (1) with a retaining session: the single-match and zero-match inline `raw_result` contain no class names, stay under 32 KiB, carry `items_omitted`, and link a retained complete record whose raw calls hold all 7,096 names; (2) without a retaining session: the complete inventory stays inline. - Base reproduction or other evidence: with the `src/` change removed (test and fixtures in place) on the rebased base, the retaining-session case fails exactly on the inline-raw assertion — the single-match response contains the full 7,096-name inventory, i.e. the issue symptom. - User-visible CLI/MCP output: MCP responses for retaining sessions shrink from ~382 KB to a bounded summary for focused/zero-match searches; complete CLI JSON output is unchanged. - Remaining proof gaps: no real-device or real-APK lane was run; the fixture reproduces the real bridge's pagination envelope at the same 4,096 page limit. For evidence-bearing changes: - [x] Observed, derived, and inferred claims remain distinguishable. - [x] Artifact identity, source provenance, and failed attempts remain preserved. - [x] Unsupported, incomplete, unavailable, or uncertain outcomes remain visible. ## Validation performed Rebased onto `origin/main` (`fa9a7e5e`, which touches `src/domain/evidence.ts`), artifacts refreshed with `npm run test:prepare`, then: - `npx vitest run --project process-boundary tests/boundary/process/androidSearchEvidenceBudget.test.ts` with the `src/` fix removed — 1 failed / 1 passed; the failure is the bounded-Evidence assertion with the full 7,096-name inventory present in a single-match response (base reproduction). - Same command with the fix restored — Test Files 1 passed, Tests 2 passed. - `npx vitest run tests/boundary/android/ tests/boundary/process/ src/server/ src/domain/android/ src/contracts/android/` — 36 files passed, 237 passed + 2 skipped, 0 failed. - `npx vitest run src/android/ src/server/` — 2 files passed, 12 passed. (Note: `npx vitest run src/android/` alone exits 1 with "No test files found"; `src/android/` has no test files in this repository.) - `npx vitest run tests/acceptance/tools/localToolContracts.test.ts tests/boundary/mcp/` — 97 files (95 passed + 2 skipped), 370 passed + 3 skipped, 0 failed. - `npx tsc --noEmit` — exit 0. - `npx oxlint` — 0 errors (the `max-params(5)` warning on `registerAndroidTools` is pre-existing at `origin/main`); `npx oxfmt --check src/ tests/` — clean. - Real Hopper/Ghidra, browser, or OS lanes: not exercised; the change is covered by the jadx protocol fixture suites above. ## Compatibility, safety, and release - Breaking changes or migration steps: none. Retaining-session MCP consumers that parsed the full inventory out of the inline raw record should read the retained complete record via `evidence_links`/`complete_evidence_id`; CLI output (no retention) is byte-identical in shape. - Real Hopper/Ghidra, browser, or OS coverage: not applicable; Android jadx bridge fixture only. - Package or release metadata impact: none. - Security, privacy, process, or containment review: bounds a response-size amplification (one focused query cost the entire class inventory on the wire); no new network, process, or authority surface. ## Review checklist - [x] This PR addresses a concrete problem or an agreed enhancement. - [x] The PR has one focused outcome and the title follows `type(scope): outcome`. - [x] Related issue is linked, or the reason for not linking one is stated above. - [x] Tests cover changed observable behavior and meaningful failure paths. - [x] Owning docs, contracts, and generated metadata are updated where needed. - [x] User-visible CLI/MCP changes include representative output. - [x] I checked the final diff for secrets, unrelated cleanup, and unsupported claims. 🤖 Generated with AI assistance (disclosure voluntary per repo policy) --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
bsakaguchi 2026-10-31 00:56:23 +00:00
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.

Dependencies

No dependencies set.

Reference
morluto/rea#4881
No description provided.