[PR #3025] [CLOSED] fix(ghidra): declare observed function facets and call coverage #5564

Closed
opened 2026-10-31 01:07:36 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/morluto/rea/pull/3025
Author: @RishTheFish21
Created: 10/13/2026
Status: ❌ Closed

Base: main ← Head: fix/ghidra-function-facet-coverage


📝 Commits (2)

  • 8e3a097 fix(ghidra): declare observed function facets and call coverage
  • 04f3bb1 Merge upstream main

📊 Changes

5 files changed (+112 additions, -6 deletions)

View changed files

📝 bridge/ghidra/ReaGhidraBridge.java (+40 -0)
📝 docs/mcp-contracts.md (+5 -1)
📝 src/ghidra/GhidraAnalysisProfile.ts (+4 -2)
📝 src/ghidra/GhidraProvider.behavior.test.ts (+55 -1)
📝 src/ghidra/GhidraProviderCapabilities.ts (+8 -2)

📄 Description

Summary

Fixes #2980. Since #2450 (b17487f4), compare_functions treats an empty facet without a coverage declaration as unknown, and the calls dimension also needs Evidence relationship coverage. The Ghidra bridge declared neither, so comparing a Ghidra analyze_function result with itself returned unknown instead of unchanged. That stopped every real-Ghidra lane at the native-value check (scripts/lib/native-value-e2e.mjs:238), before it reached the lane's own fixtures.

Problem and expected behavior

Before, on main: a Ghidra dossier compared with itself → status: "unknown", with comments ("Provider did not declare coverage for this empty facet") and calls ("Provider did not report relationship coverage") unresolved.

After: the same comparison is unchanged, because Ghidra declares the facets it actually observed and reports call coverage for analyze_function.

Change and scope

  • ReaGhidraBridge.java: function dossiers include facet_availability. Every facet is read from the complete function body (all instructions and comment types, reference-manager edges, call references, basic blocks), so each is declared available. pseudocode is declared unavailable when there's no decompiled body (external or bodyless procedures).
  • GhidraProviderCapabilities.ts: relationshipCoverageFor("analyze_function") returns the same complete coverage and diagnostic as procedure_callers/procedure_callees. The dossier's callers and callees come from the same getCallingFunctions/getCalledFunctions calls.
  • GhidraAnalysisProfile.ts: call_relationship_coverage bumped to -v2 and function_facet_availability: "declared-observed-facets-v1" added, so cached representations from before this change can't replay under the new contract (same approach as 5dfb9d05).
  • docs/mcp-contracts.md: documents Ghidra's declarations next to the existing IDA note.

Not changed: the comparison rules from #2450.

Contract and boundary impact

  • Semantic owner and earliest changed stage: Ghidra bridge function dossier and Ghidra provider Evidence.
  • CLI and MCP/tool-catalog contract: no schema change. Ghidra now fills the existing optional facet_availability field, and analyze_function Evidence carries relationship_coverage.
  • Provider, bridge, target-format, or platform compatibility: Ghidra only, all platforms.
  • Evidence, artifact, provenance, or reconstruction contract: Ghidra analyze_function call relationships are now reported complete with the existing resolved-call-reference diagnostic, matching procedure_callers/procedure_callees. The profile version changes so older cached Evidence doesn't replay.
  • Process execution, authorization, cleanup, or containment impact: none.
  • Generated metadata (docs/public/product-catalog.json), package, or installation impact: none.

Evidence and regression coverage

  • Tests added or updated: GhidraProvider.behavior.test.ts. A new test checks that analyze_function Evidence carries the complete call coverage with its diagnostic and keeps the declared facets; the profile assertion is updated for the new versions.
  • Base reproduction or other evidence: on main, npm run verify:ghidra:aarch64-jump-table fails at native-value-e2e.mjs:238 with the two unknown dimensions above (details in #ISSUE).
  • User-visible CLI/MCP output: compare_functions on two identical Ghidra dossiers returns unchanged.
  • Remaining proof gaps: real Ghidra on macOS arm64 only; Hopper not checked.

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

  • npm run test:focused -- src/ghidra/GhidraProvider.behavior.test.ts: passed
  • npm run check:fast: passed
  • npm run knip: passed
  • npm run docs:check: passed
  • npm run verify:ghidra:aarch64-jump-table on an Apple Silicon Mac (Ghidra 12.1.4, JDK 21), with no local skips: passed. On main it fails at the native-value self-comparison.

Compatibility, safety, and release

  • Breaking changes or migration steps: none. Cached Ghidra Evidence from the old profile is not replayed and is recomputed.
  • Real Hopper/Ghidra, browser, or OS coverage: real Ghidra on macOS arm64.
  • Package or release metadata impact: none.
  • Security, privacy, process, or containment review: none; declares facts the bridge already collected.

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.

🔄 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/3025 **Author:** [@RishTheFish21](https://github.com/RishTheFish21) **Created:** 10/13/2026 **Status:** ❌ Closed **Base:** `main` ← **Head:** `fix/ghidra-function-facet-coverage` --- ### 📝 Commits (2) - [`8e3a097`](https://github.com/morluto/rea/commit/8e3a0975e8ba2bd9add2b6dcac20745bf16e5b2c) fix(ghidra): declare observed function facets and call coverage - [`04f3bb1`](https://github.com/morluto/rea/commit/04f3bb14c6c98170a1785fbc6d66cdffb0cdd14f) Merge upstream main ### 📊 Changes **5 files changed** (+112 additions, -6 deletions) <details> <summary>View changed files</summary> 📝 `bridge/ghidra/ReaGhidraBridge.java` (+40 -0) 📝 `docs/mcp-contracts.md` (+5 -1) 📝 `src/ghidra/GhidraAnalysisProfile.ts` (+4 -2) 📝 `src/ghidra/GhidraProvider.behavior.test.ts` (+55 -1) 📝 `src/ghidra/GhidraProviderCapabilities.ts` (+8 -2) </details> ### 📄 Description ## Summary Fixes #2980. Since #2450 (`b17487f4`), `compare_functions` treats an empty facet without a coverage declaration as `unknown`, and the calls dimension also needs Evidence relationship coverage. The Ghidra bridge declared neither, so comparing a Ghidra `analyze_function` result with itself returned `unknown` instead of `unchanged`. That stopped every real-Ghidra lane at the native-value check (`scripts/lib/native-value-e2e.mjs:238`), before it reached the lane's own fixtures. ## Problem and expected behavior Before, on `main`: a Ghidra dossier compared with itself → `status: "unknown"`, with `comments` ("Provider did not declare coverage for this empty facet") and `calls` ("Provider did not report relationship coverage") unresolved. After: the same comparison is `unchanged`, because Ghidra declares the facets it actually observed and reports call coverage for `analyze_function`. ## Change and scope - `ReaGhidraBridge.java`: function dossiers include `facet_availability`. Every facet is read from the complete function body (all instructions and comment types, reference-manager edges, call references, basic blocks), so each is declared available. `pseudocode` is declared unavailable when there's no decompiled body (external or bodyless procedures). - `GhidraProviderCapabilities.ts`: `relationshipCoverageFor("analyze_function")` returns the same complete coverage and diagnostic as `procedure_callers`/`procedure_callees`. The dossier's callers and callees come from the same `getCallingFunctions`/`getCalledFunctions` calls. - `GhidraAnalysisProfile.ts`: `call_relationship_coverage` bumped to `-v2` and `function_facet_availability: "declared-observed-facets-v1"` added, so cached representations from before this change can't replay under the new contract (same approach as `5dfb9d05`). - `docs/mcp-contracts.md`: documents Ghidra's declarations next to the existing IDA note. Not changed: the comparison rules from #2450. ## Contract and boundary impact - Semantic owner and earliest changed stage: Ghidra bridge function dossier and Ghidra provider Evidence. - CLI and MCP/tool-catalog contract: no schema change. Ghidra now fills the existing optional `facet_availability` field, and `analyze_function` Evidence carries `relationship_coverage`. - Provider, bridge, target-format, or platform compatibility: Ghidra only, all platforms. - Evidence, artifact, provenance, or reconstruction contract: Ghidra `analyze_function` call relationships are now reported complete with the existing resolved-call-reference diagnostic, matching `procedure_callers`/`procedure_callees`. The profile version changes so older cached Evidence doesn't replay. - Process execution, authorization, cleanup, or containment impact: none. - Generated metadata (`docs/public/product-catalog.json`), package, or installation impact: none. ## Evidence and regression coverage - Tests added or updated: `GhidraProvider.behavior.test.ts`. A new test checks that `analyze_function` Evidence carries the complete call coverage with its diagnostic and keeps the declared facets; the profile assertion is updated for the new versions. - Base reproduction or other evidence: on `main`, `npm run verify:ghidra:aarch64-jump-table` fails at `native-value-e2e.mjs:238` with the two unknown dimensions above (details in #ISSUE). - User-visible CLI/MCP output: `compare_functions` on two identical Ghidra dossiers returns `unchanged`. - Remaining proof gaps: real Ghidra on macOS arm64 only; Hopper not checked. 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 - `npm run test:focused -- src/ghidra/GhidraProvider.behavior.test.ts`: passed - `npm run check:fast`: passed - `npm run knip`: passed - `npm run docs:check`: passed - `npm run verify:ghidra:aarch64-jump-table` on an Apple Silicon Mac (Ghidra 12.1.4, JDK 21), with no local skips: passed. On `main` it fails at the native-value self-comparison. ## Compatibility, safety, and release - Breaking changes or migration steps: none. Cached Ghidra Evidence from the old profile is not replayed and is recomputed. - Real Hopper/Ghidra, browser, or OS coverage: real Ghidra on macOS arm64. - Package or release metadata impact: none. - Security, privacy, process, or containment review: none; declares facts the bridge already collected. ## 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. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
bsakaguchi 2026-10-31 01:07:36 +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#5564
No description provided.