[GH-ISSUE #8169] [Bug] Ghidra project digest mismatch is hidden behind generic provider startup failure #4694

Closed
opened 2026-10-31 00:52:32 +00:00 by bsakaguchi · 7 comments
Owner

Originally created by @morluto on GitHub (Oct 30, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/8169

Before submitting

  • I searched the open and closed issues for a related report.
  • I removed credentials, tokens, private paths, and sensitive target data.

Area

MCP or agent integration

Observed behavior

Opening a selected binary with REA_GHIDRA_PROJECT_PATH pointing to a project imported from a different binary returns a normal open_binary target binding; the first Ghidra-backed procedure_pseudo_code then fails. The error is execution_failure, with the generic instruction to run rea doctor. The actual cause, Ghidra imported-byte digest does not match the admitted target, appears only inside captured Ghidra process output. close_binary subsequently succeeds.

Reproduction

On macOS arm64, use Ghidra 12.1.4 to import and save a self-built Mach-O in a project. Configure REA_GHIDRA_PROJECT_PATH and REA_GHIDRA_PROGRAM_NAME for that project; start an REA stdio MCP server. open_binary a different self-built Mach-O using provider_id: ghidra, then request procedure_pseudo_code by address. The mismatch was rejected once in this test. Target A SHA-256 was c9ef0167227d5901b84a70ede5fb212caaa68574252c6da29b295faff0971f73; project B was imported from SHA-256 ffad1b70c6a5a7ef3f7c3f59126be516520210777d949b35e97c9067f272aa2d.

Expected behavior

Preserve the integrity refusal and expose the verified target/project import digest mismatch as the top-level actionable reason, with guidance to select the matching target or project. rea doctor checks installation readiness and cannot choose the intended matching project. Do not infer a mismatch from generic process exit or human-readable log text without a structured producer signal.

Impact and scope

The mismatch was safely refused; the issue is attribution and recovery UX. Reproduced on one macOS fixture and one stdio session; other platforms and releases are unverified.

REA version

Source checkout 47fa6ab3795d547207001ffb01643c0aa39dcad8; server version 6.8.0.

Runtime and platform

Node 24.22.1, macOS arm64, Ghidra 12.1.4, JDK 21.

Target and artifact context

Two self-built arm64 Mach-O binaries and a local Ghidra project.

Interface

MCP

Earliest failure stage

Native analysis or reconstruction

Relevant output or evidence

Observed top-level error: execution_failure, message Analysis could not complete. Run rea doctor ..., details.diagnostics.failure_message: Ghidra exited before bridge startup. Captured process output contains IllegalStateException: Ghidra imported-byte digest does not match the admitted target from ReaGhidraBridge.java. Related: #1048 and #2147 (existing-project support and identity binding).

A follow-up on one MCP connection repeated the mismatch, called close_binary successfully, then opened the matching binary and project. procedure_pseudo_code returned the expected adjust function; the final close succeeded. This verifies that recovery sequence on this fixture, not failure attribution on other hosts.

Controlled bridge failure and doctor follow-up

On REA 46c13b998, Ghidra 12.1.4 and macOS arm64, a separate isolated checkout deliberately replaced its bridge script with one that throws a known exception. This was an injected fault, not a naturally observed broken installation. Real stdio MCP open_binary bound an owned ELF, then list_procedures returned execution_failure; its captured diagnostics contained the injected exception and Ghidra REPORT SCRIPT ERROR, even though the Ghidra process exit code was zero. The normalized error response serialized to 19,292 UTF-8 bytes; this is a size observation, not a token or latency measurement. REA removed the owned runtime and the target digest remained unchanged.

Following the error advice, rea doctor --provider ghidra --target <same ELF> --format json returned scoped healthy: true and did not detect the injected bridge failure. Doctor checks installation and target readiness, not execution of this bridge; its result is not contradictory. This control is a different failure from the project digest mismatch above. It illustrates why startup recovery needs a truthful separation between setup checks and observed execution failure, while preserving captured diagnostic evidence. Do not classify arbitrary Ghidra log lines as trusted structured reasons.

Existing fix: mismatch and same-connection recovery

On unmodified #8379 head 0212145b0feb1ee8267af53e19c727d89d0ffba0, real Ghidra 12.1.4 stdio MCP with compact and full profiles selected an owned project imported from one Mach-O while opening a different owned Mach-O. The first pseudocode request returned configuration_invalid, named the imported SHA-256 mismatch at the top level, and advised selecting the matching target or project. After closing that failed binary, I opened the matching target on the same MCP connection and read the project-authored function name successfully. The selected project files and both target hashes (510a23fc3c152175b9977130b3b1711d580e47d22a085cdee6a4e0c9dea2c51a and 0e0ecf82c96e4918ee2ca1287dc25513d8fd13d5ab7fa5b98db00ad342f03930) remained unchanged. This is a mismatch control on one host, not a test of missing import identity or all startup failures.

Existing fix: CLI mismatch control

On unmodified #8379 head 0212145b0feb1ee8267af53e19c727d89d0ffba0, real rea decompile --provider ghidra --format json against the mismatched target and saved project returned exit 1 and configuration_invalid, identifying the imported SHA-256 mismatch and offering target/project selection guidance. A separate invocation with the matching target succeeded and returned the project-authored name. The project and both target hashes (510a23fc3c152175b9977130b3b1711d580e47d22a085cdee6a4e0c9dea2c51a, 0e0ecf82c96e4918ee2ca1287dc25513d8fd13d5ab7fa5b98db00ad342f03930) remained unchanged. This is one macOS/Ghidra mismatch control, not a missing-identity or broad platform test.

Packed-program restoration mismatch control

On macOS arm64 and real Ghidra 12.1.4, I imported an owned .gzf program into a new expanded project, then selected a different source-owned Mach-O. In both compact and full stdio MCP profiles, #8820 head f3c0336c5 bound the target lazily, and the first pseudocode request returned execution_failure with generic doctor guidance; the underlying diagnostics contained the digest mismatch. With the same project and targets, unmodified #8379 head 0212145b0 returned configuration_invalid, explicitly identified the imported SHA-256 mismatch, and advised selecting the matching target or project without fresh-import fallback. After closing each failed selection, reopening the matching target on the same connection succeeded. Successful outputs validated against their advertised schemas. Both target hashes and all regular source-project files stayed unchanged; neither executable was run.

This adds the explicit .gzf-to-expanded-project route to the earlier selected-project controls. It does not establish missing-digest behavior, Windows transport, or cancellation behavior for real Ghidra.

Originally created by @morluto on GitHub (Oct 30, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/8169 ### Before submitting - [x] I searched the open and closed issues for a related report. - [x] I removed credentials, tokens, private paths, and sensitive target data. ### Area MCP or agent integration ### Observed behavior Opening a selected binary with `REA_GHIDRA_PROJECT_PATH` pointing to a project imported from a different binary returns a normal `open_binary` target binding; the first Ghidra-backed `procedure_pseudo_code` then fails. The error is `execution_failure`, with the generic instruction to run `rea doctor`. The actual cause, `Ghidra imported-byte digest does not match the admitted target`, appears only inside captured Ghidra process output. `close_binary` subsequently succeeds. ### Reproduction On macOS arm64, use Ghidra 12.1.4 to import and save a self-built Mach-O in a project. Configure `REA_GHIDRA_PROJECT_PATH` and `REA_GHIDRA_PROGRAM_NAME` for that project; start an REA stdio MCP server. `open_binary` a different self-built Mach-O using `provider_id: ghidra`, then request `procedure_pseudo_code` by address. The mismatch was rejected once in this test. Target A SHA-256 was `c9ef0167227d5901b84a70ede5fb212caaa68574252c6da29b295faff0971f73`; project B was imported from SHA-256 `ffad1b70c6a5a7ef3f7c3f59126be516520210777d949b35e97c9067f272aa2d`. ### Expected behavior Preserve the integrity refusal and expose the verified target/project import digest mismatch as the top-level actionable reason, with guidance to select the matching target or project. `rea doctor` checks installation readiness and cannot choose the intended matching project. Do not infer a mismatch from generic process exit or human-readable log text without a structured producer signal. ### Impact and scope The mismatch was safely refused; the issue is attribution and recovery UX. Reproduced on one macOS fixture and one stdio session; other platforms and releases are unverified. ### REA version Source checkout `47fa6ab3795d547207001ffb01643c0aa39dcad8`; server version 6.8.0. ### Runtime and platform Node 24.22.1, macOS arm64, Ghidra 12.1.4, JDK 21. ### Target and artifact context Two self-built arm64 Mach-O binaries and a local Ghidra project. ### Interface MCP ### Earliest failure stage Native analysis or reconstruction ### Relevant output or evidence Observed top-level error: `execution_failure`, message `Analysis could not complete. Run rea doctor ...`, `details.diagnostics.failure_message: Ghidra exited before bridge startup`. Captured process output contains `IllegalStateException: Ghidra imported-byte digest does not match the admitted target` from `ReaGhidraBridge.java`. Related: #1048 and #2147 (existing-project support and identity binding). A follow-up on one MCP connection repeated the mismatch, called `close_binary` successfully, then opened the matching binary and project. `procedure_pseudo_code` returned the expected `adjust` function; the final close succeeded. This verifies that recovery sequence on this fixture, not failure attribution on other hosts. ### Controlled bridge failure and doctor follow-up On REA `46c13b998`, Ghidra 12.1.4 and macOS arm64, a separate isolated checkout deliberately replaced its bridge script with one that throws a known exception. This was an injected fault, not a naturally observed broken installation. Real stdio MCP `open_binary` bound an owned ELF, then `list_procedures` returned `execution_failure`; its captured diagnostics contained the injected exception and Ghidra `REPORT SCRIPT ERROR`, even though the Ghidra process exit code was zero. The normalized error response serialized to 19,292 UTF-8 bytes; this is a size observation, not a token or latency measurement. REA removed the owned runtime and the target digest remained unchanged. Following the error advice, `rea doctor --provider ghidra --target <same ELF> --format json` returned scoped `healthy: true` and did not detect the injected bridge failure. Doctor checks installation and target readiness, not execution of this bridge; its result is not contradictory. This control is a different failure from the project digest mismatch above. It illustrates why startup recovery needs a truthful separation between setup checks and observed execution failure, while preserving captured diagnostic evidence. Do not classify arbitrary Ghidra log lines as trusted structured reasons. ### Existing fix: mismatch and same-connection recovery On unmodified #8379 head `0212145b0feb1ee8267af53e19c727d89d0ffba0`, real Ghidra 12.1.4 stdio MCP with compact and full profiles selected an owned project imported from one Mach-O while opening a different owned Mach-O. The first pseudocode request returned `configuration_invalid`, named the imported SHA-256 mismatch at the top level, and advised selecting the matching target or project. After closing that failed binary, I opened the matching target on the same MCP connection and read the project-authored function name successfully. The selected project files and both target hashes (`510a23fc3c152175b9977130b3b1711d580e47d22a085cdee6a4e0c9dea2c51a` and `0e0ecf82c96e4918ee2ca1287dc25513d8fd13d5ab7fa5b98db00ad342f03930`) remained unchanged. This is a mismatch control on one host, not a test of missing import identity or all startup failures. ### Existing fix: CLI mismatch control On unmodified #8379 head `0212145b0feb1ee8267af53e19c727d89d0ffba0`, real `rea decompile --provider ghidra --format json` against the mismatched target and saved project returned exit 1 and `configuration_invalid`, identifying the imported SHA-256 mismatch and offering target/project selection guidance. A separate invocation with the matching target succeeded and returned the project-authored name. The project and both target hashes (`510a23fc3c152175b9977130b3b1711d580e47d22a085cdee6a4e0c9dea2c51a`, `0e0ecf82c96e4918ee2ca1287dc25513d8fd13d5ab7fa5b98db00ad342f03930`) remained unchanged. This is one macOS/Ghidra mismatch control, not a missing-identity or broad platform test. ### Packed-program restoration mismatch control On macOS arm64 and real Ghidra 12.1.4, I imported an owned `.gzf` program into a new expanded project, then selected a different source-owned Mach-O. In both compact and full stdio MCP profiles, #8820 head `f3c0336c5` bound the target lazily, and the first pseudocode request returned `execution_failure` with generic doctor guidance; the underlying diagnostics contained the digest mismatch. With the same project and targets, unmodified #8379 head `0212145b0` returned `configuration_invalid`, explicitly identified the imported SHA-256 mismatch, and advised selecting the matching target or project without fresh-import fallback. After closing each failed selection, reopening the matching target on the same connection succeeded. Successful outputs validated against their advertised schemas. Both target hashes and all regular source-project files stayed unchanged; neither executable was run. This adds the explicit `.gzf`-to-expanded-project route to the earlier selected-project controls. It does not establish missing-digest behavior, Windows transport, or cancellation behavior for real Ghidra.
Author
Owner

@morluto commented on GitHub (Oct 31, 2026):

Looping in @gensokyo-dev @kaoru0822-kitauji @N0zoM1z0 for visibility on the Ghidra, Kuna, and REA tool-design investigation.

<!-- gh-comment-id:6538782455 --> @morluto commented on GitHub (Oct 31, 2026): Looping in @gensokyo-dev @kaoru0822-kitauji @N0zoM1z0 for visibility on the Ghidra, Kuna, and REA tool-design investigation.
Author
Owner

@morluto commented on GitHub (Oct 31, 2026):

Additional real-provider control on main e6d061897, macOS arm64 and Ghidra 12.1.4: in a new disposable project imported from the original workflow Mach-O, I explicitly cleared the program's recorded executable SHA-256 through a headless Ghidra script. A separate read-only headless process of the saved project reported getExecutableSHA256() = unknown. This is a deliberately constructed unknown-identity case, not a claim about its frequency in ordinary projects.

With that project selected and the same original binary, real MCP open_binary succeeded but the first query failed with generic execution_failure; captured bridge output said Ghidra imported-byte digest does not match the admitted target. A separate selected project with a known digest from a different binary produced the same public error and captured bridge message. A matching known-digest project and target succeeded. Both projects and both input files retained their content hashes during the REA calls, and all sessions closed.

The bridge currently groups null and unequal values; in this Ghidra version, clearing the value read back as the string unknown, which also takes the unequal branch. The refusal remains appropriate when identity cannot be established, but a structured recovery should distinguish identity unavailable from a verified mismatch rather than promoting the common log text as proof that two known digests differ. Project selection must still remain explicit; no automatic fresh-import fallback is implied.

<!-- gh-comment-id:6540651929 --> @morluto commented on GitHub (Oct 31, 2026): Additional real-provider control on main `e6d061897`, macOS arm64 and Ghidra 12.1.4: in a new disposable project imported from the original `workflow` Mach-O, I explicitly cleared the program's recorded executable SHA-256 through a headless Ghidra script. A separate read-only headless process of the saved project reported `getExecutableSHA256() = unknown`. This is a deliberately constructed unknown-identity case, not a claim about its frequency in ordinary projects. With that project selected and the same original binary, real MCP `open_binary` succeeded but the first query failed with generic `execution_failure`; captured bridge output said `Ghidra imported-byte digest does not match the admitted target`. A separate selected project with a known digest from a different binary produced the same public error and captured bridge message. A matching known-digest project and target succeeded. Both projects and both input files retained their content hashes during the REA calls, and all sessions closed. The bridge [currently groups null and unequal values](https://github.com/morluto/rea/blob/e6d0618979f0df3f1d5db218b0fc7b5e6a9cf3f9/bridge/ghidra/ReaGhidraBridge.java#L225-L233); in this Ghidra version, clearing the value read back as the string `unknown`, which also takes the unequal branch. The refusal remains appropriate when identity cannot be established, but a structured recovery should distinguish **identity unavailable** from a verified mismatch rather than promoting the common log text as proof that two known digests differ. Project selection must still remain explicit; no automatic fresh-import fallback is implied.
Author
Owner

@morluto commented on GitHub (Oct 31, 2026):

Real CLI recovery control on REA b43c329, Ghidra 12.1.4 / JDK 21: REA_GHIDRA_PROJECT_PATH selected a caller-owned project containing workflow; the requested target was a different source-owned arm64 Mach-O. rea decompile <different-target> entry --provider ghidra --format json exited 1 with generic execution_failure and advice to run rea doctor. Captured Ghidra diagnostics contained the imported-byte digest mismatch; no pseudocode was returned.

rea doctor --provider ghidra --target <different-target> --format json returned exit 0 and healthy:true both before and after the failed decompile: its target and explicitly scoped Ghidra checks passed. This is installation/target-format readiness, not proof the selected project contains that target. The report also had environment_healthy:false because other unrequested integrations/registrations were missing. Running the exact generic rea doctor --format json returned exit 1 for those broader environment findings, but did not identify the project/target mismatch; its Ghidra installation inspection was available.

Using the same selected project with its matching workflow target, the same CLI decompile succeeded. The two target hashes and all 10 original project-file hashes remained unchanged; neither target was executed. This establishes that the suggested doctor path did not diagnose this fixture, not that doctor promises or should perform deep project admission. A structured mismatch from the producing bridge remains the more precise recovery path.

<!-- gh-comment-id:6542206799 --> @morluto commented on GitHub (Oct 31, 2026): Real CLI recovery control on REA `b43c329`, Ghidra 12.1.4 / JDK 21: `REA_GHIDRA_PROJECT_PATH` selected a caller-owned project containing `workflow`; the requested target was a different source-owned arm64 Mach-O. `rea decompile <different-target> entry --provider ghidra --format json` exited 1 with generic `execution_failure` and advice to run `rea doctor`. Captured Ghidra diagnostics contained the imported-byte digest mismatch; no pseudocode was returned. `rea doctor --provider ghidra --target <different-target> --format json` returned exit 0 and `healthy:true` both before and after the failed decompile: its target and explicitly scoped Ghidra checks passed. This is installation/target-format readiness, not proof the selected project contains that target. The report also had `environment_healthy:false` because other unrequested integrations/registrations were missing. Running the exact generic `rea doctor --format json` returned exit 1 for those broader environment findings, but did not identify the project/target mismatch; its Ghidra installation inspection was available. Using the same selected project with its matching `workflow` target, the same CLI decompile succeeded. The two target hashes and all 10 original project-file hashes remained unchanged; neither target was executed. This establishes that the suggested doctor path did not diagnose this fixture, not that doctor promises or should perform deep project admission. A structured mismatch from the producing bridge remains the more precise recovery path.
Author
Owner

@morluto commented on GitHub (Oct 31, 2026):

Side-by-side CLI control on REA b43c329 / Ghidra 12.1.4, with scoped rea doctor --provider ghidra --target <target> followed by rea decompile for each case:

Selected project state Scoped doctor Decompile
Program name absent from otherwise valid project healthy, exit 0 configuration_invalid, names the missing program and selection setting
Program's recorded digest belongs to a different target healthy, exit 0 generic execution_failure / doctor advice; captured bridge output says digest mismatch
Program's recorded digest was deliberately cleared in an owned project healthy, exit 0 same generic error; captured bridge output also says digest mismatch
Matching program and target healthy, exit 0 success

The absent-digest project's saved value was independently checked as unknown in the earlier headless control linked above. Refusing an unverified identity is correct; an unavailable identity should remain distinct from a known unequal digest when designing any structured startup signal. The existing typed missing-program recovery shows the UX distinction the agent can already receive in a different failure mode; this does not mean doctor currently promises deep project verification.

Both target hashes and all recorded original project files (10 in the matched project, 9 in the cleared-digest project) were unchanged; the targets were not executed. One host and fixture family only.

<!-- gh-comment-id:6542219770 --> @morluto commented on GitHub (Oct 31, 2026): Side-by-side CLI control on REA `b43c329` / Ghidra 12.1.4, with scoped `rea doctor --provider ghidra --target <target>` followed by `rea decompile` for each case: | Selected project state | Scoped doctor | Decompile | | --- | --- | --- | | Program name absent from otherwise valid project | healthy, exit 0 | `configuration_invalid`, names the missing program and selection setting | | Program's recorded digest belongs to a different target | healthy, exit 0 | generic `execution_failure` / doctor advice; captured bridge output says digest mismatch | | Program's recorded digest was deliberately cleared in an owned project | healthy, exit 0 | same generic error; captured bridge output also says digest mismatch | | Matching program and target | healthy, exit 0 | success | The absent-digest project's saved value was independently checked as `unknown` in the earlier headless control linked above. Refusing an unverified identity is correct; an unavailable identity should remain distinct from a *known* unequal digest when designing any structured startup signal. The existing typed missing-program recovery shows the UX distinction the agent can already receive in a different failure mode; this does not mean `doctor` currently promises deep project verification. Both target hashes and all recorded original project files (10 in the matched project, 9 in the cleared-digest project) were unchanged; the targets were not executed. One host and fixture family only.
Author
Owner

@morluto commented on GitHub (Oct 31, 2026):

Headless identity check and project-copy control

I tested a possible manual diagnosis with Ghidra 12.1.4 itself, on disposable copies of the two owned projects from the earlier matrix. An analyzeHeadless ... -process workflow -noanalysis -readOnly -postScript script printed the selected program path, getExecutableSHA256() and language. The known project printed the same SHA-256 as the matching target and a different SHA-256 from the other target; the deliberately cleared project printed unknown. Both processes exited 0. This checks the recorded import digest, not every possible source of project integrity, and an unknown value cannot establish either equality or mismatch.

A practical caution: Ghidra's bundled headless documentation describes -readOnly in process mode as discarding script/analysis changes to existing program files; it does not promise byte-for-byte immutability of all project storage. In two independent copies of the known project, the headless run removed idata/~journal.bak and changed idata/~index.bak, while the other regular project files matched their pre-run hashes. The copied unknown project had no regular-file content changes in this check. This observation does not establish that the program itself changed or that all projects behave this way. All 10 and 9 recorded files in the original projects still matched their prior hashes, and both target digests were unchanged.

Therefore a recovery recipe should preserve the caller's original project and use a safely acquired private copy before running headless inspection, rather than equating Ghidra's -readOnly flag with a guarantee that the entire project directory remains untouched. Copy consistency and lock/active-writer handling still require their own checks; this experiment does not establish them. The existing REA design already uses a private copy, and the earlier runtime result remains a generic startup error—this manual diagnostic does not change it.

<!-- gh-comment-id:6542349568 --> @morluto commented on GitHub (Oct 31, 2026): ### Headless identity check and project-copy control I tested a possible manual diagnosis with Ghidra 12.1.4 itself, on **disposable copies** of the two owned projects from the earlier matrix. An `analyzeHeadless ... -process workflow -noanalysis -readOnly -postScript` script printed the selected program path, `getExecutableSHA256()` and language. The known project printed the same SHA-256 as the matching target and a different SHA-256 from the other target; the deliberately cleared project printed `unknown`. Both processes exited 0. This checks the recorded import digest, not every possible source of project integrity, and an unknown value cannot establish either equality or mismatch. A practical caution: Ghidra's bundled headless documentation describes `-readOnly` in process mode as discarding script/analysis changes to existing program files; it does not promise byte-for-byte immutability of all project storage. In two independent copies of the known project, the headless run removed `idata/~journal.bak` and changed `idata/~index.bak`, while the other regular project files matched their pre-run hashes. The copied unknown project had no regular-file content changes in this check. This observation does not establish that the program itself changed or that all projects behave this way. All 10 and 9 recorded files in the original projects still matched their prior hashes, and both target digests were unchanged. Therefore a recovery recipe should preserve the caller's original project and use a safely acquired private copy before running headless inspection, rather than equating Ghidra's `-readOnly` flag with a guarantee that the entire project directory remains untouched. Copy consistency and lock/active-writer handling still require their own checks; this experiment does not establish them. The existing REA design already uses a private copy, and the earlier runtime result remains a generic startup error—this manual diagnostic does not change it.
Author
Owner

@morluto commented on GitHub (Oct 31, 2026):

Exploratory structured startup refusal

On a disposable checkout of REA main d54a4661, I tried a non-integrated prototype for this exact failure. Instead of exiting before a bridge exists, the Java bridge classifies the imported digest as a valid unequal SHA-256 or an unavailable/malformed identity, opens its existing authenticated transport in a failure-only state, and returns a distinct structured code to the startup request. The client prototype projects those codes to different configuration_invalid messages and recovery guidance. It does not infer identity from captured log text or admit analysis after failed identity validation.

With real Ghidra 12.1.4 on macOS arm64 and public stdio MCP, the previously constructed project whose digest was cleared returned import_identity_unavailable; the project with a known different digest returned import_identity_mismatch; the matching project/target succeeded. For both refusals, a second list_documents request also failed with the corresponding structured reason. In the mismatch case, opening the matching target on the same MCP connection and requesting pseudocode succeeded. All sessions closed successfully, and the target and selected source-project file hashes were unchanged. Successful calls used the SDK's full advertised schemas.

This is feasibility evidence, not a proposed ready-to-merge implementation. The experiment covered one local Unix-socket host and two project fixtures. It did not verify malformed or truncated bridge responses, late handshakes, unauthorized connections, cancellation or cleanup failure, Windows loopback transport, fresh-import errors, or multiple concurrent waiters. The rough projection also assumes a project setting; that attribution must be correct for fresh-import mode before integration. A production design must retain the primary failure alongside cleanup uncertainty, never expose capabilities for a refused program, and distinguish a missing identity from a verified mismatch without promising that retry or project editing will repair it. The original failure and matching recovery remain documented above.

<!-- gh-comment-id:6549371195 --> @morluto commented on GitHub (Oct 31, 2026): ### Exploratory structured startup refusal On a disposable checkout of REA main `d54a4661`, I tried a **non-integrated prototype** for this exact failure. Instead of exiting before a bridge exists, the Java bridge classifies the imported digest as a valid unequal SHA-256 or an unavailable/malformed identity, opens its existing authenticated transport in a failure-only state, and returns a distinct structured code to the startup request. The client prototype projects those codes to different `configuration_invalid` messages and recovery guidance. It does not infer identity from captured log text or admit analysis after failed identity validation. With real Ghidra 12.1.4 on macOS arm64 and public stdio MCP, the previously constructed project whose digest was cleared returned `import_identity_unavailable`; the project with a known different digest returned `import_identity_mismatch`; the matching project/target succeeded. For both refusals, a second `list_documents` request also failed with the corresponding structured reason. In the mismatch case, opening the matching target on the **same MCP connection** and requesting pseudocode succeeded. All sessions closed successfully, and the target and selected source-project file hashes were unchanged. Successful calls used the SDK's full advertised schemas. This is feasibility evidence, not a proposed ready-to-merge implementation. The experiment covered one local Unix-socket host and two project fixtures. It did not verify malformed or truncated bridge responses, late handshakes, unauthorized connections, cancellation or cleanup failure, Windows loopback transport, fresh-import errors, or multiple concurrent waiters. The rough projection also assumes a project setting; that attribution must be correct for fresh-import mode before integration. A production design must retain the primary failure alongside cleanup uncertainty, never expose capabilities for a refused program, and distinguish a missing identity from a verified mismatch without promising that retry or project editing will repair it. The original failure and matching recovery remain documented above.
Author
Owner

@morluto commented on GitHub (Oct 31, 2026):

Further prototype controls: classification now happens only when those codes arrive during the authenticated startup ping; a later remote error with the same code stays a remote operation error. On the real selected-project fixtures, both stdio MCP and rea decompile --format json returned distinct unavailable/mismatch diagnostics, a matching target succeeded, and the selected projects and target files stayed unchanged. The mismatch was also recoverable by opening the matching target on the same MCP connection.

The production-client boundary fixture exercised the two startup codes, retained cleanup ownership and a successful cleanup retry, a late remote error, and cancellation after the identity refusal had reached final cleanup. These are controlled fixture outcomes, not real-Ghidra cancellation tests. The wider client file passed 97 tests before the extra final-cleanup cancellation case was added; a focused run of the six relevant cases including that addition passed. The Java bridge sources compiled against Ghidra 12.1.4.

For fresh-import attribution, I temporarily forced the test-only bridge to report a missing digest during an otherwise real import. The CLI returned the unavailable-identity reason without blaming REA_GHIDRA_PROJECT_PATH; the bridge source and target were restored unchanged. This injection does not show that Ghidra naturally loses the digest. The prototype remains unmerged and needs a source-owned real-provider regression, Windows and protocol/failure coverage, and review of its error and lifecycle semantics before it is ready for integration.

<!-- gh-comment-id:6549456344 --> @morluto commented on GitHub (Oct 31, 2026): Further prototype controls: classification now happens only when those codes arrive during the authenticated startup ping; a later remote error with the same code stays a remote operation error. On the real selected-project fixtures, both stdio MCP and `rea decompile --format json` returned distinct unavailable/mismatch diagnostics, a matching target succeeded, and the selected projects and target files stayed unchanged. The mismatch was also recoverable by opening the matching target on the same MCP connection. The production-client boundary fixture exercised the two startup codes, retained cleanup ownership and a successful cleanup retry, a late remote error, and cancellation after the identity refusal had reached final cleanup. These are controlled fixture outcomes, not real-Ghidra cancellation tests. The wider client file passed 97 tests before the extra final-cleanup cancellation case was added; a focused run of the six relevant cases including that addition passed. The Java bridge sources compiled against Ghidra 12.1.4. For fresh-import attribution, I temporarily forced the test-only bridge to report a missing digest during an otherwise real import. The CLI returned the unavailable-identity reason without blaming `REA_GHIDRA_PROJECT_PATH`; the bridge source and target were restored unchanged. This injection does not show that Ghidra naturally loses the digest. The prototype remains unmerged and needs a source-owned real-provider regression, Windows and protocol/failure coverage, and review of its error and lifecycle semantics before it is ready for integration.
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#4694
No description provided.