mirror of
https://github.com/morluto/rea.git
synced 2026-11-05 06:45:22 +00:00
[GH-ISSUE #8344] [Design] Discover candidates before selecting an existing Ghidra program #6550
Open
opened 2026-11-01 21:12:09 +00:00 by bsakaguchi
·
13 comments
No Branch/Tag specified
main
codex/procedure-reference-stacked
release-please--branches--main--components--rea-agents
release-please--branches--main--components--rea-agents--release-notes
codex/kuna-independent-retry
codex/byte-search-overlay-coverage
codex/open-failure-target-receipt
codex/kuna-xref-evidence
codex/ghidra-ambiguous-procedure-recovery
codex/function-source-bundle
codex/trace-function-integration-prototype
codex/snapshot-source-closure
codex/recorded-evidence-array-pages
codex/evidence-recovery-redesign
codex/trace-feature-address-scope
codex/call-discovery-scope
codex/ghidra-literal-candidate-evidence
codex/decompile-directory-independent-retry
codex/retry-provenance-base
codex/value-flow-capability-experiment
codex/ghidra-containing-instruction
cursor/readme-discord-button-ef9f
dependabot/github_actions/actions/setup-node-7.1.0
dependabot/github_actions/actions/upload-artifact-7.0.2
dependabot/npm_and_yarn/npm-minor-and-patch-d8d97ce6df
codex/kuna-1989
fix/8559-native-ui-fixture-ownership
fix/8555-go-verifier-mcp-ownership
codex/provider-capability-recovery
fix/8642-rizin-target-stdin
codex/ghidra-memory-effect-scope
codex/ghidra-annotation-entry-selector
codex/ghidra-readonly-recovery
codex/ghidra-regex-verifier-recovery
codex/evidence-discovery-view-guidance
fix/8545-bulk-append-overflow
fix/8428-navigation-refusal-alternatives
codex/address-context-recovery
codex/matching-member-limitations
codex/unknown-scope-contract
codex/evidence-view-guidance
feat/8373-native-trace-views
codex/ghidra-snapshot-verifier-binding
codex/elf-export-visibility
codex/evidence-view-recovery
codex/native-reference-cli-help
codex/provider-candidate-observation-scope
codex/kuna-session-verification
codex/ghidra-switch-lifetime-guidance
codex/pseudocode-paging-discovery
codex/clarify-evidence-view-support
codex/kuna-xrefs-selector-recovery
codex/context-capacity-recovery
codex/evidence-closure-large-links
codex/same-function-evidence-identity
codex/ghidra-verifier-inspected-jdk
codex/audit-guided-workflows
codex/crash-prompt-retained-dossiers
codex/analysis-view-error-recovery
codex/kuna-verifier-target-selection
codex/ghidra-selected-project-call-coverage
codex/docs-native-analysis-scope
codex/ghidra-java-socket-boundary
codex/ghidra-retained-snapshot-verifier
codex/ghidra-search-inventory-facts
fix/8284-zero-argument-parameter-bindings
fix/8198-ui-action-handler-offset
fix/8257-elf-entry-label-identity
fix/8304-deferred-output-schema-projection
fix/8277-call-path-computed-target-uncertainty
fix/pr-8089-conflicts
fix/ci-static-duplicate-export
dependabot/npm_and_yarn/lint-staged-17.6.0
dependabot/github_actions/actions/download-artifact-8.0.2
dependabot/npm_and_yarn/write-file-atomic-8.0.0
dependabot/npm_and_yarn/canonicalize-5.1.0
dependabot/github_actions/actions/cache/restore-6.1.0
dependabot/npm_and_yarn/toon-format/toon-4.4.0
dependabot/github_actions/actions/deploy-pages-5.0.1
codex/review-fixes-pr-2403
codex/review-fixes-pr-2400
codex/process-terminal-width
codex/test-brittleness-cleanup
verify/7167-browser-connect-macos
fix/7167-browser-connect
feat/native-matching-allowance-setting
feat/browser-scenario-dry-run
test/managed-caller-parameters-20261024
codex/parser-fortythird-boundaries
fix/6926-pinvoke-platform-decorations-20261023
fix/6889-har-body-encoding-20261023
fix/6888-sqlite-schema-budget-20261023
fix/dependency-security-gates-6708
cursor/evidence-overwrite-rebase-b4ac
feat/4057-verify-native-exports
fix/windows-acl-observer-overhead-20261022
cursor/erlang-beam-restack-4476
cursor/pe-certificate-review-bf32
fix/6116-javascript-application-fs-errors
fix/6061-unmapped-runtime-script-unknown
feat/browser-scenario-summary
fix/pe-certificates
codex/verify-3407
cursor/rebase-5699
cursor/rebase-5352
cursor/rebase-5600
cursor/fix-cli-schema-omission-10a8
codex/audit-hopper-shutdown-reasons-20261021
fix/demangle-swift-omitted-record
fix/nib-boolean-value-types
cursor/cancellation-partial-observation-a5ac
fix/ib-object-limit-reasons
fix/electron-fuse-limit-advice
fix/kuna-doctor-dangling-cache
fix/fixed-budget-refusal-advice
fix/js-ipc-listener-dedup
fix/js-return-member-provenance
fix/dmg-integrity-retry-advice
fix/ida-doctor-readiness
fix/kuna-version-probe-output-drain
fix/legacy-swift-runtime-name-matching
fix/dylib-lazy-load-commands
fix/browser-scenario-startup-cancel
fix/swift-nested-punycode-classification
fix/compare-unselected-scopes
fix/objc-method-budget-coverage
fix/dmg-mounted-root-observations
audit/ci-tests-20261019-round2
release/6.7.0
fix/wabt-cleanup-retry-resolution
fix/electron-fuse-elf-pe-architecture
fix/native-plist-descriptor-ownership
fix/kuna-caller-cleanup-ownership
experiment/atomic-codemode-20261016
review/first-contributor-3776
fix/call-resolution-full-read-cycles
fix/member-mutation-selection-repeat
experiment/atomic-re-tools-20261015
perf/member-mutation-lazy-origins
perf/static-network-mutation-index
fix/hopper-startup-lifecycle
audit/3050-windows-proof-20261015
feat/windows-window-capture-2183
audit/3049-windows-proof-20261015
audit/3735-macos-fixtures-20261015
fix/batch-decompile-null-pseudocode
fix/kuna-inventory-honors-jobs
experiment/browser-codemode-tool-design-20261015
fix/hopper-clean-runtime-argv
codex/review-2049-27f1b367
review-fixes/pr-2787-routing
fix/3360-scoped-this-emitters
fix/3367-replaced-global-effects
fix/3838-compact-any-value-items
test/hopper-download-probe-20261015
codex/windows-runtime-creation-red-green-20261015
codex/windows-readonly-root-red-green-20261015
codex/windows-root-share-red-green-20261015
codex/exp-1685-win-mcp-startup
codex/bug-sweep-20261015
investigate/2190-windows-package-phases
investigate/3543-macos-cli-deadline
perf/3281-reverse-tools-feedback-quality
fix/3406-hopper-doctor-fixture
fix/3341-wrapper-require-test-context
fix/3301-owned-uninstall-fixtures
fix/3164-compiler-input-ownership
fix/pcap-cleanup-stdout-newline-20261014
perf/interface-builder-summary
codex/review-fixes-pr-1977
fix/ghidra-effective-jvm-platform
fix/docs-turkish-rust-link-20261013
fix/main-maintenance-ci-20261010
fix/recover-binary-session-process-effects
codex/review-fixes-pr-2457
codex/ci-required-cancellation-gate
fix/main-format-baseline-20261013
fix/setup-preserve-entry-fields
cursor/restore-main-checks-1ffc
fix/ghidra-bridge-hashmap-import
codex/shared-ci-jadx-json-default-20261013
codex/shared-ci-ghidra-test-groups-20261013
codex/shared-ci-java-fixture-format-20261013
codex/fix-intrinsic-invocation-boundaries
test/release-preflight-reusable-jobs
test/js-parameter-provenance-expectation
fix/linux-hopper-verification-current
test/2419-ghidra-elf-probe-size
followup/pr-1544-native-ida
codex/fold-pr-2230-contribution
docs/readme-responsible-use
fix/issue-2064
fix/issue-2050
fix/issue-2053
fix/issue-1968
fix/issue-1945
experiment/codemode-bridge-20261011
fix/tool-effects-negative-wording-1904-20261012
fix/1842-electron-interop-provenance
feat/recover-javascript-multi-input
refactor/website-i18n-prep
fix/nativeaot-pe-virtual-extent
fix/browser-verifier-ci-hang
fix/pe-resources-input-selection
docs/beginner-friendly-guides
docs/beginner-setup-guide
fix/javascript-analysis-worker-1563
fix/1649-ghidra-duplicate-names
fix/javascript-export-keys-integration-1369
test/maintenance-boundary-contracts-20261011
review/javascript-export-keys-1369
review/process-live-progress-1483
test/javascript-1495-growth-ratio
fix/ci-escape-timing
docs/normalized-terminal-geometry-1450
release/6.3.0
archive/release-6.3.0-pr1381
release/6.2.0
fix/electron-preload-dynamic-override
fix/javascript-self-referential-bundler-edges
release/6.1.0
cursor/grok-client-cherry-pick-0b56
feat/signature-posture
feat/dyld-shared-cache
feat/artifact-integrity-policy
feat/pkg-inventory
release/6.0.0
chore/sync-release-5.0.0
release/5.0.0
cursor/cloud-agent-node-787e
fix/javascript-xhr-open-endpoints
review/pr-711-realpath-fallback-wording
cleanup/replay-confinement
codex/test-suite-performance
codex/issue-foundations
codex/address-open-issues-2026-07-20
agent/javascript-vite-rollup-esbuild-adapters
agent/process-cleanup
agent/triage-integration
agent/unknown-commands-registry
feat/mcp-identity-metadata
morlvps/issue_cleanup
codex/installer-redesign
morl/roadmap-investigation-core
morl/roadmap-investigation-docs
release-please--branches--main--components--rea
rea-agents-6.8.0
rea-agents-6.7.0
rea-agents-6.6.0
experiment/atomic-codemode-wait-runtime-20261016
rea-agents-6.5.0
rea-agents-6.4.0
rea-agents-6.3.0
rea-agents-6.2.0
rea-agents-6.1.0
rea-agents-6.0.0
rea-agents-5.0.0
rea-agents-4.1.0
rea-agents-4.0.1
rea-agents-4.0.0
rea-agents-3.2.1
rea-agents-3.2.0
rea-agents-3.1.0
rea-agents-3.0.0
rea-agents-2.7.0
rea-agents-2.6.0
rea-agents-2.5.1
rea-agents-2.5.0
rea-agents-2.4.0
rea-agents-2.3.0
rea-agents-2.2.0
rea-agents-2.1.0
rea-agents-2.0.0
rea-agents-1.7.0
rea-agents-1.6.0
rea-agents-1.5.0
rea-agents-1.4.0
rea-agents-1.3.0
rea-agents-1.2.0
rea-agents-1.1.0
rea-agents-1.0.0
rea-agents-0.5.0
rea-agents-0.4.0
rea-agents-0.3.0
rea-0.2.1
rea-0.2.0
Labels
Clear labels
area: bridge
area: bridge
area: build
area: build
area: build
area: cli
area: domain
area: evidence
area: mcp
area: native
area: providers
area: runtime
area: server
area: setup
area: tests
bug
design
documentation
enhancement
enhancement
enhancement
experiment
help wanted
i18n
invalid
P0: critical
P1: high
P2: medium
P2: medium
P2: medium
P3: low
performance
provider-request
pull-request
Mirrored from GitHub Pull Request
question
roadmap
triage
No labels
area: bridge
area: bridge
area: build
area: build
area: build
area: cli
area: domain
area: evidence
area: mcp
area: native
area: providers
area: runtime
area: server
area: setup
area: tests
bug
design
documentation
enhancement
enhancement
enhancement
experiment
help wanted
i18n
invalid
P0: critical
P1: high
P2: medium
P2: medium
P2: medium
P3: low
performance
provider-request
pull-request
question
roadmap
triage
Milestone
Clear milestone
No items
No milestone
Projects
Clear projects
No items
No project
Assignees
Clear assignees
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#6550
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Originally created by @morluto on GitHub (Oct 31, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/8344
Before submitting
Decision needed
Should REA offer a bounded way to discover programs in a caller-selected Ghidra project before requiring the analyst to select one?
Context and evidence
On REA main
60149282, macOS arm64, and Ghidra 12.1.4, a source-built binary had two root programs in one caller-owned project,program_alphaandprogram_beta. They recorded the same imported executable SHA-256, but had distinct analyst-authored function names and comments.With
REA_GHIDRA_PROJECT_PATHset andREA_GHIDRA_PROGRAM_NAMEunset,open_binarybound the target lazily; the firstlist_documentsfailed with the current actionable configuration error because the binary basename was not a project program. Setting either exact program name in a new REA server worked, andlist_documentsthen returned only the selected name. Decompilation showed the corresponding distinct authored name and comment. This is consistent with the advertised one-program session contract; the issue is candidate discovery before selection, not a claim thatlist_documentsis incorrect.As a separate direct Ghidra control, I copied the project to a disposable location and ran headless
-process '*' -noanalysis -readOnlywith a script reporting each program's path, recorded executable digest and language. It reported both names and the same digest. The original project and binary content digests were unchanged; the temporary project copy's bookkeeping files did change. The target was not executed. Ghidra's headless documentation documents wildcard processing; this experiment is not evidence that processing every program is cheap or a suitable REA implementation.Nested selection integration control
On macOS arm64, an uncommitted temporary merge of #3817
f8abf28cwith main46c13b998resolved two conflicts locally; this was not the contributor’s unmodified PR head. With Ghidra 12.1.4, an owned project containingworkflowunder/group, and explicit folder and program settings, real REA stdio MCP in compact and full profiles opened the target, listed three procedures and returned ananalyze_functionsummary forevaluate. The analysis profile committed the exact/groupfolder and selected program. The target SHA-256ffad1b70c6a5a7ef3f7c3f59126be516520210777d949b35e97c9067f272aa2dand the eight regular files in the project.reptree were unchanged; both sessions closed. The two focused project/launcher suites passed 80 tests with 3 skipped. This demonstrates explicit nested selection in that local merge, not discovery of candidates, verification of every project file or a clean #3817 run.Same digest in two folders
A separate owned project imported the same unexecuted Mach-O as
workflowunder both/groupand/other. Headless Ghidra scripts assigned different function names (audit_alphaandaudit_beta) to the same function in those saved programs. With the same temporary conflict-resolved #3817/main merge described above, also containing the unrelated missing-program guidance prototype from #8579, real REA/Ghidra 12.1.4 compact and full MCP sessions selected each folder explicitly and returned its corresponding name. The selected project path and program name were otherwise identical; the analysis profile digests differed by folder. The target and all regular files in the owned.gpr/.repproject tree retained their hashes across REA use, and each session closed.This is one controlled project, not proof of candidate-discovery behavior or automatic selection. It demonstrates why equal imported executable digests cannot choose between analysis programs: their saved names and other analysis may differ. Discovery should preserve each folder/name identity and leave the choice to the caller.
Cross-folder snapshot control
Using the same two-folder project and temporary integration checkout, real compact and full MCP controls saved a snapshot after listing the
/groupprogram. A fresh server configured for/otherand the same target rejected that snapshot withevidence_integrity_mismatchand a structuredbinding_mismatch.profile_digest; the rejected open left no target selected. Opening/otherwithout the snapshot then returnedaudit_beta. Target and.gpr/.repfile hashes were unchanged. This verifies one cross-folder replay boundary; it does not establish a general cache or project-discovery policy.A subsequent control started with
/otheralready open and itsaudit_betaanalysis available. In both MCP profiles, trying to reopen the same target with the/groupsnapshot returned the same structured mismatch; the active analysis run identity and listed procedures remained unchanged, andclose_binarysucceeded. Target and project file hashes were unchanged. This tests failed-open session preservation for this fixture and temporary integration only.Recursive Ghidra discovery control
On the owned two-folder project above, direct Ghidra 12.1.4 headless on a disposable project copy with
-process "*" -recursive -noanalysis -readOnlyand a probe script reported/group/workflowand/other/workflow. Both recorded the same imported SHA-256, while the saved function names wereaudit_alphaandaudit_beta. The original target and all 12 regular original project files matched their earlier hashes after the run. This was direct Ghidra, not a REA discovery operation, and the project copy may change internally.The two-row control establishes that recursive processing can enumerate this fixture and retain path identity; it does not establish boundedness, partial-failure coverage, cancellation behavior, cost, or safe behavior for arbitrary project contents. A discovery design should report those limits and avoid silently choosing a same-digest result.
Recursive processing partial-failure control
On a fresh disposable copy of the same two-folder project, a direct Ghidra 12.1.4 headless probe intentionally threw an exception while processing
/group/workflowand emitted a row for/other/workflow. Ghidra loggedREPORT SCRIPT ERROR, continued to the second program, and exited with code 0. The original project and target hashes remained unchanged. This is an injected script failure, not a naturally corrupted project and not a REA discovery run.A future discovery workflow cannot treat exit code zero or one emitted row as proof that every candidate was inspected. It needs per-item completion/failure evidence and truthful aggregate coverage, as well as cancellation and output bounds. Do not silently label the failed candidate absent or mismatch.
Expected interaction with targets
Read existing caller-selected project and binary files; launch the local Ghidra provider and run an REA-owned inspection script against an owned copy if that approach is chosen. Do not execute the target binary or modify the caller's project. Exact effects and authority need design review.
Boundary details (optional)
Keep user choice explicit: equal imported digests do not establish identical analysis or select a program. Preserve exact project path, project fingerprint, program path/name, known/unknown digest and completeness; never turn an uninspected or failed item into a mismatch. Do not silently fall back to a fresh import or mutate the source project. Current REA selection supports root program names; nested-project paths are a separate scope decision.
Options and tradeoffs
Consider a project-specific discovery operation or a focused setup/doctor handoff before opening a target, versus documenting a safe external Ghidra workflow. Compare bounded metadata discovery with per-program opening, including copy cost, cancellation, unrecognized/non-program entries, identity verification, concurrent edits, pagination and cleanup. Avoid exposing a generic arbitrary-script tool or automatically selecting the first digest match.
Verification available so far
I have a prototype or observed results
Success criteria and open questions
Exercise MCP and CLI with zero, one and multiple candidates; same-digest programs with different annotations; missing/unknown identities; nested and non-program items if supported; cancellation, partial inventory, copy failure and source-project preservation. Make inventory scope and failure/coverage explicit. The direct Ghidra controls include two root programs and, separately, two nested programs on one POSIX host. Nested selection was exercised only in a temporary REA integration; REA candidate discovery was not implemented or tested. Windows and performance remain unverified.
References
Existing-project request: #3751. Missing-program recovery already fixed: #8216 / #8222. Nested program selection PR: #3817. Digest mismatch handling: #8169. The checked
list_documentscontract is insrc/contracts/officialToolContracts.tsand intentionally describes one Ghidra Program.@gensokyo-dev @kaoru0822-kitauji @N0zoM1z0
Host readiness versus selected-program validity
On #8820 head
a4b7dfcd1, macOS arm64 and Ghidra 12.1.4, I selected an owned expanded project containing a root program namedprogram, but setREA_GHIDRA_PROGRAM_NAME=not_a_program. Scopedrea doctor --provider ghidra --target <target>reported healthy; a subsequentrea decompilerejected the selection withconfiguration_invalid, namedREA_GHIDRA_PROGRAM_NAME, and advised selecting an exact root program without modifying the project or falling back to a fresh import. Selectingprograminstead returned the expected function. The target, original packed input and every regular file in the expanded project kept their hashes during REA use; none was executed.This is consistent with the documented doctor contract: its provider checks are host-level and do not attest that a particular project program exists or matches the target. It is not evidence that the installation check is wrong. It does show that project discovery or readiness UX must keep host readiness, selected project shape, exact program selection, target identity and successful analysis separate; finding a project file or receiving a healthy doctor result does not establish the later facts. No project-discovery operation was implemented in this control.
Single renamed program with spaces
On #8820 head
f3c0336c5, I used Ghidra 12.1.4 headless to import a copy of an owned Mach-O namedrenamed sampleinto an expanded project whose directory and project name contained spaces. The admitted REA target was a separate same-digest file namedarm64. In compact/full MCP, leavingREA_GHIDRA_PROGRAM_NAMEunset admitted the target lazily, then the first pseudocode query returnedconfiguration_invalid, explaining that the default namearm64was not a root program and asking for the exact name. Restarting withREA_GHIDRA_PROGRAM_NAME=renamed samplesuccessfully decompiled the expected function in both profiles. The target SHA-2560e0ecf82c96e4918ee2ca1287dc25513d8fd13d5ab7fa5b98db00ad342f03930and regular original project files remained unchanged. Ghidra's import log supplied the name in this control; REA itself did not enumerate it. This verifies exact-name selection and preservation for one program and host, not candidate discovery or automatic same-digest selection.@morluto commented on GitHub (Oct 31, 2026):
A second Ghidra 12.1.4 control narrows the design space. A headless script running against one already-known root program traversed
state.getProject().getProjectData().getRootFolder()usingDomainFolder.getFiles()/getFolders()and inspected eachDomainFile's path, content type and saved metadata. Without opening each candidate as aProgram, it reported both/workflowand/group/workflowin the owned nested-project fixture. Both happened to exposeExecutable SHA256in saved metadata. Ghidra's own batch-project example shows the folder traversal and distinguishes content types and links.In a separate owned project whose saved program's executable SHA-256 had previously been cleared, the same metadata traversal omitted the SHA-256 key; the script did not treat this as a mismatch. Both runs used disposable copies and
-noanalysis -readOnly; original project and binary hashes remained unchanged. The first copied project's bookkeeping files changed, while the second copy's did not. Neither target ran.This is not yet a complete solution for an unknown project: the scripts were launched using an exact known root program, so an empty project or a project with no known eligible root program needs a separate bootstrap strategy. Saved metadata is a discovery hint, not proof that the current program database is unmodified or that analysis is complete; exact candidate selection and the existing live imported-digest check still matter. This tests two owned projects on one host, not stale metadata, links, unreadable entries, pagination, cancellation or cost.
@morluto commented on GitHub (Oct 31, 2026):
The bootstrap limitation in my previous comment is not inherent to Ghidra's project API. I built a separate, disposable Java
GhidraLaunchableagainst the installed Ghidra 12.1.4: initialize a headless Ghidra application, callGhidraProject.openProject, traverse its rootDomainFolder, read eachDomainFile's content type/path/saved metadata, and close the project. The helper did not select or open aProgram.On copied fixtures it returned
/workflowand/group/workflowwith the same recorded SHA-256; a project whose recorded digest was cleared returned that field as absent; a newly created disposable empty project returned zero items successfully. Original source-project file hashes stayed unchanged. Opening the nested disposable copy changed its bookkeeping files; the unknown-digest copy and empty project did not change in these runs. No target was executed. An initial launch attempt with an incomplete classpath failed before opening the projects; adding the installed Ghidra jars allowed the checks above.This is a feasibility experiment, not an REA implementation or measured improvement: the helper currently uses all installed Ghidra jars, has no bounds, structured error/paging contract, cancellation controls, ownership-aware cleanup, concurrent-edit checks, or supported cross-platform packaging.
openProjecton an owned copy is not proof of a read-only storage operation, and saved metadata remains a hint rather than a verified selected-program digest. These gaps need to be addressed before considering integration.@morluto commented on GitHub (Oct 31, 2026):
I exercised link entries in a disposable copy of the nested project. Using Ghidra's project API, I created an internal folder link
group_aliasto/groupand an internal program linkprogram_aliasto/workflow. A project-root metadata traversal then reported/workflowasProgram,/program_aliasasProgramLink, and/group_aliasasFolderLink;getFolders()returned the ordinary/group, not the folder-link entry in this fixture. The original project remained unchanged.On current REA main
60149282/ Ghidra 12.1.4, selectingworkflowworked. Selecting either link name bound the target lazily, thenlist_documentsreturned the actionableconfiguration_invalidmissing-root-program outcome. DirectanalyzeHeadless -processon another disposable copy showed the same result:workflowprocessed successfully, while each link name was reported as no matching project program. The linked source project and target hashes remained unchanged during these queries. This is consistent with headless processing, not a claim that REA broke a supported link selection.For discovery, this means an entry list should preserve actual content type/link status and distinguish a visible link from a selectable program; blindly presenting every
DomainFileas a selectable program would give incorrect guidance. Whether and how REA should resolve internal links is a separate design question. No external/network links, broken links, link cycles, other Ghidra versions, or nested selection implementation were tested.@morluto commented on GitHub (Oct 31, 2026):
A concrete control for the metadata caveat: the standalone project inventory reported the saved
Executable SHA256of a previously edited owned program as equal to its original binary (a6bd938bf4ae8031a33d270ca7698ee1ced5b78347a9c4fa3750b0e57f6b126c). The program's instruction bytes had been deliberately changed in the project database before this check; the original binary was unchanged.On current REA main
60149282with real Ghidra 12.1.4, the caller-selected project and a fresh import both mapped the tested entry to file offset 728. The saved project returned40058052c0035fd6and pseudocode returning0x2a; the fresh import and independent original-file read returned00008052c0035fd6and pseudocode returning0. The selected-project Evidence included the existing prior-edits caveat.inspect_native_load_image's source-file view showed different original/modified source digests for the saved project and equal digests for this fresh control; both views survived export and target-free import unchanged. Original project and target hashes remained unchanged during the REA sessions.This illustrates why a discovery result must describe a saved import digest as an identity hint, not unchanged program bytes or equivalent analysis. Digest inequality alone also does not establish an analyst edit: a separate earlier fresh-import relocation control is documented in #8232. One intentionally edited fixture, not a prevalence or runtime-execution claim.
@morluto commented on GitHub (Oct 31, 2026):
A related selection/handoff control using a local conflict resolution of nested-folder PR #3817 onto current main: the selected Ghidra project contained root and nested programs with the same imported artifact and distinct analyst names/comments. Root and a nested folder with spaces both opened through real REA/Ghidra; the source project remained unchanged.
Because selection is still configured on process startup, I exported the root dossier Evidence, started a separately configured nested MCP server, imported that bundle, and compared the root and nested dossiers. The comparison retained both sources and its observed text/comment differences, while keeping function correspondence ambiguous. A combined bundle imported into a targetless server reproduced the exact comparison and Evidence ID. Separate
address_to_file_offsetand boundedread_bytescalls gave matching mappings/bytes for this constructed pair, with independent Mach-O load-image verification still unsupported; these observations do not establish arbitrary project equivalence.This is a viable explicit recovery path for comparing saved programs, but candidate discovery alone would not let an agent change the selected program inside the existing server. It does not establish whether per-open selection belongs in REA or justify treating same-digest candidates as interchangeable. More details and the local merge limitations are on PR #3817.
@morluto commented on GitHub (Oct 31, 2026):
Broken-link control on macOS arm64 with Ghidra 12.1.4 and REA main
d54a4661: in a disposable copy of the earlier project, I created an internalProgramLinkto a nonexistent program and an internalFolderLinkto a nonexistent folder. Root metadata enumeration retained both as link entries with no recorded executable SHA-256. For these local links, Ghidra'sLinkFileInfo.getLinkStatusreportedBROKENand identified the missing target; the existing valid program and folder links reportedINTERNAL, with their targets present. The check skipped external links and did not open a candidateProgram.Through public REA MCP, selecting the actual root program succeeded. Selecting the valid program link, broken program link or broken folder link each produced
configuration_invalidonlist_documents;close_binarysucceeded in each session. REA left the selected source project and binary byte-identical. This is consistent with the current root-program selection contract, not an assertion that REA promises to resolve links.For any future discovery UX, retain entry type and link-resolution status as separate facts:
ProgramLinkalone does not establish a selectable program or an existing target. If status is not checked, leave it unknown. This probe does not establish behavior or side effects for external/network links, cycles, permission failures, concurrent edits or other Ghidra versions; it is not an argument to follow external links automatically.@morluto commented on GitHub (Nov 1, 2026):
Current-main nested-selection control
On clean main
6e9ee97a, macOS arm64 and Ghidra 12.1.4, I retested the owned project from #3817. DirectanalyzeHeadlesson a disposable copy selected/workflowand/group/workflowseparately using the project folder in its project argument. Both programs recorded the same imported binary SHA-256, while their authored function names differ. This agrees with Ghidra's headless folder syntax.With current REA and
REA_GHIDRA_PROGRAM_NAME=workflow, the session selected the root program:list_documentsreturnedworkflow, root-authored pseudocode succeeded, and the nested-only name was unavailable. SettingREA_GHIDRA_PROGRAM_NAME=group/workflowprevented MCP startup: SDK connect observed the server close and stderr explicitly said to select one root program name without paths.I also checked
REA_GHIDRA_PROGRAM_FOLDER=/group, the configuration name used by the still-open, conflicting #3817 branch. That setting is not supported by current main: it was ignored, and REA again selected the root program with the same analysis-profile digest and root-authored pseudocode. This is a control for someone trying the unmerged branch's configuration, not a claim that main promises folder support. If nested selection is implemented, preserve the folder as part of the selected program identity/profile and fail explicitly on invalid selection; the source binary digest alone cannot distinguish these two saved analyses.The source project regular-file hashes and binary SHA-256 were unchanged during the REA calls; direct Ghidra operated on a disposable copy. Successful MCP responses were checked against fetched compact schemas. This is one constructed project on one host, not a claim about other folder forms, existing user configurations or agent behavior.
A follow-up left
REA_GHIDRA_PROGRAM_NAMEunset. Because the target basename wasworkflow, REA again selected the root program and produced the same profile digest and root-only pseudocode; it did not enumerate the nested same-named program. This matches the current root-name default, but reinforces that the reported selected name is not a project-wide uniqueness check. Original project and target hashes stayed unchanged.@morluto commented on GitHub (Nov 2, 2026):
Direct Ghidra no-match control
On a disposable copy of the same owned two-folder project, Ghidra 12.1.4 headless on macOS arm64 with
-process '*' -recursive -noanalysis -readOnlyran the identity script on both programs and exited 0. Repeating with a nonmatching exact name, then a nonmatching wildcard, returned exit 1, no script rows, andRequested project program file(s) not foundin both cases. All 11 regular files in the source copy retained their hashes; only a fresh disposable project copy was processed. The target was not run.For this fixture, an empty script output from a selective
-processinvocation must therefore be interpreted together with Ghidra's reported selection failure, not silently promoted to a successful empty inventory. Together with the earlier injected per-program failure that exited 0, this shows that neither nonzero nor zero process exit alone describes complete candidate coverage. This is direct Ghidra behavior for one project and two selectors, not an implemented REA discovery operation or a claim about empty projects and other error modes.@morluto commented on GitHub (Nov 2, 2026):
Empty-project control
On macOS arm64 with Ghidra 12.1.4, I created a new disposable local project by importing an owned fixture with
-noanalysisand a headless post-script requestingCONTINUE_THEN_DELETE. The import exited 0 and Ghidra logged that it discarded the only imported program. Processing that project with-process '*' -recursive -noanalysis -readOnlythen exited 1 withRequested project program file(s) not found: *and produced no identity-script rows. The source fixture was not executed and its SHA-256 remained9fc02faea0c3723a094c209adbe62a279011df4d508298625bd766fa20ed7c46.Thus, in this one verified empty project, headless reports an empty wildcard selection through the same broad failure form seen for a nonmatching selector in a populated project. A prospective REA inventory needs independent evidence about the selected project and enumeration scope before converting that result into a complete empty list; it should preserve genuine project/processing errors. This is direct Ghidra behavior only, not a claim that all projects or Ghidra versions behave alike.
@morluto commented on GitHub (Nov 2, 2026):
Program-independent project inventory control
Ghidra 12.1.4's bundled headless documentation describes running program-independent scripts without
-importor-process. I tested that route on disposable copies of both owned projects from the controls above, using-readOnlyand a pre-script that recursively visitsstate.getProject().getProjectData().getRootFolder(),DomainFolder.getFiles()andgetFolders(). It does not open a program or invoke analysis. The populated project returned/group/workflowand/other/workflowasProgramentries; theirDomainFile.getMetadata()maps each reported the same imported SHA-256. The independently constructed empty project returned an explicit completion marker with count zero. Both invocations exited 0; source-project regular-file hashes remained unchanged.This offers a more direct candidate-discovery direction than processing every program, and can represent an observed empty project in these controls. Metadata is a recorded project observation, not proof of current target identity, project immutability or equivalent saved analysis: the two same-digest programs have different annotations. The experiment does not establish bounds, ordering, lazy metadata cost, non-program handling, cancellation, partial failures, concurrent changes, cross-version behavior, or whether
-readOnlyavoids changes to the disposable copy's bookkeeping. Any REA implementation still needs explicit caller selection, preservation of unknown metadata and per-scope completion, source-project ownership, and the applicable authority decision. This is a direct Ghidra experiment, not an REA capability.@morluto commented on GitHub (Nov 2, 2026):
Bounded project inventory and link scope
A direct Ghidra 12.1.4 program-independent script on a disposable copy of the owned two-program project emitted one entry at a caller-selected limit of one and explicitly reported
has_more=trueafter encountering the second. It exited 0 and the 11 regular files of the source project copy retained their hashes. This tests one output-limit control, not a resumable cursor, performance, or cancellation.The bundled Ghidra 12.1.4
DomainFolderAPI documents thatgetFiles()andgetFolders()return arrays for an entire folder and may use cached data without forcing a full refresh. Therefore, bounding emitted rows alone does not prove bounded per-folder materialization or fresh live inventory. The same bundled source describes linked-folder/file semantics;ProjectDataUtils.descendantFiles(folder)ignores link files by default, whereas its filtered overload can follow selected links. A design should state its link scope and avoid treating ignored or inaccessible linked content as inspected; do not follow external links merely to make an inventory appear complete. These are source-derived API observations, not measurements or a test of a linked project. Limits, partial completion, ordering and project identity across continuation still need a concrete design and runtime checks.@morluto commented on GitHub (Nov 2, 2026):
Linked-folder control
On a disposable copy of the owned two-program project, a program-independent Ghidra 12.1.4 script created an internal folder link
/audit_linkpointing to/group. A separate headless-readOnlyscript saw/audit_linkin the root'sgetFiles()asFolderLink, withisLink=trueandisFolderLink=true; rootgetFolders()returned/groupand/other, not the link.ProjectDataUtils.descendantFiles(root)returned the two original program paths and omitted the link. Both scripts completed, and the original project's regular-file hashes remained unchanged. Creation intentionally modified only the disposable copy.An inventory using the default descendant iterator can therefore omit even the existence of a link in this controlled case; a non-following inventory that needs to disclose skipped links must separately observe them. This is one internal link on macOS, not an external/broken-link, permission, cycle, or network test. It does not imply that link targets should be followed or counted as candidates.
@morluto commented on GitHub (Nov 2, 2026):
Read-only inventory copy check
For the program-independent project inventory control, I repeated Ghidra 12.1.4
-readOnlyagainst a fresh disposable copy of the two-program project and hashed every regular file in both the copied project and its source before and after. The script completed and reported both candidates; the copy had the same 11 regular-file paths and SHA-256 values afterward, with none added or removed. The source project likewise remained unchanged. This narrows the earlier uncertainty about copy bookkeeping for this fixture and one invocation; it does not check timestamps, directory metadata, transient writes, other project states or platforms, and does not establish that using a caller-owned project directly is safe.