[GH-ISSUE #8349] [Bug] Decompile resume mismatch directs agents to the input schema #6555

Closed
opened 2026-11-01 21:12:11 +00:00 by bsakaguchi · 3 comments
Owner

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

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

A decompile_all resume correctly rejected a journal made under a different Ghidra analysis profile, but the error only lists four possible mismatch categories and directs the caller to inspect the input schema. In the reproduced case the request itself was valid: the selected saved project, rather than the target, provider or layout, changed the profile.

Reproduction

On macOS arm64 / Ghidra 12.1.4, using an owned arm64 Mach-O and a caller-selected Ghidra project containing the same imported file:

  1. In a fresh-import MCP session, open the target and call decompile_all({output_directory: <new-dir>, name: "entry"}). It writes one function and records the target, provider, profile digest and address layout in the journal and index.
  2. In a new MCP server configured with REA_GHIDRA_PROJECT_PATH and REA_GHIDRA_PROGRAM_NAME for the existing project, open the same target and call decompile_all on the same directory and name with resume: true.
  3. It returns invalid_request: “The decompile journal does not match this target, provider, analysis profile, or layout.” Its remediation says to read the input schema and correct the fields, with inspect_input_contract recovery. binary_session independently exposes the selected project's different profile digest and import mode.
  4. A third fresh-import MCP session resumes the original directory successfully, skips its verified file, and writes none. The rejected attempt left every output-file digest unchanged; both target and selected source-project file hashes were unchanged throughout.

The same selected-project retry through public rea decompile-all <target> --provider ghidra --output <dir> --name entry --resume --format json exited 1 with the same error and schema-inspection remediation, and left the directory files unchanged. This is one controlled fixture and one attempt per interface, not a frequency estimate. The target was not executed.

Expected behavior

Keep refusing an incompatible resume. When a valid, parsed journal and current identity establish the differing field(s), report those facts and an applicable recovery, such as returning to the matching profile or choosing a new output directory. If the journal is invalid or a component is unknown, report that uncertainty rather than inventing a mismatch. Preserve existing output and do not silently rewrite its journal. Reading a schema cannot establish which already-valid identity differs.

Impact and scope

The current error prevents unsafe mixing and preserves the files; the issue is actionable diagnosis. An agent must independently inspect the journal and session or guess among target, provider, profile and layout. I did not measure an actual agent retry or establish behavior for corrupted journals, other mismatch categories, providers or versions.

REA version

60149282fcfc05ac32fbc94434c107710120da6f

Runtime and platform

Node 24.22.1; macOS arm64; Ghidra 12.1.4 / JDK 21; MCP SDK 2.3.1.

Target and artifact context

Owned unexecuted arm64 Mach-O; saved project had a prior controlled database byte edit, with the original executable unchanged. Fresh and selected sessions reported the same target SHA-256, a6bd938bf4ae8031a33d270ca7698ee1ced5b78347a9c4fa3750b0e57f6b126c, and different profile digests.

Interface

MCP and CLI

Earliest failure stage

Transport or result projection

Relevant output or evidence

Fresh profile digest was 589427371e23b508f480b76cb045b44688b3890c0348f2d6b2505369ddb48ddb; selected-project profile digest was f3b7b02dc225ed9f05c91226e26bc8c2cb371b86b4563b84fcd2f17c9233cde6. Both were visible via the respective MCP binary_session bindings. The journal and index retain the fresh digest. The error reported neither digest nor the differing component, and its details.issues[0].path was output_directory.

Typed outcome or recovery expected

An identity-mismatch outcome with supported differing components and concrete next steps, distinct from malformed arguments or damaged journals. Avoid requiring any provider switch or changing caller-selected project state automatically.

Analysis or suspected cause

Source inspection at this revision shows src/application/decompileDirectory/exportTree.ts collapsing the journal identity comparison into one decompileInputError; the generic invalid-input projection adds schema-inspection recovery. This explains the observed result for this path; other error owners remain untested.

Additional context

Related #8260 concerns per-function producer provenance when profiles match; #8232 explains why the same executable digest does not guarantee unchanged saved-project memory. Test schema validation and identity cases, intact/tampered journals, resume preservation, and both adapters if changing this boundary. Last known good version: NOT_ENOUGH_INFO.

Looping in @gensokyo-dev @kaoru0822-kitauji @N0zoM1z0.

Originally created by @morluto on GitHub (Oct 31, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/8349 ### 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 A `decompile_all` resume correctly rejected a journal made under a different Ghidra analysis profile, but the error only lists four possible mismatch categories and directs the caller to inspect the input schema. In the reproduced case the request itself was valid: the selected saved project, rather than the target, provider or layout, changed the profile. ### Reproduction On macOS arm64 / Ghidra 12.1.4, using an owned arm64 Mach-O and a caller-selected Ghidra project containing the same imported file: 1. In a fresh-import MCP session, open the target and call `decompile_all({output_directory: <new-dir>, name: "entry"})`. It writes one function and records the target, provider, profile digest and address layout in the journal and index. 2. In a new MCP server configured with `REA_GHIDRA_PROJECT_PATH` and `REA_GHIDRA_PROGRAM_NAME` for the existing project, open the **same** target and call `decompile_all` on the same directory and name with `resume: true`. 3. It returns `invalid_request`: “The decompile journal does not match this target, provider, analysis profile, or layout.” Its remediation says to read the input schema and correct the fields, with `inspect_input_contract` recovery. `binary_session` independently exposes the selected project's different profile digest and import mode. 4. A third fresh-import MCP session resumes the original directory successfully, skips its verified file, and writes none. The rejected attempt left every output-file digest unchanged; both target and selected source-project file hashes were unchanged throughout. The same selected-project retry through public `rea decompile-all <target> --provider ghidra --output <dir> --name entry --resume --format json` exited 1 with the same error and schema-inspection remediation, and left the directory files unchanged. This is one controlled fixture and one attempt per interface, not a frequency estimate. The target was not executed. ### Expected behavior Keep refusing an incompatible resume. When a valid, parsed journal and current identity establish the differing field(s), report those facts and an applicable recovery, such as returning to the matching profile or choosing a new output directory. If the journal is invalid or a component is unknown, report that uncertainty rather than inventing a mismatch. Preserve existing output and do not silently rewrite its journal. Reading a schema cannot establish which already-valid identity differs. ### Impact and scope The current error prevents unsafe mixing and preserves the files; the issue is actionable diagnosis. An agent must independently inspect the journal and session or guess among target, provider, profile and layout. I did not measure an actual agent retry or establish behavior for corrupted journals, other mismatch categories, providers or versions. ### REA version `60149282fcfc05ac32fbc94434c107710120da6f` ### Runtime and platform Node 24.22.1; macOS arm64; Ghidra 12.1.4 / JDK 21; MCP SDK 2.3.1. ### Target and artifact context Owned unexecuted arm64 Mach-O; saved project had a prior controlled database byte edit, with the original executable unchanged. Fresh and selected sessions reported the same target SHA-256, `a6bd938bf4ae8031a33d270ca7698ee1ced5b78347a9c4fa3750b0e57f6b126c`, and different profile digests. ### Interface MCP and CLI ### Earliest failure stage Transport or result projection ### Relevant output or evidence Fresh profile digest was `589427371e23b508f480b76cb045b44688b3890c0348f2d6b2505369ddb48ddb`; selected-project profile digest was `f3b7b02dc225ed9f05c91226e26bc8c2cb371b86b4563b84fcd2f17c9233cde6`. Both were visible via the respective MCP `binary_session` bindings. The journal and index retain the fresh digest. The error reported neither digest nor the differing component, and its `details.issues[0].path` was `output_directory`. ### Typed outcome or recovery expected An identity-mismatch outcome with supported differing components and concrete next steps, distinct from malformed arguments or damaged journals. Avoid requiring any provider switch or changing caller-selected project state automatically. ### Analysis or suspected cause Source inspection at this revision shows `src/application/decompileDirectory/exportTree.ts` collapsing the journal identity comparison into one `decompileInputError`; the generic invalid-input projection adds schema-inspection recovery. This explains the observed result for this path; other error owners remain untested. ### Additional context Related #8260 concerns per-function producer provenance when profiles match; #8232 explains why the same executable digest does not guarantee unchanged saved-project memory. Test schema validation and identity cases, intact/tampered journals, resume preservation, and both adapters if changing this boundary. Last known good version: NOT_ENOUGH_INFO. Looping in @gensokyo-dev @kaoru0822-kitauji @N0zoM1z0.
bsakaguchi 2026-11-01 21:12:11 +00:00
  • closed this issue
  • added the
    bug
    triage
    labels
Author
Owner

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

A separate public CLI control used the original fresh-import target and provider configuration and attempted --resume --layout dwarf against the address-layout directory. It also exited 1 and returned byte-for-byte the same JSON error as the selected-project profile mismatch, with a known layout mismatch instead of the selected-project configuration. All output-file hashes remained unchanged. In this case the caller can retry with the matching --layout address; in the profile case restoring the original profile or selecting a fresh directory is the relevant choice. This demonstrates why naming only one possible universal remedy would be insufficient. It is a single layout control, not coverage of malformed journals or every identity combination.

<!-- gh-comment-id:6546821621 --> @morluto commented on GitHub (Oct 31, 2026): A separate public CLI control used the original fresh-import target and provider configuration and attempted `--resume --layout dwarf` against the address-layout directory. It also exited 1 and returned **byte-for-byte the same JSON error** as the selected-project profile mismatch, with a known layout mismatch instead of the selected-project configuration. All output-file hashes remained unchanged. In this case the caller can retry with the matching `--layout address`; in the profile case restoring the original profile or selecting a fresh directory is the relevant choice. This demonstrates why naming only one possible universal remedy would be insufficient. It is a single layout control, not coverage of malformed journals or every identity combination.
Author
Owner

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

Test-coverage control on the same source revision: npm run test:local -- src/application/decompileDirectory/decompileAll.test.ts passed 59 tests. Its mismatch test creates a fixture export, changes the journal target digest, and checks rejection plus a message containing does not match; it separately checks malformed-journal refusal and preservation of a function file. That validates useful safety properties, but it does not check which identity component differs or the projected adapter recovery.

npm run test:focused -- tests/boundary/cli/decompileAllCli.test.ts tests/boundary/mcp/decompileAllCaveats.test.ts also passed (1 CLI and 5 MCP tests). The CLI test uses fake Kuna for successful filter/limit/resume; the MCP caveat tests use fixture providers and exercise retained caveats and related failures. Neither of those focused files exercises a valid profile/layout mismatch through the public error. Source search of the checked scripts/verify-real-ghidra.mjs, scripts/verify-real-kuna.mjs, and their scripts/lib helpers found no decompile_all/decompile-all invocation; this is not a claim about external CI or every test in the repository.

A regression for this issue can use the real-provider profile/layout controls above to assert both refusal and the actual reported identity/recovery, with a matching-resume and malformed-journal control. Producer-boundary tests can cover combinations that are hard to reproduce reliably. The existing preservation and failure-path coverage should not be removed merely because a stronger workflow is added.

<!-- gh-comment-id:6546867804 --> @morluto commented on GitHub (Oct 31, 2026): Test-coverage control on the same source revision: `npm run test:local -- src/application/decompileDirectory/decompileAll.test.ts` passed 59 tests. Its mismatch test creates a fixture export, changes the journal target digest, and checks rejection plus a message containing `does not match`; it separately checks malformed-journal refusal and preservation of a function file. That validates useful safety properties, but it does not check which identity component differs or the projected adapter recovery. `npm run test:focused -- tests/boundary/cli/decompileAllCli.test.ts tests/boundary/mcp/decompileAllCaveats.test.ts` also passed (1 CLI and 5 MCP tests). The CLI test uses fake Kuna for successful filter/limit/resume; the MCP caveat tests use fixture providers and exercise retained caveats and related failures. Neither of those focused files exercises a valid profile/layout mismatch through the public error. Source search of the checked `scripts/verify-real-ghidra.mjs`, `scripts/verify-real-kuna.mjs`, and their `scripts/lib` helpers found no `decompile_all`/`decompile-all` invocation; this is not a claim about external CI or every test in the repository. A regression for this issue can use the real-provider profile/layout controls above to assert both refusal and the actual reported identity/recovery, with a matching-resume and malformed-journal control. Producer-boundary tests can cover combinations that are hard to reproduce reliably. The existing preservation and failure-path coverage should not be removed merely because a stronger workflow is added.
Author
Owner

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

Follow-up: exercise a narrower REA-side diagnostic

I tested an isolated, unmerged prototype at 60149282fcfc05ac32fbc94434c107710120da6f. After the journal has parsed and the existing unknown-digest checks pass, it reports the components whose existing equality checks differ in details.diagnostics.mismatched_identity_components. It uses AnalysisInputError's existing conformsToInputContract and remediationAction options to suppress inspect_input_contract and suggest restoring the original identity or choosing a new directory. It does not report the journal's actual values or relax the refusal.

Using real Ghidra 12.1.4 and MCP SDK 2.3.1, separate attempts on an owned export reported analysis_profile, layout, both analysis_profile and layout, and target respectively. A CLI --layout dwarf retry reported layout as well. Every refused attempt left the corresponding export files unchanged; a matching fresh-profile resume still succeeded without rewriting them. Both target hashes and the selected project's file hashes were unchanged. These are individual controlled runs, not a frequency estimate or coverage of every provider combination.

Two additional MCP controls used separate copies of the export: an invalid journal and a journal with an unknown target digest were correctly refused without fabricated mismatch components or output changes. However, those existing refusal paths still direct the agent to inspect the input schema; they need their own applicable recovery and unknown-state semantics. Likewise, the prototype still classifies these requests as invalid_input, so naming the components and suppressing schema advice alone does not settle the public error taxonomy.

The existing source test file had 58 passed, 1 failed: the failure expects the previous message to contain does not match. It does not check the new diagnostics or useful recovery. This experiment is not a proposed production patch: it lacks an agreed contract for machine-readable fields, provider-version mismatch tests, adapter/error-schema compatibility checks, and tests for the remaining recovery paths. SDK tool discovery was performed; success-output schema validation does not validate these error payloads. It supports extending the issue to cover valid-request refusals caused by malformed/unknown journal state, not only a known mismatch.

<!-- gh-comment-id:6547147935 --> @morluto commented on GitHub (Oct 31, 2026): ### Follow-up: exercise a narrower REA-side diagnostic I tested an isolated, unmerged prototype at `60149282fcfc05ac32fbc94434c107710120da6f`. After the journal has parsed and the existing unknown-digest checks pass, it reports the components whose existing equality checks differ in `details.diagnostics.mismatched_identity_components`. It uses `AnalysisInputError`'s existing `conformsToInputContract` and `remediationAction` options to suppress `inspect_input_contract` and suggest restoring the original identity or choosing a new directory. It does not report the journal's actual values or relax the refusal. Using real Ghidra 12.1.4 and MCP SDK 2.3.1, separate attempts on an owned export reported `analysis_profile`, `layout`, both `analysis_profile` and `layout`, and `target` respectively. A CLI `--layout dwarf` retry reported `layout` as well. Every refused attempt left the corresponding export files unchanged; a matching fresh-profile resume still succeeded without rewriting them. Both target hashes and the selected project's file hashes were unchanged. These are individual controlled runs, not a frequency estimate or coverage of every provider combination. Two additional MCP controls used separate copies of the export: an invalid journal and a journal with an unknown target digest were correctly refused without fabricated mismatch components or output changes. However, those **existing** refusal paths still direct the agent to inspect the input schema; they need their own applicable recovery and unknown-state semantics. Likewise, the prototype still classifies these requests as `invalid_input`, so naming the components and suppressing schema advice alone does not settle the public error taxonomy. The existing source test file had **58 passed, 1 failed**: the failure expects the previous message to contain `does not match`. It does not check the new diagnostics or useful recovery. This experiment is not a proposed production patch: it lacks an agreed contract for machine-readable fields, provider-version mismatch tests, adapter/error-schema compatibility checks, and tests for the remaining recovery paths. SDK tool discovery was performed; success-output schema validation does not validate these error payloads. It supports extending the issue to cover valid-request refusals caused by malformed/unknown journal state, not only a known mismatch.
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#6555
No description provided.