[PR #1785] [MERGED] fix(ida): report a procedure IDA cannot resolve as invalid input #4815

Closed
opened 2026-10-31 00:55:19 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/morluto/rea/pull/1785
Author: @nikolay-dementiev
Created: 10/11/2026
Status: ✅ Merged
Merged: 10/11/2026
Merged by: @morluto

Base: main ← Head: fix/1769-ida-missing-procedure


📝 Commits (10+)

  • 48ea6bc test(ida): expect a missing procedure to be reported as invalid input
  • e067a85 test(ida): name the invalid_request code and its category for a missing procedure
  • fdf2993 fix(ida): report a procedure IDA cannot resolve as invalid input
  • dc11d28 test(ida): pin the missing-procedure issue shape and the lookups that stay unreadable
  • 101b111 fix(ida): treat a lookup answer for another query as a producer fault
  • a99d665 test(ida): expect an argument-independent issue path and pin legacy error replies and escaping
  • 6b71db8 fix(ida): report a missing procedure without naming an argument path
  • 46a3c38 test(ida): expect provider-derived selectors to stay producer faults in composed workflows
  • 06345bc fix(ida): keep provider-derived procedure selectors out of the caller's input errors
  • 5feff18 test(ida): cover session forwarding, parameter hygiene and structured derived-node errors

📊 Changes

15 files changed (+753 additions, -21 deletions)

View changed files

📝 src/application/AnalysisProvider.ts (+7 -0)
📝 src/application/CallPathTracing.ts (+6 -2)
➕ src/application/EnhancedToolSelectorOrigin.test.ts (+142 -0)
📝 src/application/EnhancedTools.ts (+10 -7)
📝 src/application/binary/BinarySession.ts (+2 -2)
📝 src/application/native/EnhancedNativeInvestigation.test.ts (+34 -0)
📝 src/application/native/NativeCallRoutes.ts (+9 -2)
📝 src/application/native/NativeUiActionTrace.ts (+1 -0)
📝 src/application/native/NativeValueTrace.test.ts (+29 -0)
📝 src/application/native/NativeValueTrace.ts (+4 -1)
📝 src/ida/IdaOperationFailures.test.ts (+290 -0)
📝 src/ida/IdaOperationRunner.ts (+39 -1)
📝 src/ida/IdaSessionClient.ts (+11 -6)
➕ tests/boundary/providers/ida/selectorProvenance.test.ts (+131 -0)
📝 tests/composition/analysis-sessions/binarySession.test.ts (+38 -0)

📄 Description

Summary

In the headless IDA profile, asking for a procedure that IDA cannot resolve (an unknown name, or a location outside any function) was reported as unreadable_output with "Retry once; if it continues, run rea doctor". The server answered correctly; the caller supplied a name it does not know. Such answers are now reported as an invalid request that names the procedure.

Fixes #1769

This branch is based on current main, which already contains #1766 (PR #1778); the two changes share no code.

Problem and expected behavior

Upstream lookup_funcs (ida-pro-mcp at c133c3853faa111a9b00ee615c013b720d0c4acd, api_core.py) answers with fn: null and exactly one of three errors: "Not found" (the name does not resolve), "Not a function" (the name or address resolves to a location outside any function, for example the Mach-O header; an address inside a function resolves to that function), or the text of an exception. IdaOperationRunner.resolve() turned any fn: null into AnalysisProtocolError, which is presented as unreadable_output, retryable: false, and advises rea doctor.

On macOS IDA names Mach-O C symbols with a leading underscore, so rea function <binary> main --json fails for the main an analyst naturally types, while _main succeeds. The failure pointed at the installation instead of the argument.

Expected: a name or address that IDA reports as not found or not a function is a correctable caller mistake and says which procedure was asked for. Anything else the producer returns stays a producer fault.

Change and scope

  • src/ida/IdaOperationRunner.ts: resolve(query, requestedBy?). When the caller's own procedure is being resolved (requestedBy is the current operation) and the lookup returns the same query with fn: null and an error that is exactly "Not found" or "Not a function", it throws AnalysisInputError(requestedBy, { cause }, [{ path: [], reason: "invalid_value", message }]). The issue path is empty, as in the Ghidra not-found mapping, because composed workflows and some CLI commands reach this code with other argument names. The message names the queried procedure and keeps the upstream text: IDA could not resolve procedure "main": Not found. The retained cause is an AnalysisProtocolError carrying the lookup text. This follows the Ghidra mapping of not-found answers to AnalysisInputError and the runner's own AnalysisInputError("search").
  • Selector provenance. A procedure that a composed workflow takes from provider output is never the caller's input mistake, so it must not become invalid input. ExecutionOptions gains an optional per-call selectorOrigin?: "provider" (absent means caller-supplied). The workflows set it on every selector derived from provider output: get_call_graph (the canonical start and every discovered address), trace_call_path (expansions of the resolved start and discovered addresses), the canonical retry in batch_decompile, trace_native_ui_action (provider-reported function addresses) and trace_native_values (procedures discovered after the first). The caller's own selectors stay unmarked: the root of a call graph, the start and goal of a call path, batch items and their fallback retry, the action seed and the first value-trace procedure. IdaSessionClient forwards the origin to the runner, which passes requestedBy only for an unmarked selector. The option never enters tool parameters, Evidence or raw observations. With this, composed workflows behave as on main for provider-derived selectors: a derived address IDA answers Not found or Not a function is recorded in that node's error entry as unreadable_output, and only the caller's own selector becomes invalid_request.
  • Unchanged, still unreadable_output: an exception text from upstream, a function returned together with an error, neither a function nor an error, a near-miss or padded or embedded spelling of the two texts, an empty error, an answer for a different query, and the callee resolution inside relationships(), which resolves addresses IDA itself reported and is called without requestedBy (an address IDA reports that it cannot resolve is a producer inconsistency, not a caller mistake).
  • The legacy (attached) profile is untouched. For an unknown name it still surfaces the producer's MCP error reply as unreadable_output; that is an existing limitation of the legacy path and is not changed here. Tests cover its malformed reply and its error reply.

Intentionally not included:

  • A structured not-found state or a new error code; the existing invalid-request projection is used as is.
  • Suggesting the underscore-prefixed name or any other corrective hint; REA reports what IDA said and does not guess.
  • Ghidra and Hopper are not changed: they classify a missing procedure in their own adapters and still report a derived miss in a composed workflow as invalid_request.
  • No docs change: docs/ida-provider.md and docs/testing.md do not describe how a missing procedure is reported.

Contract and boundary impact

  • Semantic owner and earliest changed stage: src/ida headless operation runner, at procedure resolution, before any analysis result exists.
  • CLI and MCP/tool-catalog contract: none. No tool, parameter, schema or code is added; selectorOrigin is an internal per-call option, not a tool parameter; the outcome reuses invalid_request (category invalid_input) with its existing details.operation and details.issues shape.
  • Provider, bridge, target-format, or platform compatibility: IDA headless database-supervisor profile; checked on macOS arm64 with IDA 9.1. Attached profile, Hopper and Ghidra unchanged.
  • Evidence, artifact, provenance, or reconstruction contract: none. Successful lookups produce identical Evidence; only the error classification of a previously misreported case changes.
  • Process execution, authorization, cleanup, or containment impact: none; the cleanup path of IdaSessionClient is unchanged and every new test asserts a clean close().
  • Generated metadata/package/installation impact: none.

Evidence and regression coverage

  • Tests added or updated: src/ida/IdaOperationFailures.test.ts, at the session-client boundary with the recording fake connection (no module mocks): "Not found" and "Not a function" answers for several operations and for a name, a hexadecimal address and an underscore name each give an AnalysisInputError whose projection is invalid_request / invalid_input / retryable: true with exactly one issue (empty path) whose message names the query and the upstream text, and the upstream text kept on the retained cause; a query containing a control character is carried JSON-escaped, never raw; nine further cases stay AnalysisProtocolError / unreadable_output (exception text, function with an error, neither a function nor an error, lowercase spelling, embedded text, padded text, an empty error, a whitespace-only error, an answer for another query); the callee path stays unreadable_output; the legacy profile keeps its existing classification for a malformed reply and for an MCP error reply; every case asserts a clean close(). Selector provenance: tests/boundary/providers/ida/selectorProvenance.test.ts drives EnhancedTools over the real headless IdaSessionClient with the recording fake connection; for a start main whose external callee 0x2000 IDA answers Not a function, get_call_graph and trace_call_path keep the root ok and record only the derived node, by address, as unreadable_output, while an unknown start, graph root or batch item (and the batch fallback retry) stays invalid_request. src/application/EnhancedToolSelectorOrigin.test.ts records the options of every provider call and asserts which calls carry selectorOrigin: "provider" (graph expansions, trace expansions, the canonical batch retry) and which do not (initial resolutions, the unresolved batch fallback), and that the origin is never copied into the parameters; the native value trace and native UI action tests do the same for their derived selectors; tests/composition/analysis-sessions/binarySession.test.ts shows that BinarySession.execute and withAdmittedAnalysis forward the origin to the provider client; src/ida/IdaOperationFailures.test.ts covers the runner with a provider-origin selector.

  • Base reproduction or other evidence: the new mapping tests fail on main (5 failed in the first test commit and 7 with the final assertions; the "stays unreadable" cases pass there by design). The composed-workflow tests fail on the published head 6b71db81e (9 failed, among them the call-graph and call-path regressions that show invalid_request for the derived node). Real CLI on macOS 26.5 arm64, IDA Professional 9.1, an arm64 binary built from tests/conformance/ida/inventory.c, REA_ANALYSIS_PROVIDER=ida, rea function <binary> main --json: before, unreadable_output, retryable: false, "Retry once; if it continues, run rea doctor"; after, invalid_request / invalid_input, retryable: true, issue IDA could not resolve procedure "main": Not found. With _main the command succeeds in both builds. After the change the same command with __mh_execute_header and with 0x100000000 (the Mach-O header, outside any function) reports invalid_request with the upstream text Not a function, while 0x100000350, an address inside _main, resolves to _main.

  • Real MCP on the same host in one session: analyze_function and procedure_pseudo_code for main give invalid_request; procedure_address for __mh_execute_header and procedure_assembly for 0x100000000 give invalid_request with Not a function; procedure_address for _main succeeds.

  • User-visible CLI/MCP output (after, sanitized):

    { "error": "Analysis failed", "code": "invalid_request", "category": "invalid_input",
      "message": "Analysis input is invalid. Check the arguments and try again.", "retryable": true,
      "remediation": { "action": "Correct the listed arguments and retry." },
      "details": { "operation": "analyze_function",
        "issues": [{ "path": [], "reason": "invalid_value",
          "message": "IDA could not resolve procedure \"main\": Not found." }] } }
    
  • Remaining proof gaps: the real checks ran only on macOS arm64 with IDA 9.1. A real provider-derived miss could not be produced on this host, because IDA currently reports no callees on ARM64 (#1794); the derived-node path is covered by the boundary and application tests.

  • 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 check:fast, npm run check, npm run docs:check, npm run verify:test-discovery (822 files) - green
  • npm run test:focused over the thirteen affected files (eight src/ida files, the enhanced-tool origin test, the native value and UI trace tests, the IDA selector-provenance boundary test and the binary-session composition test) - 133 passed
  • npm run test:fast - 378 files, 4348 passed, 7 skipped, 0 failed
  • npm run test:mcp - 93 files passed, 2 skipped, 362 passed, 2 skipped
  • npm run test:boundary for tests/boundary/providers/ida/selectorProvenance.test.ts after npm run build:cached - passed
  • Real checks on the final code, macOS arm64, IDA 9.1: rea function <binary> main --json -> invalid_request (Not found), __mh_execute_header -> invalid_request (Not a function), _main -> Evidence; MCP analyze_function and procedure_pseudo_code for main, procedure_address for __mh_execute_header and procedure_assembly for 0x100000000 -> invalid_request, procedure_address for _main and for 0x100000350 -> 0x10000034c; get_call_graph and trace_call_path with the unknown start main -> invalid_request, with _main -> ok; npm run verify:ida -- --target <inventory> --procedure _rea_fixture_add -> 2 of 2 passed, all five checks; no IDA worker left running
  • Mutation checks, each reverted: ignoring the origin in the runner fails 4 tests; dropping the marker in the call graph, the trace expansion, the batch retry, the native value trace or the native UI trace each fails the corresponding 1 to 2 tests; marking every enhanced call as provider-derived fails 4; dropping the origin in BinarySession fails 1; copying the origin into the provider parameters fails 3

macOS arm64 only; Linux and Windows were not executed. test:acceptance was not run because no acceptance file is touched.

Compatibility, safety, and release

  • Breaking changes or migration steps: none for successful calls. A caller or script that matched the old unreadable_output for a missing procedure now receives invalid_request; that case was misreported and retryable: false before.
  • Real Hopper/Ghidra, browser, or OS coverage: real IDA 9.1 on macOS arm64; Hopper, Ghidra and browser not applicable.
  • Package or release metadata impact: none.
  • Security, privacy, process, or containment review: the issue message repeats only the caller's own query (JSON-quoted) and one of two fixed upstream texts; no producer-supplied free text is echoed, and no path, credential or process behaviour changes.

Review checklist

  • The change addresses a concrete problem, not an unscoped refactor.
  • The PR has one focused outcome, and the title follows type(scope): outcome.
  • Related issue is linked.
  • Tests cover changed behavior and failure paths.
  • Owning docs, contracts, and generated metadata are updated.
  • 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/1785 **Author:** [@nikolay-dementiev](https://github.com/nikolay-dementiev) **Created:** 10/11/2026 **Status:** ✅ Merged **Merged:** 10/11/2026 **Merged by:** [@morluto](https://github.com/morluto) **Base:** `main` ← **Head:** `fix/1769-ida-missing-procedure` --- ### 📝 Commits (10+) - [`48ea6bc`](https://github.com/morluto/rea/commit/48ea6bc216628d455d9befa68a0bfb2f8df973de) test(ida): expect a missing procedure to be reported as invalid input - [`e067a85`](https://github.com/morluto/rea/commit/e067a85f51029e51b08425f4a970f838830c1722) test(ida): name the invalid_request code and its category for a missing procedure - [`fdf2993`](https://github.com/morluto/rea/commit/fdf2993f9694cf9391366ee9bb7e219e3adb8125) fix(ida): report a procedure IDA cannot resolve as invalid input - [`dc11d28`](https://github.com/morluto/rea/commit/dc11d2838190896b4dc0c96f3e1adab99f7f5376) test(ida): pin the missing-procedure issue shape and the lookups that stay unreadable - [`101b111`](https://github.com/morluto/rea/commit/101b111502cad6c8ac3d2c3c99cc18a32468cfde) fix(ida): treat a lookup answer for another query as a producer fault - [`a99d665`](https://github.com/morluto/rea/commit/a99d665a094ae0dde3cedf1a7cc4ae632567b528) test(ida): expect an argument-independent issue path and pin legacy error replies and escaping - [`6b71db8`](https://github.com/morluto/rea/commit/6b71db81ea14909dcc2eafa05e1fca8ad7c080fc) fix(ida): report a missing procedure without naming an argument path - [`46a3c38`](https://github.com/morluto/rea/commit/46a3c382e3041c12d604e6966197f68a85f8bc0f) test(ida): expect provider-derived selectors to stay producer faults in composed workflows - [`06345bc`](https://github.com/morluto/rea/commit/06345bc50b712ab2344291c44d18c37786ee5cf5) fix(ida): keep provider-derived procedure selectors out of the caller's input errors - [`5feff18`](https://github.com/morluto/rea/commit/5feff18da1d333a9ad5a05db77195f667f96386c) test(ida): cover session forwarding, parameter hygiene and structured derived-node errors ### 📊 Changes **15 files changed** (+753 additions, -21 deletions) <details> <summary>View changed files</summary> 📝 `src/application/AnalysisProvider.ts` (+7 -0) 📝 `src/application/CallPathTracing.ts` (+6 -2) ➕ `src/application/EnhancedToolSelectorOrigin.test.ts` (+142 -0) 📝 `src/application/EnhancedTools.ts` (+10 -7) 📝 `src/application/binary/BinarySession.ts` (+2 -2) 📝 `src/application/native/EnhancedNativeInvestigation.test.ts` (+34 -0) 📝 `src/application/native/NativeCallRoutes.ts` (+9 -2) 📝 `src/application/native/NativeUiActionTrace.ts` (+1 -0) 📝 `src/application/native/NativeValueTrace.test.ts` (+29 -0) 📝 `src/application/native/NativeValueTrace.ts` (+4 -1) 📝 `src/ida/IdaOperationFailures.test.ts` (+290 -0) 📝 `src/ida/IdaOperationRunner.ts` (+39 -1) 📝 `src/ida/IdaSessionClient.ts` (+11 -6) ➕ `tests/boundary/providers/ida/selectorProvenance.test.ts` (+131 -0) 📝 `tests/composition/analysis-sessions/binarySession.test.ts` (+38 -0) </details> ### 📄 Description ## Summary In the headless IDA profile, asking for a procedure that IDA cannot resolve (an unknown name, or a location outside any function) was reported as `unreadable_output` with "Retry once; if it continues, run `rea doctor`". The server answered correctly; the caller supplied a name it does not know. Such answers are now reported as an invalid request that names the procedure. Fixes #1769 This branch is based on current `main`, which already contains #1766 (PR #1778); the two changes share no code. ## Problem and expected behavior Upstream `lookup_funcs` (ida-pro-mcp at `c133c3853faa111a9b00ee615c013b720d0c4acd`, `api_core.py`) answers with `fn: null` and exactly one of three errors: `"Not found"` (the name does not resolve), `"Not a function"` (the name or address resolves to a location outside any function, for example the Mach-O header; an address inside a function resolves to that function), or the text of an exception. `IdaOperationRunner.resolve()` turned any `fn: null` into `AnalysisProtocolError`, which is presented as `unreadable_output`, `retryable: false`, and advises `rea doctor`. On macOS IDA names Mach-O C symbols with a leading underscore, so `rea function <binary> main --json` fails for the `main` an analyst naturally types, while `_main` succeeds. The failure pointed at the installation instead of the argument. Expected: a name or address that IDA reports as not found or not a function is a correctable caller mistake and says which procedure was asked for. Anything else the producer returns stays a producer fault. ## Change and scope - `src/ida/IdaOperationRunner.ts`: `resolve(query, requestedBy?)`. When the caller's own procedure is being resolved (`requestedBy` is the current operation) and the lookup returns the same `query` with `fn: null` and an error that is exactly `"Not found"` or `"Not a function"`, it throws `AnalysisInputError(requestedBy, { cause }, [{ path: [], reason: "invalid_value", message }])`. The issue path is empty, as in the Ghidra not-found mapping, because composed workflows and some CLI commands reach this code with other argument names. The message names the queried procedure and keeps the upstream text: `IDA could not resolve procedure "main": Not found.` The retained cause is an `AnalysisProtocolError` carrying the lookup text. This follows the Ghidra mapping of not-found answers to `AnalysisInputError` and the runner's own `AnalysisInputError("search")`. - Selector provenance. A procedure that a composed workflow takes from provider output is never the caller's input mistake, so it must not become invalid input. `ExecutionOptions` gains an optional per-call `selectorOrigin?: "provider"` (absent means caller-supplied). The workflows set it on every selector derived from provider output: `get_call_graph` (the canonical start and every discovered address), `trace_call_path` (expansions of the resolved start and discovered addresses), the canonical retry in `batch_decompile`, `trace_native_ui_action` (provider-reported function addresses) and `trace_native_values` (procedures discovered after the first). The caller's own selectors stay unmarked: the root of a call graph, the start and goal of a call path, batch items and their fallback retry, the action seed and the first value-trace procedure. `IdaSessionClient` forwards the origin to the runner, which passes `requestedBy` only for an unmarked selector. The option never enters tool parameters, Evidence or raw observations. With this, composed workflows behave as on `main` for provider-derived selectors: a derived address IDA answers `Not found` or `Not a function` is recorded in that node's error entry as `unreadable_output`, and only the caller's own selector becomes `invalid_request`. - Unchanged, still `unreadable_output`: an exception text from upstream, a function returned together with an error, neither a function nor an error, a near-miss or padded or embedded spelling of the two texts, an empty error, an answer for a different query, and the callee resolution inside `relationships()`, which resolves addresses IDA itself reported and is called without `requestedBy` (an address IDA reports that it cannot resolve is a producer inconsistency, not a caller mistake). - The legacy (attached) profile is untouched. For an unknown name it still surfaces the producer's MCP error reply as `unreadable_output`; that is an existing limitation of the legacy path and is not changed here. Tests cover its malformed reply and its error reply. Intentionally not included: - A structured not-found state or a new error code; the existing invalid-request projection is used as is. - Suggesting the underscore-prefixed name or any other corrective hint; REA reports what IDA said and does not guess. - Ghidra and Hopper are not changed: they classify a missing procedure in their own adapters and still report a derived miss in a composed workflow as `invalid_request`. - No docs change: `docs/ida-provider.md` and `docs/testing.md` do not describe how a missing procedure is reported. ## Contract and boundary impact - Semantic owner and earliest changed stage: `src/ida` headless operation runner, at procedure resolution, before any analysis result exists. - CLI and MCP/tool-catalog contract: none. No tool, parameter, schema or code is added; `selectorOrigin` is an internal per-call option, not a tool parameter; the outcome reuses `invalid_request` (category `invalid_input`) with its existing `details.operation` and `details.issues` shape. - Provider, bridge, target-format, or platform compatibility: IDA headless database-supervisor profile; checked on macOS arm64 with IDA 9.1. Attached profile, Hopper and Ghidra unchanged. - Evidence, artifact, provenance, or reconstruction contract: none. Successful lookups produce identical Evidence; only the error classification of a previously misreported case changes. - Process execution, authorization, cleanup, or containment impact: none; the cleanup path of `IdaSessionClient` is unchanged and every new test asserts a clean `close()`. - Generated metadata/package/installation impact: none. ## Evidence and regression coverage - Tests added or updated: `src/ida/IdaOperationFailures.test.ts`, at the session-client boundary with the recording fake connection (no module mocks): `"Not found"` and `"Not a function"` answers for several operations and for a name, a hexadecimal address and an underscore name each give an `AnalysisInputError` whose projection is `invalid_request` / `invalid_input` / `retryable: true` with exactly one issue (empty path) whose message names the query and the upstream text, and the upstream text kept on the retained cause; a query containing a control character is carried JSON-escaped, never raw; nine further cases stay `AnalysisProtocolError` / `unreadable_output` (exception text, function with an error, neither a function nor an error, lowercase spelling, embedded text, padded text, an empty error, a whitespace-only error, an answer for another query); the callee path stays `unreadable_output`; the legacy profile keeps its existing classification for a malformed reply and for an MCP error reply; every case asserts a clean `close()`. Selector provenance: `tests/boundary/providers/ida/selectorProvenance.test.ts` drives `EnhancedTools` over the real headless `IdaSessionClient` with the recording fake connection; for a start `main` whose external callee `0x2000` IDA answers `Not a function`, `get_call_graph` and `trace_call_path` keep the root ok and record only the derived node, by address, as `unreadable_output`, while an unknown start, graph root or batch item (and the batch fallback retry) stays `invalid_request`. `src/application/EnhancedToolSelectorOrigin.test.ts` records the options of every provider call and asserts which calls carry `selectorOrigin: "provider"` (graph expansions, trace expansions, the canonical batch retry) and which do not (initial resolutions, the unresolved batch fallback), and that the origin is never copied into the parameters; the native value trace and native UI action tests do the same for their derived selectors; `tests/composition/analysis-sessions/binarySession.test.ts` shows that `BinarySession.execute` and `withAdmittedAnalysis` forward the origin to the provider client; `src/ida/IdaOperationFailures.test.ts` covers the runner with a provider-origin selector. - Base reproduction or other evidence: the new mapping tests fail on `main` (5 failed in the first test commit and 7 with the final assertions; the "stays unreadable" cases pass there by design). The composed-workflow tests fail on the published head `6b71db81e` (9 failed, among them the call-graph and call-path regressions that show `invalid_request` for the derived node). Real CLI on macOS 26.5 arm64, IDA Professional 9.1, an arm64 binary built from `tests/conformance/ida/inventory.c`, `REA_ANALYSIS_PROVIDER=ida`, `rea function <binary> main --json`: before, `unreadable_output`, `retryable: false`, "Retry once; if it continues, run `rea doctor`"; after, `invalid_request` / `invalid_input`, `retryable: true`, issue `IDA could not resolve procedure "main": Not found.` With `_main` the command succeeds in both builds. After the change the same command with `__mh_execute_header` and with `0x100000000` (the Mach-O header, outside any function) reports `invalid_request` with the upstream text `Not a function`, while `0x100000350`, an address inside `_main`, resolves to `_main`. - Real MCP on the same host in one session: `analyze_function` and `procedure_pseudo_code` for `main` give `invalid_request`; `procedure_address` for `__mh_execute_header` and `procedure_assembly` for `0x100000000` give `invalid_request` with `Not a function`; `procedure_address` for `_main` succeeds. - User-visible CLI/MCP output (after, sanitized): ```json { "error": "Analysis failed", "code": "invalid_request", "category": "invalid_input", "message": "Analysis input is invalid. Check the arguments and try again.", "retryable": true, "remediation": { "action": "Correct the listed arguments and retry." }, "details": { "operation": "analyze_function", "issues": [{ "path": [], "reason": "invalid_value", "message": "IDA could not resolve procedure \"main\": Not found." }] } } ``` - Remaining proof gaps: the real checks ran only on macOS arm64 with IDA 9.1. A real provider-derived miss could not be produced on this host, because IDA currently reports no callees on ARM64 (#1794); the derived-node path is covered by the boundary and application tests. - [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 check:fast`, `npm run check`, `npm run docs:check`, `npm run verify:test-discovery` (822 files) - green - `npm run test:focused` over the thirteen affected files (eight `src/ida` files, the enhanced-tool origin test, the native value and UI trace tests, the IDA selector-provenance boundary test and the binary-session composition test) - 133 passed - `npm run test:fast` - 378 files, 4348 passed, 7 skipped, 0 failed - `npm run test:mcp` - 93 files passed, 2 skipped, 362 passed, 2 skipped - `npm run test:boundary` for `tests/boundary/providers/ida/selectorProvenance.test.ts` after `npm run build:cached` - passed - Real checks on the final code, macOS arm64, IDA 9.1: `rea function <binary> main --json` -> `invalid_request` (`Not found`), `__mh_execute_header` -> `invalid_request` (`Not a function`), `_main` -> Evidence; MCP `analyze_function` and `procedure_pseudo_code` for `main`, `procedure_address` for `__mh_execute_header` and `procedure_assembly` for `0x100000000` -> `invalid_request`, `procedure_address` for `_main` and for `0x100000350` -> `0x10000034c`; `get_call_graph` and `trace_call_path` with the unknown start `main` -> `invalid_request`, with `_main` -> ok; `npm run verify:ida -- --target <inventory> --procedure _rea_fixture_add` -> 2 of 2 passed, all five checks; no IDA worker left running - Mutation checks, each reverted: ignoring the origin in the runner fails 4 tests; dropping the marker in the call graph, the trace expansion, the batch retry, the native value trace or the native UI trace each fails the corresponding 1 to 2 tests; marking every enhanced call as provider-derived fails 4; dropping the origin in `BinarySession` fails 1; copying the origin into the provider parameters fails 3 macOS arm64 only; Linux and Windows were not executed. `test:acceptance` was not run because no acceptance file is touched. ## Compatibility, safety, and release - Breaking changes or migration steps: none for successful calls. A caller or script that matched the old `unreadable_output` for a missing procedure now receives `invalid_request`; that case was misreported and `retryable: false` before. - Real Hopper/Ghidra, browser, or OS coverage: real IDA 9.1 on macOS arm64; Hopper, Ghidra and browser not applicable. - Package or release metadata impact: none. - Security, privacy, process, or containment review: the issue message repeats only the caller's own query (JSON-quoted) and one of two fixed upstream texts; no producer-supplied free text is echoed, and no path, credential or process behaviour changes. ## Review checklist - [x] The change addresses a concrete problem, not an unscoped refactor. - [x] The PR has one focused outcome, and the title follows `type(scope): outcome`. - [x] Related issue is linked. - [x] Tests cover changed behavior and failure paths. - [x] Owning docs, contracts, and generated metadata are updated. - [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 00:55:19 +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#4815
No description provided.