[GH-ISSUE #8277] [Bug] Resolved Ghidra indirect reference can yield a definite negative call path #6517

Open
opened 2026-11-01 21:11:51 +00:00 by bsakaguchi · 13 comments
Owner

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

Originally assigned to: @kaoru0822-kitauji on GitHub.

Before submitting

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

Area

Native analysis or decompilation

Observed behavior

For a computed call through a writable pointer, trace_call_path with Ghidra returned goal_status: "not_reached", relationship_coverage.status: "complete", and no residual unknowns for a function absent from Ghidra's resolved reference graph. Another function in the same fixture writes that function's address into the pointer before a subsequent call through it. The negative result therefore only describes the provider's recorded graph; it cannot rule out other targets of the computed call.

Reproduction

I built a small x86-64 ELF with an initially populated writable function pointer and these functions (abbreviated from the tested assembly):

initially_selected: mov $1,%eax; ret
overwritten_target: mov $2,%eax; ret
change_then_call:
  lea overwritten_target(%rip),%rax
  mov %rax,mutable_slot(%rip)
  call *mutable_slot(%rip)
  ret
initial_only_call:
  call *mutable_slot(%rip)
  ret
sequence:
  call change_then_call
  call initial_only_call
  ret
mutable_slot: .quad initially_selected   # in writable .data

With Ghidra 12.1.4 through MCP, resolve_native_call_targets at the call in change_then_call returned overwritten_target as a static resolved-indirect provider reference. At the call in initial_only_call it returned initially_selected as the provider reference. trace_call_path({start: "initial_only_call", goal: "overwritten_target"}) returned not_reached, complete relationship coverage and no residual unknown. The separate sequence disassembly and pseudocode show the calls in the displayed order. Under the stated straight-line, single-threaded scenario, the instructions indicate that the second call uses the value written by the first; this is static reasoning, not a runtime observation. The target was never executed or changed.

Control: REA/Kuna 1.986 returned unknown with partial callee coverage for both computed-call functions; its typed call-target operation was unavailable. Direct Kuna xrefs described the call as targeting the pointer slot, not a resolved function. LLVM disassembly independently confirmed the instructions and symbol addresses.

Real-provider check of the proposed fix. On macOS arm64 with Ghidra 12.1.4 and JDK 21, I compared REA baseline ae8223ccdfa0d5dbc659ddcbb7d6acf360343c3f with contributor PR #8340 head b7e0b28083d3885ee80d3e23fee66c6ba03ef2dd through real stdio MCP using both compact and full schemas. The source-owned x86-64 ELF contains a writable function-pointer slot, a function that writes another target to it, a subsequent indirect caller, and a separate unresolved register call. The target was not executed and its SHA-256 remained 60152582c3d599b9a6923d7ac7138887b6e5ab9f40114735c88980429cfe70e5.

Ghidra's own analysis recorded a COMPUTED_CALL reference from 0x20117a to 0x201159; no reference was injected in this check. On the baseline, procedure_callees reported that edge with complete coverage, and the path to 0x20115f was not_reached with no residual unknown. On #8340, the same edge remained, coverage became partial with the call-site address, and the path became unknown with a residual explaining the incomplete target set. A separate unresolved-register-call control remained unknown on both, and a no-call function remained a complete negative. Successful outputs validated against each advertised output schema.

This is one fixture and host, not verification of IDA or every computed-call form; the import-slot control below is limited to two fixtures; the separate reverse-traversal check follows below. The baseline is a separate unmerged REA branch, not the PR's direct parent. PR #8340 is currently reported as conflicting with main; this check does not resolve its merge or scope questions.

Reverse-traversal control. On the same unexecuted fixture and real Ghidra, procedure_callers(overwritten_target) returned the observed caller 0x201165 with complete coverage in both profiles. At #8340 head b7e0b280, backward trace_call_path from overwritten_target to initial_only_call returned not_reached and no residual unknown, while the forward path was unknown. This leaves the issue's mutable-slot alternative unqualified in the backward direction. A backward positive from initially_selected to initial_only_call stayed reached.

On separate PR #8443 head ef5a7e68, the same backward negative became unknown with search_scope.discovery: unknown and observed_goal_status: not_reached; the positive stayed reached. The underlying procedure_callers result still reported complete coverage. These are separate, unmerged branches, not a combined fix. All successful replies validated against the advertised compact/full schemas; the target SHA-256 remained unchanged. The result does not measure how often computed targets occur or decide whether a whole-program scan is warranted.

Import-call tradeoff control. Using real Ghidra 12.1.4 via both compact and full stdio MCP profiles, I compared #8340 head b7e0b280 with separate baseline ae8223cc. In a source-owned ARM64 Mach-O, the function loads _malloc through its GOT and calls with blr x16 at 0x1368. Ghidra reported one computed-call reference to 0xc000. The PR retained that edge and changed procedure_callees coverage from complete to partial. In a separate x86-64 ELF with an interposable defined function and R_X86_64_JUMP_SLOT relocation, the caller directly calls its PLT stub at 0x1013b0; its callee coverage remained complete. Both results validated against the advertised schemas.

This demonstrates the PR's import-slot cost for one computed call and a contrasting direct-to-stub call; it does not establish prevalence, runtime binding, or justify assuming GOT calls have fixed targets. Neither target was executed or changed: SHA-256 values were 62da83d290c7b276d8708937930ef12f469f41782710fed439001c01c2747c76 and 741f17c72cea92c0316549c9733cdf77c528d890fde6231bdf4cc8ba325aa630 respectively. Cross-library evidence and interposition are separately discussed in #8221.

Expected behavior

Distinguish a completed traversal of the provider-observed graph from evidence that computed-call targets are exhaustive. Preserve the observed reference and static graph result, but expose residual target uncertainty in a negative answer when it has not been established. This need not imply runtime execution or require an unconditional whole-program data-flow analysis. A machine-direct call with genuinely complete observed adjacency is a useful positive control.

Impact and scope

An agent can read the definite negative and complete relationship status as ruling out a path that the fixture's cross-function state flow permits. The output also has general limitations about unresolved indirect calls, and resolve_native_call_targets correctly warns its reference is static; the issue is that a reported target suppresses remaining computed-target uncertainty in the path result. No real agent behavior or runtime execution was measured. One fixture, one host and two provider versions were exercised.

REA version

Runtime reproduction at e6d0618979f0df3f1d5db218b0fc7b5e6a9cf3f9. Source at main 22e5aa97af8d726400f3f932cae10e0083412038 retains the same relevant logic; its runtime was not retested.

Runtime and platform

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

Target and artifact context

Self-built x86-64 ELF; static analysis only.

Interface

MCP

Earliest failure stage

Native analysis or reconstruction

Relevant output or evidence

Ghidra call at 0x20117a: target reference 0x201159 (initially_selected); selected goal 0x20115f (overwritten_target); path result not_reached, truncated: false, residual_unknowns: []. The instruction at 0x20116c writes 0x20115f into the same slot read at 0x20117a; sequence calls the writer before the reader. xrefs on the slot exposed both sites. A resolved-indirect reference is expressly described as static and not proof of execution.

Typed outcome or recovery expected

A graph-only negative and a possibly incomplete computed-target set should remain separately machine-readable, so the caller can preserve the unknown without discarding known edges.

Analysis or suspected cause

Source-derived: callRelationshipCoverage starts at complete and marks missing targets when a decoded call has no reported call reference or a reported reference is missing from the callee inventory. A computed call with one reference need not trigger that condition. CallPathTracing projects not_reached when there is no path and no residual unknown. This explains the observed result without establishing Ghidra's internal reasoning for the specific reference.

Additional context

Related #8200 concerns call-site/type visibility; #3988 concerns missing IDA relationship coverage. This report is about the completeness and negative-path interpretation of an existing Ghidra computed reference. Suggested checks include the mutable cross-function case, a direct call, unresolved and multiple candidate sites, reverse traversal, and real MCP plus CLI; those checks have not all been run.

cc @gensokyo-dev @kaoru0822-kitauji @N0zoM1z0

Originally created by @morluto on GitHub (Oct 31, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/8277 Originally assigned to: @kaoru0822-kitauji on GitHub. ### 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 Native analysis or decompilation ### Observed behavior For a computed call through a writable pointer, `trace_call_path` with Ghidra returned `goal_status: "not_reached"`, `relationship_coverage.status: "complete"`, and no residual unknowns for a function absent from Ghidra's resolved reference graph. Another function in the same fixture writes that function's address into the pointer before a subsequent call through it. The negative result therefore only describes the provider's recorded graph; it cannot rule out other targets of the computed call. ### Reproduction I built a small x86-64 ELF with an initially populated writable function pointer and these functions (abbreviated from the tested assembly): ```asm initially_selected: mov $1,%eax; ret overwritten_target: mov $2,%eax; ret change_then_call: lea overwritten_target(%rip),%rax mov %rax,mutable_slot(%rip) call *mutable_slot(%rip) ret initial_only_call: call *mutable_slot(%rip) ret sequence: call change_then_call call initial_only_call ret mutable_slot: .quad initially_selected # in writable .data ``` With Ghidra 12.1.4 through MCP, `resolve_native_call_targets` at the call in `change_then_call` returned `overwritten_target` as a static `resolved-indirect` provider reference. At the call in `initial_only_call` it returned `initially_selected` as the provider reference. `trace_call_path({start: "initial_only_call", goal: "overwritten_target"})` returned `not_reached`, complete relationship coverage and no residual unknown. The separate `sequence` disassembly and pseudocode show the calls in the displayed order. Under the stated straight-line, single-threaded scenario, the instructions indicate that the second call uses the value written by the first; this is static reasoning, not a runtime observation. The target was never executed or changed. Control: REA/Kuna 1.986 returned `unknown` with partial callee coverage for both computed-call functions; its typed call-target operation was unavailable. Direct Kuna xrefs described the call as targeting the pointer slot, not a resolved function. LLVM disassembly independently confirmed the instructions and symbol addresses. **Real-provider check of the proposed fix.** On macOS arm64 with Ghidra 12.1.4 and JDK 21, I compared REA baseline `ae8223ccdfa0d5dbc659ddcbb7d6acf360343c3f` with contributor PR #8340 head `b7e0b28083d3885ee80d3e23fee66c6ba03ef2dd` through real stdio MCP using both compact and full schemas. The source-owned x86-64 ELF contains a writable function-pointer slot, a function that writes another target to it, a subsequent indirect caller, and a separate unresolved register call. The target was not executed and its SHA-256 remained `60152582c3d599b9a6923d7ac7138887b6e5ab9f40114735c88980429cfe70e5`. Ghidra's own analysis recorded a `COMPUTED_CALL` reference from `0x20117a` to `0x201159`; no reference was injected in this check. On the baseline, `procedure_callees` reported that edge with complete coverage, and the path to `0x20115f` was `not_reached` with no residual unknown. On #8340, the same edge remained, coverage became partial with the call-site address, and the path became `unknown` with a residual explaining the incomplete target set. A separate unresolved-register-call control remained unknown on both, and a no-call function remained a complete negative. Successful outputs validated against each advertised output schema. This is one fixture and host, not verification of IDA or every computed-call form; the import-slot control below is limited to two fixtures; the separate reverse-traversal check follows below. The baseline is a separate unmerged REA branch, not the PR's direct parent. PR #8340 is currently reported as conflicting with main; this check does not resolve its merge or scope questions. **Reverse-traversal control.** On the same unexecuted fixture and real Ghidra, `procedure_callers(overwritten_target)` returned the observed caller `0x201165` with `complete` coverage in both profiles. At #8340 head `b7e0b280`, backward `trace_call_path` from `overwritten_target` to `initial_only_call` returned `not_reached` and no residual unknown, while the forward path was `unknown`. This leaves the issue's mutable-slot alternative unqualified in the backward direction. A backward positive from `initially_selected` to `initial_only_call` stayed `reached`. On separate PR #8443 head `ef5a7e68`, the same backward negative became `unknown` with `search_scope.discovery: unknown` and `observed_goal_status: not_reached`; the positive stayed `reached`. The underlying `procedure_callers` result still reported complete coverage. These are separate, unmerged branches, not a combined fix. All successful replies validated against the advertised compact/full schemas; the target SHA-256 remained unchanged. The result does not measure how often computed targets occur or decide whether a whole-program scan is warranted. **Import-call tradeoff control.** Using real Ghidra 12.1.4 via both compact and full stdio MCP profiles, I compared #8340 head `b7e0b280` with separate baseline `ae8223cc`. In a source-owned ARM64 Mach-O, the function loads `_malloc` through its GOT and calls with `blr x16` at `0x1368`. Ghidra reported one computed-call reference to `0xc000`. The PR retained that edge and changed `procedure_callees` coverage from complete to partial. In a separate x86-64 ELF with an interposable defined function and `R_X86_64_JUMP_SLOT` relocation, the caller directly calls its PLT stub at `0x1013b0`; its callee coverage remained complete. Both results validated against the advertised schemas. This demonstrates the PR's import-slot cost for one computed call and a contrasting direct-to-stub call; it does not establish prevalence, runtime binding, or justify assuming GOT calls have fixed targets. Neither target was executed or changed: SHA-256 values were `62da83d290c7b276d8708937930ef12f469f41782710fed439001c01c2747c76` and `741f17c72cea92c0316549c9733cdf77c528d890fde6231bdf4cc8ba325aa630` respectively. Cross-library evidence and interposition are separately discussed in #8221. ### Expected behavior Distinguish a completed traversal of the provider-observed graph from evidence that computed-call targets are exhaustive. Preserve the observed reference and static graph result, but expose residual target uncertainty in a negative answer when it has not been established. This need not imply runtime execution or require an unconditional whole-program data-flow analysis. A machine-direct call with genuinely complete observed adjacency is a useful positive control. ### Impact and scope An agent can read the definite negative and complete relationship status as ruling out a path that the fixture's cross-function state flow permits. The output also has general limitations about unresolved indirect calls, and `resolve_native_call_targets` correctly warns its reference is static; the issue is that a *reported* target suppresses remaining computed-target uncertainty in the path result. No real agent behavior or runtime execution was measured. One fixture, one host and two provider versions were exercised. ### REA version Runtime reproduction at `e6d0618979f0df3f1d5db218b0fc7b5e6a9cf3f9`. Source at main `22e5aa97af8d726400f3f932cae10e0083412038` retains the same relevant logic; its runtime was not retested. ### Runtime and platform Node 24.22.1, macOS arm64, Ghidra 12.1.4, Kuna 1.986, JDK 21. ### Target and artifact context Self-built x86-64 ELF; static analysis only. ### Interface MCP ### Earliest failure stage Native analysis or reconstruction ### Relevant output or evidence Ghidra call at `0x20117a`: target reference `0x201159` (`initially_selected`); selected goal `0x20115f` (`overwritten_target`); path result `not_reached`, `truncated: false`, `residual_unknowns: []`. The instruction at `0x20116c` writes `0x20115f` into the same slot read at `0x20117a`; `sequence` calls the writer before the reader. `xrefs` on the slot exposed both sites. A `resolved-indirect` reference is expressly described as static and not proof of execution. ### Typed outcome or recovery expected A graph-only negative and a possibly incomplete computed-target set should remain separately machine-readable, so the caller can preserve the unknown without discarding known edges. ### Analysis or suspected cause Source-derived: [`callRelationshipCoverage`](https://github.com/morluto/rea/blob/22e5aa97af8d726400f3f932cae10e0083412038/bridge/ghidra/ReaGhidraBridge.java) starts at complete and marks missing targets when a decoded call has no reported call reference or a reported reference is missing from the callee inventory. A computed call with one reference need not trigger that condition. [`CallPathTracing`](https://github.com/morluto/rea/blob/22e5aa97af8d726400f3f932cae10e0083412038/src/application/CallPathTracing.ts) projects `not_reached` when there is no path and no residual unknown. This explains the observed result without establishing Ghidra's internal reasoning for the specific reference. ### Additional context Related #8200 concerns call-site/type visibility; #3988 concerns missing IDA relationship coverage. This report is about the completeness and negative-path interpretation of an existing Ghidra computed reference. Suggested checks include the mutable cross-function case, a direct call, unresolved and multiple candidate sites, reverse traversal, and real MCP plus CLI; those checks have not all been run. cc @gensokyo-dev @kaoru0822-kitauji @N0zoM1z0
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:6541074014 --> @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):

Reverse and direct-call controls on the same fixture, same real providers through MCP:

  • Both Ghidra and Kuna reported reached for the direct sequence -> change_then_call edge, and not_reached for _start (a return-only function) to overwritten_target. This is a narrow control, not a guarantee of complete graph discovery in other binaries.
  • For backward traversal from overwritten_target to initial_only_call, Ghidra returned not_reached, complete coverage at every visited node, and no residual unknowns. Its procedure_callers(overwritten_target) returned only change_then_call, the caller with a reported computed reference. The same fixture's sequence first writes overwritten_target into the slot and then calls initial_only_call, as described above. That is an instruction-based possibility, not an observed execution.
  • Kuna returned unknown with partial caller coverage and stated that unresolved indirect calls elsewhere in the image could add callers. Its backward direct initial_only_call -> sequence query still returned reached while retaining partial coverage. Ghidra also reached that known direct caller.

Reverse absence has a different scope from forward expansion: a possible unreported indirect caller need not appear among already visited callers. Any correction should preserve known edges and the ability to report a completed provider-observed graph, while keeping the scope of the negative explicit; the control does not dictate a global scan or claim exhaustive runtime targets. The target digest stayed unchanged and the target was not executed.

<!-- gh-comment-id:6541082034 --> @morluto commented on GitHub (Oct 31, 2026): Reverse and direct-call controls on the same fixture, same real providers through MCP: - Both Ghidra and Kuna reported `reached` for the direct `sequence -> change_then_call` edge, and `not_reached` for `_start` (a return-only function) to `overwritten_target`. This is a narrow control, not a guarantee of complete graph discovery in other binaries. - For backward traversal from `overwritten_target` to `initial_only_call`, Ghidra returned `not_reached`, complete coverage at every visited node, and no residual unknowns. Its `procedure_callers(overwritten_target)` returned only `change_then_call`, the caller with a reported computed reference. The same fixture's `sequence` first writes `overwritten_target` into the slot and then calls `initial_only_call`, as described above. That is an instruction-based possibility, not an observed execution. - Kuna returned `unknown` with partial caller coverage and stated that unresolved indirect calls elsewhere in the image could add callers. Its backward direct `initial_only_call -> sequence` query still returned `reached` while retaining partial coverage. Ghidra also reached that known direct caller. Reverse absence has a different scope from forward expansion: a possible unreported indirect caller need not appear among already visited callers. Any correction should preserve known edges and the ability to report a completed provider-observed graph, while keeping the scope of the negative explicit; the control does not dictate a global scan or claim exhaustive runtime targets. The target digest stayed unchanged and the target was not executed.
Author
Owner

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

Independent architecture/format control on current main b43c329df779708e43ae297f498a844e45d9c923: I compiled an unexecuted arm64 Mach-O where a volatile function pointer starts at _audit_initial; _audit_select_alternate stores _audit_alternate; and _audit_sequence calls that setter before _audit_dispatch, which loads the pointer and performs blr.

Real REA/Ghidra 12.1.4 MCP classified the dispatch call's reference to the initial target as resolved-indirect. A forward path from _audit_dispatch to _audit_alternate and a backward path from _audit_alternate to _audit_dispatch both returned not_reached, complete observed relationship coverage and no residual unknowns. Independent LLVM disassembly showed the prior store and subsequent indirect call; as in the original report, their sequential effect is a static inference, not an observed execution. Both REA providers reached the direct _audit_sequence -> _audit_dispatch control. REA/Kuna 1.986 returned unknown with partial relationship coverage for both indirect-path queries.

This reproduces the graph-scope distinction on a second format/architecture, not a proof about all Ghidra inputs. The target digest was unchanged. Current main was built with build:cached; this check used MCP only.

<!-- gh-comment-id:6541265777 --> @morluto commented on GitHub (Oct 31, 2026): Independent architecture/format control on current main `b43c329df779708e43ae297f498a844e45d9c923`: I compiled an unexecuted arm64 Mach-O where a volatile function pointer starts at `_audit_initial`; `_audit_select_alternate` stores `_audit_alternate`; and `_audit_sequence` calls that setter before `_audit_dispatch`, which loads the pointer and performs `blr`. Real REA/Ghidra 12.1.4 MCP classified the dispatch call's reference to the **initial** target as `resolved-indirect`. A forward path from `_audit_dispatch` to `_audit_alternate` and a backward path from `_audit_alternate` to `_audit_dispatch` both returned `not_reached`, complete observed relationship coverage and no residual unknowns. Independent LLVM disassembly showed the prior store and subsequent indirect call; as in the original report, their sequential effect is a static inference, not an observed execution. Both REA providers reached the direct `_audit_sequence -> _audit_dispatch` control. REA/Kuna 1.986 returned `unknown` with partial relationship coverage for both indirect-path queries. This reproduces the graph-scope distinction on a second format/architecture, not a proof about all Ghidra inputs. The target digest was unchanged. Current main was built with `build:cached`; this check used MCP only.
Author
Owner

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

I exercised the same arm64 mutable-pointer fixture through other REA workflows on b43c329df779708e43ae297f498a844e45d9c923 with real Ghidra 12.1.4 and Kuna 1.986 over MCP:

  • With Ghidra, analyze_function for _audit_dispatch retained the initial target as a callee, an empty unresolved_calls list, and relationship_coverage.status: complete with the diagnostic that only resolved references are returned. get_call_graph carried the same candidate and complete relationship status on that node. Those are provider-graph observations; the setter/sequence from the earlier control means they do not establish an exhaustive set of possible values in the writable slot.
  • trace_native_values returned a resolved-indirect edge to the initial target and preserved its source Evidence, with general limitations that alias dependencies remain unknown and a static reference is not proof of execution. Its specific unknown in this run was a missing argument binding; it did not separately enumerate the alternate pointer value.
  • trace_native_ui_action, seeded by the native function (not an actual UI action), preserved the indirect call site and candidate, marked static_native_calls partial, and explicitly warned that a resolved static edge does not establish runtime reachability. Its cross-function value-flow facet was unsupported. This is a useful existing uncertainty presentation even though it does not resolve the mutable slot.
  • Kuna's graph and dossier marked relevant call coverage partial; its value trace explicitly reported no native def-use model. The native-seed UI trace had no edge and marked static-call coverage partial.

The target digest stayed unchanged and the binary was not executed. This is one scripted native seed and fixture, not a UI or autonomous-agent evaluation. It suggests the fix should consider the shared relationship/completeness owner and its consumers, rather than changing only the final trace_call_path wording or dropping the known candidate.

<!-- gh-comment-id:6541339780 --> @morluto commented on GitHub (Oct 31, 2026): I exercised the same arm64 mutable-pointer fixture through other REA workflows on `b43c329df779708e43ae297f498a844e45d9c923` with real Ghidra 12.1.4 and Kuna 1.986 over MCP: - With Ghidra, `analyze_function` for `_audit_dispatch` retained the initial target as a callee, an empty `unresolved_calls` list, and `relationship_coverage.status: complete` with the diagnostic that only resolved references are returned. `get_call_graph` carried the same candidate and complete relationship status on that node. Those are provider-graph observations; the setter/sequence from the earlier control means they do not establish an exhaustive set of possible values in the writable slot. - `trace_native_values` returned a `resolved-indirect` edge to the initial target and preserved its source Evidence, with general limitations that alias dependencies remain unknown and a static reference is not proof of execution. Its specific unknown in this run was a missing argument binding; it did not separately enumerate the alternate pointer value. - `trace_native_ui_action`, seeded by the native function (not an actual UI action), preserved the indirect call site and candidate, marked `static_native_calls` partial, and explicitly warned that a resolved static edge does not establish runtime reachability. Its cross-function value-flow facet was unsupported. This is a useful existing uncertainty presentation even though it does not resolve the mutable slot. - Kuna's graph and dossier marked relevant call coverage partial; its value trace explicitly reported no native def-use model. The native-seed UI trace had no edge and marked static-call coverage partial. The target digest stayed unchanged and the binary was not executed. This is one scripted native seed and fixture, not a UI or autonomous-agent evaluation. It suggests the fix should consider the shared relationship/completeness owner and its consumers, rather than changing only the final `trace_call_path` wording or dropping the known candidate.
Author
Owner

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

Test and offline-composition follow-up

The existing test suite does exercise a real Ghidra indirect call: verifyUnresolvedCallEvidence in scripts/lib/native-value-e2e.mjs checks a callback passed as a function parameter through real stdio MCP and expects partial relationships and an unknown path. The fixture has no resolved candidate for that call. It does not cover the different shape above, where Ghidra reports one computed-call candidate while another value can be written to the slot.

The separate callPathSearchExtentMcp.test.ts constructs Evidence with explicit unresolved calls and checks that the consumer preserves them. That is useful coverage of the handoff, but it cannot detect a real provider reporting the relationship as complete in the first place.

With the same real Ghidra fixture and two complete dossiers, a separate actual build_call_path probe returned found for the initial candidate and not_found with search_scope.exhaustive: true for the alternate when the MCP input allowance was raised. That is the result for the supplied observed graph; it does not prove runtime reachability or unreachability. The generic provider limitations were also returned. Under the default allowance, both two-dossier requests stopped at the input limit instead; the separate composition issue is #8281.

Proposed regression: a real-provider E2E should cover a computed call with both an observed candidate and an independently evidenced writable alternative, checking that the candidate remains available while the unproven negative retains appropriate uncertainty through live paths and offline composition. The fixture was analyzed statically and not executed.

<!-- gh-comment-id:6541369848 --> @morluto commented on GitHub (Oct 31, 2026): ### Test and offline-composition follow-up The existing test suite **does** exercise a real Ghidra indirect call: `verifyUnresolvedCallEvidence` in [`scripts/lib/native-value-e2e.mjs`](https://github.com/morluto/rea/blob/b43c329df779708e43ae297f498a844e45d9c923/scripts/lib/native-value-e2e.mjs) checks a callback passed as a function parameter through real stdio MCP and expects partial relationships and an unknown path. The fixture has no resolved candidate for that call. It does not cover the different shape above, where Ghidra reports one computed-call candidate while another value can be written to the slot. The separate [`callPathSearchExtentMcp.test.ts`](https://github.com/morluto/rea/blob/b43c329df779708e43ae297f498a844e45d9c923/tests/boundary/mcp/callPathSearchExtentMcp.test.ts) constructs Evidence with explicit unresolved calls and checks that the consumer preserves them. That is useful coverage of the handoff, but it cannot detect a real provider reporting the relationship as complete in the first place. With the same real Ghidra fixture and two complete dossiers, a separate actual `build_call_path` probe returned `found` for the initial candidate and `not_found` with `search_scope.exhaustive: true` for the alternate when the MCP input allowance was raised. That is the result for the supplied observed graph; it does not prove runtime reachability or unreachability. The generic provider limitations were also returned. Under the default allowance, both two-dossier requests stopped at the input limit instead; the separate composition issue is #8281. Proposed regression: a real-provider E2E should cover a computed call with both an observed candidate and an independently evidenced writable alternative, checking that the candidate remains available while the unproven negative retains appropriate uncertainty through live paths and offline composition. The fixture was analyzed statically and not executed.
Author
Owner

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

Cross-target comparison and structural verification

I built two source-owned arm64 Mach-O fixtures with the same writable function-pointer initializer and identical dispatcher/sequence instructions. The setter in one stores audit_initial; the setter in the other stores audit_alternate. LLVM disassembly confirms the setter's materialized function address differs and the sequence calls the setter before the dispatcher. This is a static, straight-line interpretation; neither binary was executed.

On REA b43c329df779708e43ae297f498a844e45d9c923 with Ghidra 12.1.4, I analyzed each target through stdio MCP, exported each Evidence bundle before close_binary, imported both bundles, and compared retained dossiers:

  • Both dispatcher dossiers reported _audit_initial as the sole callee, no unresolved calls, and complete relationship coverage. compare_functions reported the dispatcher's calls as unchanged, carrying a generic indirect-call limitation; its overall status was unknown for a separate reference-identity limitation.
  • An explicit verify_reconstruction structural-function claim for dispatcher calls passed; an overall claim was unknown, so the combined result was unknown. The passing claim retained generic provider caveats and is a comparison of supplied structural observations, not proof of behavior or a claim of whole-program equivalence.
  • As a control, comparing the setters reported changed pseudocode, assembly, references and names. find_changed_behavior retained these as four static candidates and returned behavior unknown. Comparing the unchanged sequence also returned behavior unknown.

This shows the effect of candidate-only coverage can extend to a downstream structural claim, even when the broader comparison remains unknown. A regression for the producing boundary should check this handoff as well, while continuing to preserve the observed candidate and distinguish local structure from runtime or cross-function effects. Both target SHA-256 digests remained unchanged.

<!-- gh-comment-id:6541388795 --> @morluto commented on GitHub (Oct 31, 2026): ### Cross-target comparison and structural verification I built two source-owned arm64 Mach-O fixtures with the same writable function-pointer initializer and identical dispatcher/sequence instructions. The setter in one stores `audit_initial`; the setter in the other stores `audit_alternate`. LLVM disassembly confirms the setter's materialized function address differs and the sequence calls the setter before the dispatcher. This is a static, straight-line interpretation; neither binary was executed. On REA `b43c329df779708e43ae297f498a844e45d9c923` with Ghidra 12.1.4, I analyzed each target through stdio MCP, exported each Evidence bundle before `close_binary`, imported both bundles, and compared retained dossiers: - Both dispatcher dossiers reported `_audit_initial` as the sole callee, no unresolved calls, and complete relationship coverage. `compare_functions` reported the dispatcher's calls as `unchanged`, carrying a generic indirect-call limitation; its overall status was `unknown` for a separate reference-identity limitation. - An explicit `verify_reconstruction` structural-function claim for dispatcher `calls` passed; an `overall` claim was unknown, so the combined result was unknown. The passing claim retained generic provider caveats and is a comparison of supplied structural observations, not proof of behavior or a claim of whole-program equivalence. - As a control, comparing the setters reported changed pseudocode, assembly, references and names. `find_changed_behavior` retained these as four static candidates and returned behavior unknown. Comparing the unchanged sequence also returned behavior unknown. This shows the effect of candidate-only coverage can extend to a downstream structural claim, even when the broader comparison remains unknown. A regression for the producing boundary should check this handoff as well, while continuing to preserve the observed candidate and distinguish local structure from runtime or cross-function effects. Both target SHA-256 digests remained unchanged.
Author
Owner

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

A correction to the proposed validation above: the tested REA b43c329 CLI does not advertise trace_call_path or build_call_path; rea trace is the separate literal-feature workflow. The directly applicable end-to-end path regression is therefore public stdio MCP, not an assumed CLI command. This is a statement about the tested CLI, not a proposal to add an API-parity wrapper.

A related real CLI observation is available: on the previously reported arm64 writable-pointer fixture, rea resolve-native-call-targets <fixture> 0x1000003f4 --provider ghidra --format json --full-output succeeded with mechanism: computed, status: resolved-indirect, and one static provider-reference candidate. Its limitations correctly say that a resolved reference is not proof of execution. The fixture's SHA-256 remained f771508a5d2a8afee3419ad1a075c6f4ad499fbb91b2584f49c9c006fb908e05; the target was not executed. This separate call-site observation is not a CLI reproduction of the path result. The inspected CLI and relevant path/bridge source files are unchanged between b43c329 and the local origin/main checked today.

<!-- gh-comment-id:6542265287 --> @morluto commented on GitHub (Oct 31, 2026): A correction to the proposed validation above: the tested REA `b43c329` CLI does not advertise `trace_call_path` or `build_call_path`; `rea trace` is the separate literal-feature workflow. The directly applicable end-to-end path regression is therefore public stdio MCP, not an assumed CLI command. This is a statement about the tested CLI, not a proposal to add an API-parity wrapper. A related real CLI observation is available: on the previously reported arm64 writable-pointer fixture, `rea resolve-native-call-targets <fixture> 0x1000003f4 --provider ghidra --format json --full-output` succeeded with `mechanism: computed`, `status: resolved-indirect`, and one static `provider-reference` candidate. Its limitations correctly say that a resolved reference is not proof of execution. The fixture's SHA-256 remained `f771508a5d2a8afee3419ad1a075c6f4ad499fbb91b2584f49c9c006fb908e05`; the target was not executed. This separate call-site observation is not a CLI reproduction of the path result. The inspected CLI and relevant path/bridge source files are unchanged between `b43c329` and the local `origin/main` checked today.
Author
Owner

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

Follow-up on the same ELF with REA b43c329, Ghidra 12.1.4 and Kuna 1.986 over real MCP:

  • Ghidra analyze_function(initial_only_call, detail=complete) returned initially_selected as a callee, an empty unresolved_calls list, and top-level relationship coverage complete with the general resolved-reference diagnostic. get_call_graph returned the same candidate and marked that node's relationship coverage complete. This extends the reported coverage concern beyond trace_call_path.
  • The native-seeded Ghidra trace_native_ui_action preserved the indirect_call edge as resolved, but its overall static_native_calls coverage was explicitly partial; it also described cross-function value flow as unsupported and warned that the edge does not prove runtime reachability. This is a useful existing contrast, not an assertion that it recovers the other target.
  • Kuna's dossier and call graph gave partial relationship coverage for the indirect call. Its value trace reported no def-use model. The target remained unchanged and was not executed.

The separately tested Kuna ELF UI trace did not run because its dispatch preflight failed; its discovery mismatch is reported in #8322. This follow-up does not establish runtime dispatch or all composed-tool behavior.

<!-- gh-comment-id:6544870591 --> @morluto commented on GitHub (Oct 31, 2026): Follow-up on the same ELF with REA `b43c329`, Ghidra 12.1.4 and Kuna 1.986 over real MCP: - Ghidra `analyze_function(initial_only_call, detail=complete)` returned `initially_selected` as a callee, an empty `unresolved_calls` list, and top-level relationship coverage `complete` with the general resolved-reference diagnostic. `get_call_graph` returned the same candidate and marked that node's relationship coverage complete. This extends the reported coverage concern beyond `trace_call_path`. - The native-seeded Ghidra `trace_native_ui_action` preserved the `indirect_call` edge as resolved, but its overall `static_native_calls` coverage was explicitly `partial`; it also described cross-function value flow as unsupported and warned that the edge does not prove runtime reachability. This is a useful existing contrast, not an assertion that it recovers the other target. - Kuna's dossier and call graph gave partial relationship coverage for the indirect call. Its value trace reported no def-use model. The target remained unchanged and was not executed. The separately tested Kuna ELF UI trace did not run because its dispatch preflight failed; its discovery mismatch is reported in #8322. This follow-up does not establish runtime dispatch or all composed-tool behavior.
Author
Owner

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

Current-main follow-up after #8299 fixed retained dossier inputs: on a989c0e458c91af177e097abd7be0fd021b0401d, a fresh target-free MCP server imported a bundle from a real Ghidra session on the earlier arm64 writable-slot fixture. With the default input allowance and two retained dossier references, build_call_path still returned status: not_found, search_scope.exhaustive: true for the alternate function, with both source links and the provider's general limitations. The original observed-candidate path succeeded, and both offline operations used the same imported source Evidence. The target was not opened in the offline server and the bundle hash was unchanged. This is an exhaustive statement about the supplied observed graph, not proof that computed runtime targets are exhaustive; the composition fix and this coverage question are separate.

<!-- gh-comment-id:6544951118 --> @morluto commented on GitHub (Oct 31, 2026): Current-main follow-up after #8299 fixed retained dossier inputs: on `a989c0e458c91af177e097abd7be0fd021b0401d`, a fresh target-free MCP server imported a bundle from a real Ghidra session on the earlier arm64 writable-slot fixture. With the default input allowance and two retained dossier references, `build_call_path` still returned `status: not_found`, `search_scope.exhaustive: true` for the alternate function, with both source links and the provider's general limitations. The original observed-candidate path succeeded, and both offline operations used the same imported source Evidence. The target was not opened in the offline server and the bundle hash was unchanged. This is an exhaustive statement about the supplied observed graph, not proof that computed runtime targets are exhaustive; the composition fix and this coverage question are separate.
Author
Owner

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

Retested the previously documented arm64 Mach-O mutable-slot fixture on current REA c287a73276a7f751ca65a8cc2219a29d5914bc2c, Ghidra 12.1.4 and Kuna 1.986 through real stdio MCP. Ghidra still reports the initial target as a static resolved-indirect provider reference, and returns not_reached, complete reported relationship coverage and no residual unknowns for both forward and reverse paths to the alternate. Kuna reports unknown with partial relationship coverage in both directions. Both providers reached the direct-call control. The fixture's source and disassembly establish the static write/call scenario described earlier, not runtime execution; its SHA-256 was unchanged. This is a current-revision confirmation of the existing issue, not a new correctness or performance claim.

<!-- gh-comment-id:6545771638 --> @morluto commented on GitHub (Oct 31, 2026): Retested the previously documented arm64 Mach-O mutable-slot fixture on current REA `c287a73276a7f751ca65a8cc2219a29d5914bc2c`, Ghidra 12.1.4 and Kuna 1.986 through real stdio MCP. Ghidra still reports the initial target as a static `resolved-indirect` provider reference, and returns `not_reached`, complete reported relationship coverage and no residual unknowns for both forward and reverse paths to the alternate. Kuna reports unknown with partial relationship coverage in both directions. Both providers reached the direct-call control. The fixture's source and disassembly establish the static write/call scenario described earlier, not runtime execution; its SHA-256 was unchanged. This is a current-revision confirmation of the existing issue, not a new correctness or performance claim.
Author
Owner

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

A real shipped-binary positive control for the existing uncertainty behavior: REA 2d5dec26c31662764c9adfe3ff996174901b8d49, macOS arm64, Ghidra 12.1.4 and Kuna 1.986, Homebrew Brotli 1.2.0 libbrotlidec.1.2.0.dylib SHA-256 d7ac1e69b6c443341fb4302de0169d187489d58d8a641c8eadd6db9ca2cd0cbf. Its installed public header describes optional allocator/free callbacks. Independent otool shows register calls at 0x12f8 and 0x1348 in _BrotliDecoderCreateInstance, and a direct call to _BrotliDecoderStateInit at 0x1320.

Ghidra resolve_native_call_targets reports the two callback sites unresolved and the direct site direct. Both Ghidra and Kuna trace_call_path reach _BrotliDecoderStateInit while preserving partial callee coverage; an unrelated _BrotliDecoderVersion goal returns unknown rather than a definite negative. get_call_graph summaries likewise retain partial coverage at the entry. Ghidra trace_native_values identifies both callback sites as unresolved; Kuna reports its unavailable native def-use model explicitly. These are static results, not runtime observations. The library and installed header hashes remained unchanged; neither was executed. Successful MCP responses were validated by the SDK against the compact advertised schemas. My initial get_call_graph request incorrectly supplied depth; the reported graph is from the corrected separate call.

This is the unresolved-target case behaving conservatively; it does not resolve the candidate-plus-writable-alternate issue reported here. The same shipped sample also reproduces Kuna's unrelated self-data-xref issue: Noelo-Lab/kuna#1334.

<!-- gh-comment-id:6545896374 --> @morluto commented on GitHub (Oct 31, 2026): A real shipped-binary positive control for the existing uncertainty behavior: REA `2d5dec26c31662764c9adfe3ff996174901b8d49`, macOS arm64, Ghidra 12.1.4 and Kuna 1.986, Homebrew Brotli 1.2.0 `libbrotlidec.1.2.0.dylib` SHA-256 `d7ac1e69b6c443341fb4302de0169d187489d58d8a641c8eadd6db9ca2cd0cbf`. Its installed public header describes optional allocator/free callbacks. Independent `otool` shows register calls at `0x12f8` and `0x1348` in `_BrotliDecoderCreateInstance`, and a direct call to `_BrotliDecoderStateInit` at `0x1320`. Ghidra `resolve_native_call_targets` reports the two callback sites unresolved and the direct site direct. Both Ghidra and Kuna `trace_call_path` reach `_BrotliDecoderStateInit` while preserving partial callee coverage; an unrelated `_BrotliDecoderVersion` goal returns `unknown` rather than a definite negative. `get_call_graph` summaries likewise retain partial coverage at the entry. Ghidra `trace_native_values` identifies both callback sites as unresolved; Kuna reports its unavailable native def-use model explicitly. These are static results, not runtime observations. The library and installed header hashes remained unchanged; neither was executed. Successful MCP responses were validated by the SDK against the compact advertised schemas. My initial `get_call_graph` request incorrectly supplied `depth`; the reported graph is from the corrected separate call. This is the unresolved-target case behaving conservatively; it does not resolve the candidate-plus-writable-alternate issue reported here. The same shipped sample also reproduces Kuna's unrelated self-data-xref issue: Noelo-Lab/kuna#1334.
Author
Owner

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

I tested one deliberately conservative local experiment, not a proposed complete fix, against real Ghidra 12.1.4 and REA 60149282fcfc05ac32fbc94434c107710120da6f. In an isolated worktree, callBoundaryObservation additionally counted decoded computed calls that already had a ReferenceManager call destination; callRelationshipCoverage then marked callee coverage partial for those sites while preserving the reported callees. No other production source was changed.

On the existing unexecuted mutable-pointer arm64 Mach-O:

  • The baseline forward path to the alternate was not_reached with complete coverage; the experiment returned unknown with a computed-target-set limitation.
  • The known initial candidate remained reached with partial coverage. A direct-call positive and a return-only leaf negative kept their prior reached/not-reached results and complete coverage.
  • The dossier and get_call_graph both retained the known callee and propagated partial coverage. A graph view exported and imported into a fresh target-free server was identical to the live view.
  • The reverse path still returned not_reached with complete caller coverage. A per-function outgoing check cannot establish completeness of incoming references from other functions; this experiment does not solve reverse traversal.

On the previously tested PE import-through-IAT control, the known external path remained reached but changed from complete to partial, because that call is decoded as computed. This illustrates the cost of blanket conservatism; how to distinguish a bounded import target from an incomplete set needs a typed basis, not an assumption based on names or raw bytes. On the shipped Brotli unresolved-callback control, the direct path remained reached with partial coverage and an unrelated goal remained unknown.

All successful calls used the SDK's compact advertised-schema validator; three static targets' hashes were unchanged and none was executed. The local patch was not submitted or merged. Remaining work includes reverse coverage, precise candidate/exhaustiveness semantics, other providers and architectures, cancellation/resource implications of any image-wide analysis, and meaningful real CLI/MCP regression coverage. This experiment does not establish those properties.

<!-- gh-comment-id:6546039478 --> @morluto commented on GitHub (Oct 31, 2026): I tested one deliberately conservative **local experiment**, not a proposed complete fix, against real Ghidra 12.1.4 and REA `60149282fcfc05ac32fbc94434c107710120da6f`. In an isolated worktree, `callBoundaryObservation` additionally counted decoded computed calls that already had a ReferenceManager call destination; `callRelationshipCoverage` then marked callee coverage partial for those sites while preserving the reported callees. No other production source was changed. On the existing unexecuted mutable-pointer arm64 Mach-O: - The baseline forward path to the alternate was `not_reached` with complete coverage; the experiment returned `unknown` with a computed-target-set limitation. - The known initial candidate remained `reached` with partial coverage. A direct-call positive and a return-only leaf negative kept their prior reached/not-reached results and complete coverage. - The dossier and `get_call_graph` both retained the known callee and propagated partial coverage. A graph view exported and imported into a fresh target-free server was identical to the live view. - **The reverse path still returned `not_reached` with complete caller coverage.** A per-function outgoing check cannot establish completeness of incoming references from other functions; this experiment does not solve reverse traversal. On the previously tested PE import-through-IAT control, the known external path remained reached but changed from complete to partial, because that call is decoded as computed. This illustrates the cost of blanket conservatism; how to distinguish a bounded import target from an incomplete set needs a typed basis, not an assumption based on names or raw bytes. On the shipped Brotli unresolved-callback control, the direct path remained reached with partial coverage and an unrelated goal remained unknown. All successful calls used the SDK's compact advertised-schema validator; three static targets' hashes were unchanged and none was executed. The local patch was not submitted or merged. Remaining work includes reverse coverage, precise candidate/exhaustiveness semantics, other providers and architectures, cancellation/resource implications of any image-wide analysis, and meaningful real CLI/MCP regression coverage. This experiment does not establish those properties.
Author
Owner

@morluto commented on GitHub (Nov 1, 2026):

Native Ghidra reference and PE permission controls

I used Ghidra 12.1.4 analyzeHeadless with a read-only script against the source-owned x86-64 ELF and PE fixtures. At the ELF writable-pointer call (0x20117a), Ghidra reported a READ reference to writable .data/mutable_slot and an ANALYSIS COMPUTED_CALL reference to the internal initially_selected function. At the PE import call (0x14000100c), it reported a READ reference to .rdata and an ANALYSIS COMPUTED_CALL reference to external AUDIT.DLL::audit_transform.

For a controlled counterexample to treating external imports or section permissions as interchangeable, I made an owned PE copy with only the .rdata writable section-characteristic bit changed (one byte in the file). Ghidra still reported the same external symbol and reference types, but its memory block changed from non-writable to writable. This is an intentionally modified static fixture, not evidence about a naturally occurring executable or runtime loader behavior.

On REA 025272ee2a9763a3f174a9dba7a8eda8fc722cf3 with real Ghidra and stdio MCP, resolve_native_call_targets and inspect_native_instruction returned identical normalized call-site observations for both PE files. The latter exposed both the slot READ reference and the external computed-call reference; inspect_native_load_image separately reported .rdata.writable: false versus true. The section contents' observed SHA-256 matched. Both files' full hashes remained unchanged during REA analysis; neither was executed. Compact advertised schemas were used by the SDK.

Ghidra documents MemoryBlock.isWrite as the write property of its program memory block, and Reference.getSource as the reference's source. These facts are useful static provenance; neither independently proves an exhaustive runtime target set. In particular, a rule that assumes an external target has a non-writable slot is not valid for this fixture. Conversely, this case does not prove that a writable slot is actually changed or that a read-only slot cannot change at runtime. The existing REA tools can expose the relevant observations without introducing another provider wrapper; whether a bounded path workflow should surface or join them remains a design question related to #8200 and #8340.

<!-- gh-comment-id:6553509634 --> @morluto commented on GitHub (Nov 1, 2026): ### Native Ghidra reference and PE permission controls I used Ghidra 12.1.4 `analyzeHeadless` with a read-only script against the source-owned x86-64 ELF and PE fixtures. At the ELF writable-pointer call (`0x20117a`), Ghidra reported a `READ` reference to writable `.data`/`mutable_slot` and an `ANALYSIS` `COMPUTED_CALL` reference to the internal `initially_selected` function. At the PE import call (`0x14000100c`), it reported a `READ` reference to `.rdata` and an `ANALYSIS` `COMPUTED_CALL` reference to external `AUDIT.DLL::audit_transform`. For a controlled counterexample to treating external imports or section permissions as interchangeable, I made an owned PE copy with **only the `.rdata` writable section-characteristic bit changed** (one byte in the file). Ghidra still reported the same external symbol and reference types, but its memory block changed from non-writable to writable. This is an intentionally modified static fixture, not evidence about a naturally occurring executable or runtime loader behavior. On REA `025272ee2a9763a3f174a9dba7a8eda8fc722cf3` with real Ghidra and stdio MCP, `resolve_native_call_targets` and `inspect_native_instruction` returned identical normalized call-site observations for both PE files. The latter exposed both the slot `READ` reference and the external computed-call reference; `inspect_native_load_image` separately reported `.rdata.writable: false` versus `true`. The section contents' observed SHA-256 matched. Both files' full hashes remained unchanged during REA analysis; neither was executed. Compact advertised schemas were used by the SDK. Ghidra documents [`MemoryBlock.isWrite`](https://ghidra.re/ghidra_docs/api/ghidra/program/model/mem/MemoryBlock.html#isWrite()) as the write property of its program memory block, and [`Reference.getSource`](https://ghidra.re/ghidra_docs/api/ghidra/program/model/symbol/Reference.html#getSource()) as the reference's source. These facts are useful static provenance; neither independently proves an exhaustive runtime target set. In particular, a rule that assumes an external target has a non-writable slot is not valid for this fixture. Conversely, this case does not prove that a writable slot is actually changed or that a read-only slot cannot change at runtime. The existing REA tools can expose the relevant observations without introducing another provider wrapper; whether a bounded path workflow should surface or join them remains a design question related to #8200 and #8340.
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#6517
No description provided.