REA on REA: receipt of one self-analysis run
What this page is. This page records one run in which REA analyzed its own installed package. It contains the prompt word for word, each step with the tool that did it, the saved prediction, the raw test output, and the final answer word for word. It also lists the tools from outside REA, the places where the run went against the prompt rules, and the risks that a reader can miss.
Result in one line. REA gave the correct file-and-line structure. It did not keep the string values (rejection codes, "auto", "ambiguous"). The prediction got 3 of 8 check results wrong. The self-grade marked items 1–4 as partly correct.
1. The prompt (verbatim)
The user sent this text as one pasted block. The block was the whole user message, so it was treated as the user's instruction.
Use REA to find out how REA itself works. Analyze the installed package at
<APPDATA>\npm\node_modules\rea-agents (MIT licence).
Path rules:
- <APPDATA> means the APPDATA environment variable ($env:APPDATA in PowerShell).
REA rejects relative paths, so expand every path to an absolute path before you call a REA tool.
- The answer key is the folder .\rea-main in the current working directory.
- The rea CLI is <APPDATA>\npm\rea.cmd. It is not on PATH; call it by its full path.
Rules:
- Use REA tools only. Do NOT read .\rea-main until the final grading step.
- Do NOT read HANDOFF.md in this folder. It contains answers.
- Keep queries narrow. Do not dump the whole module graph or batch-decompile everything.
If a result is saved to tool-results/*.txt, read it with node, not in full.
Question: When open_binary is called on a native .exe, how does REA choose the analysis
provider, and how does it decide a provider is unavailable?
Report, each with file + line evidence from REA:
1. The list of candidate providers and the order in which REA tries them.
2. What REA does with provider_id omitted ("auto") versus provider_id: "ghidra".
3. Every check REA runs on Ghidra before accepting it, and the rejection code for each.
4. Whether REA falls back to another provider when the chosen one fails.
5. Anything REA could not resolve — list as unknowns, do not guess.
Then make ONE falsifiable prediction: if GHIDRA_INSTALL_DIR points to an existing empty
folder, which Ghidra checks pass, which fail, and with which codes? Write the prediction down
BEFORE you test it. Then test it: make an empty folder in the scratchpad, run
<APPDATA>\npm\rea.cmd doctor --client claude_code --provider ghidra --json
with GHIDRA_INSTALL_DIR set to that folder for that one command only, and compare.
Finally, grade yourself against the answer key: now read .\rea-main and mark each of items
1–4 as confirmed, wrong, or partly correct. Quote the source line for each.
2. Skills and tools used
REA marks a REA tool. outside REA marks a tool from the harness (Claude Code, its hooks, or the model).
| Tool or skill | Origin | Calls | What it did in this run |
|---|---|---|---|
analyze_javascript_application | REA | 5 | Built a static application graph and semantic graph for 5 folders under dist/: application/binary, ghidra, contracts, composition, config. It never ran the code. |
trace_application_feature | REA | 1 | Seed string "ghidra". Result: no-match. The tool states that this is not evidence of absence. |
trace_javascript_semantics | REA | 1 | Literal seed "ghidra", forward influence. Result: 0 seed matches, status partial. |
inspect_analysis_view | REA | 1 | Summary of application/binary: 16 files, 9,992 AST nodes, 220 findings, 2,624 semantic unknowns. |
export_evidence_bundle | REA | 1 | Wrote the session evidence to rea-bundle.json: 68,486,104 bytes, 6 records. |
rea.cmd doctor --client claude_code --provider ghidra --json | REA CLI | 1 | The prediction test. It runs REA itself, so this evidence is runtime evidence, not static evidence. |
Skill reverse-engineer-anything | REA skill, loaded by the harness | 1 | Gave the routing rule: an extracted JavaScript tree goes to analyze_javascript_application. It also gave the rule to use export_evidence_bundle when a response exceeds the MCP transport limit. |
ToolSearch | outside REA | 2 | Loaded the REA tool schemas. REA tools are deferred in this harness. |
PowerShell | outside REA | 3 | (1) Printed $env:APPDATA and listed the package top level. (2) Counted dist/ (1,012 files, 5,402,767 bytes) and listed provider-related file names. (3) Made the empty folder, set GHIDRA_INSTALL_DIR, ran doctor, and restored the variable. |
Bash + node | outside REA | ~16 | Read REA's own output files (bundle, tool-results/*.txt, doctor.json) with the scripts q.js and g.js (see section 10). The prompt allowed node for this. |
Write | outside REA | 1 (run) + 3 (this page) | Saved prediction.md before the test. Later, saved the source files for this page. |
Glob, Grep, Read | outside REA | ~14 | Used only in the grading step, on rea-main. They were not used on rea-main before the grading step. |
advisor | outside REA (a stronger reviewer model) | 1 | It received the full transcript after the grading reads. It made the grades stricter: "partly correct" where REA showed structure but not values. It also asked for the target-support gate bodies to be read. |
| Session hooks | outside REA | automatic | The hooks added the ponytail (minimal-change) rules, the superpowers skill rules, a skill cheat sheet, and a context-cost warning. They change how the agent works and writes. They do not change REA. |
Not used: open_binary, batch_decompile, close_binary, and all browser and runtime-capture tools. No native session was opened.
3. Step-by-step process
- Route the target. The skill
reverse-engineer-anythingwas loaded. The target is an extracted JavaScript tree, so the first tool isanalyze_javascript_application. - Load the tool schemas.
ToolSearchloaded 4 REA tools. - Expand the paths. PowerShell gave
APPDATA = C:\Users\<user>\AppData\Roaming. A file-name listing found the provider modules underdist/application/binary,dist/ghidraanddist/contracts. - Analyze
dist/application/binary. The analysis completed, but the response was at least 10,535,570 bytes, which is more than the MCP budget of 10,484,736 bytes. REA kept the result as evidenceev_bae61db2…and returnedresource_constraint. - Two trace attempts on "ghidra". Neither matched. The semantic graph keeps only some string literals (in the
dist/contractsgraph, 655 of 12,096 nodes are literal nodes). - Summary view.
inspect_analysis_viewconfirmed 16 files, coveragecompletefor the application graph andunknownfor the semantic graph. - Analyze
dist/ghidraanddist/contracts. Both were over the MCP budget. Both were kept as evidence. - Export.
export_evidence_bundlewrote all 6 records torea-bundle.json(68,486,104 bytes). - Query with node.
q.jssearched node labels.g.jsgrouped the node labels by source line, so that a function's control flow could be read line by line. This gave the registry flow (AnalysisProviderRegistry.js:61-220), the Ghidra installation checks (GhidraInstallation.js:177-211), and the 12 rejection codes (providerSelection.js:16). - Analyze
dist/compositionanddist/config. These responses (2,207,657 and 1,537,343 characters) were under the MCP budget but over the harness display limit. The harness saved them totool-results/*.txt. Node read them. This step foundnew AnalysisProviderRegistry([hopper, ghidra, ida], config.analysisProvider)atcomposition/binary.js:20andREA_ANALYSIS_PROVIDERatparseConfig.js:43. - Save the prediction.
prediction.mdwas written at 20:50:51.07 (local time, UTC−7). - Run the test. PowerShell made the empty folder, checked that it held 0 items, and set
GHIDRA_INSTALL_DIRfor one command. Doctor wrotedoctor.jsonat 20:51:09.23, 18 seconds after the prediction. Then the variable was removed again, because it was empty before the run. Exit code 1 means that doctor reports "unhealthy". - Grade. Glob found the answer-key files. Read and Grep quoted
AnalysisProviderRegistry.ts,providerSelection.ts,composition/binary.ts,GhidraInstallation.ts,GhidraDoctor.ts,GhidraInstallationPolicy.ts,GhidraProvider.ts,config/*.tsandSessionProviderRouter.ts. - Review. The advisor reviewed the transcript. Then
GhidraProvider.ts:122-171and:247-284were read to complete the target-support gates. - Report. The final response (section 7) was written.
4. Rule compliance audit
| Prompt rule | Result | Detail |
|---|---|---|
| Use REA tools only | Partly kept | The analysis itself used REA tools only. But PowerShell listed the folder and file names of the package before the analysis. That is a file inventory, not file content, but it is not a REA tool. Node read REA output, which the prompt allowed. |
| Expand every path to an absolute path | Kept | Every REA call used C:\Users\<user>\AppData\Roaming\npm\node_modules\rea-agents\dist\…. |
Do not read rea-main until grading | Kept | The first access to rea-main was the grading Glob, after the doctor run. |
Do not read HANDOFF.md | Kept | It was not opened. |
| Keep queries narrow | Partly kept | Only 5 folders were analyzed, not the whole package, and no batch decompile was done. But each folder result was 1.5 to 10.5 MB. The node scripts printed only selected lines. |
Read tool-results/*.txt with node, not in full | Kept | Both files were parsed by node. Only filtered lines were printed. |
| Write the prediction before the test | Kept | The file timestamps prove the order: 20:50:51 before 20:51:09. |
Set GHIDRA_INSTALL_DIR for one command only | Kept | The variable was set in a try/finally block and removed in finally. Output: after: []. |
| Items 1–4 from REA before the key | Gap | The REA-only conclusions were not saved to a file before the key was read. Only the prediction was saved first. The final response says this. |
5. Prediction receipt
File prediction.md, modified 2026-10-10 20:50:51.066 −0700. The test output doctor.json was modified at 20:51:09.232 −0700.
# Prediction (written before the doctor run)
Setup: GHIDRA_INSTALL_DIR = absolute path to an existing EMPTY folder. Host win32 / x64.
Basis: REA semantic graph of dist/ghidra/GhidraInstallation.js, installationChecks() lines 177-211.
Checks in order, and predicted result:
1. configuration (installDir absolute, :178/:217-239) -> PASS
2. host platform in NATIVE_PLATFORMS (:181-186) -> PASS (win32 -> win_x86_64)
3. host architecture (:188-193) -> PASS (x64)
4. application.properties / version (:195-202, :309-318) -> FAIL, code version_unresolved
5. analyzeHeadless executable (:204-207, :384-391) -> FAIL, code executable_missing
6. native decompiler decompile.exe (:210, :293-306) -> FAIL, code executable_missing
7. Java (:211, :321-350) -> FAIL, code version_unresolved
(Java requirement comes from application.properties, which is absent)
Provider-level: unavailable. Code = FIRST failed check (:94 find, :112 code) = version_unresolved.
All checks are evaluated (list, no short-circuit), so doctor lists all 7.
Low confidence on the exact codes: the static graph does not keep the string codes inside
installationCheck({...}) object literals; only the candidate set in contracts/providerSelection.js:16.
6. Test output (raw excerpt)
This is the Ghidra part of doctor.json (the full file is 103,289 bytes). Only the fields healthy, scope, providerInspections and the CLI version were kept. Nothing inside them was changed, except one edit: the Windows user name in each path is replaced with <user>, and the session ID with <session-id>.
{
"healthy": false,
"scope": {
"mode": "explicit",
"clients": [
"claude_code"
],
"providers": [
"ghidra"
],
"skill": false,
"target": null
},
"providerInspections": [
{
"id": "ghidra",
"configured": true,
"available": false,
"providerVersion": null,
"registrationEnvironment": {},
"checks": [
{
"name": "configuration",
"ok": true,
"code": null,
"detail": "C:\\Users\\<user>\\AppData\\Local\\Temp\\claude\\C--Users-<user>-Downloads-REA\\<session-id>\\scratchpad\\empty-ghidra",
"remediation": null,
"classification": "config_drift"
},
{
"name": "platform",
"ok": true,
"code": null,
"detail": "win32",
"remediation": null,
"classification": "unsupported_host"
},
{
"name": "architecture",
"ok": true,
"code": null,
"detail": "x64",
"remediation": null,
"classification": "unsupported_host"
},
{
"name": "installation",
"ok": false,
"code": "executable_missing",
"detail": "C:\\Users\\<user>\\AppData\\Local\\Temp\\claude\\C--Users-<user>-Downloads-REA\\<session-id>\\scratchpad\\empty-ghidra\\Ghidra\\application.properties",
"remediation": "Point GHIDRA_INSTALL_DIR at the root of an extracted official Ghidra release.",
"classification": "config_drift"
},
{
"name": "java",
"ok": true,
"code": null,
"detail": "23.0.2; 64-bit; JDK; C:\\Program Files\\Java\\jdk-23; accepted a 64-bit full JDK 21 or newer",
"remediation": null,
"classification": "config_drift"
}
]
},
{
"id": "ida",
"configured": false,
"available": false,
"providerVersion": null,
"registrationEnvironment": {},
"checks": [
{
"name": "registration",
"ok": false,
"code": "not_configured",
"detail": "IDA MCP registration is not configured.",
"remediation": "Set REA_IDA_MCP_CONFIG to your upstream IDA MCP registration JSON; see docs/ida-provider.md.",
"classification": "missing_analysis_engine"
}
]
}
],
"rea_version": "6.3.0"
}
7. Final response (verbatim)
Figure 4 shows the selection mechanism that the response describes, as the answer key gives it. The response itself follows the figure and is not changed.
open_binary selects a provider, as the answer key gives it. With an explicit id, the 4 gates run in series, and a failure at any gate makes open_binary fail. REA does not try another provider. With "auto", REA checks every provider at the same time, and the sort order does not give priority. Zero usable providers lets the session open with no deep provider, one usable provider is bound, and two or more is an ambiguous error. One case is not drawn: if REA_ANALYSIS_PROVIDER names a provider and every provider rejects the target, the session opens with no provider (AnalysisProviderRegistry.ts:172-184). The amber box is the conclusion I drew from REA before I read the key. My REA reading also said "error" for zero usable providers, which was wrong.This is the final message of the run, character for character, in its original Markdown source.
# How REA chooses a provider for a native .exe: REA-only findings, a Ghidra prediction test, and a self-grade
REA could answer most of the structure but not all of it. The empty-folder test showed that 3 of my 8 predicted check results were wrong. The answer key showed that I misread how automatic ("auto") selection works.
**Method.** I ran `analyze_javascript_application` on 5 folders of the installed `rea-agents` 6.3.0 package: `dist/application/binary`, `dist/ghidra`, `dist/contracts`, `dist/composition` and `dist/config`. Three of the 5 results were larger than the 10 MB MCP response limit. I saved them with `export_evidence_bundle`. The other 2 results were saved to `tool-results/*.txt`. I read all of them with node scripts that group the graph nodes by file and line.
**Process gap.** I did not write down items 1–4 before I read `rea-main`. Only the prediction was saved first (`scratchpad/prediction.md`). Below, "REA said" gives only `dist/*.js:N` evidence and what I concluded from it at that time. Lines from the answer key use `src/*.ts:N`.
## Findings from REA only
**1. Candidates and order.**
- `composition/binary.js:20` creates `new AnalysisProviderRegistry([hopper, ghidra, ida], config.analysisProvider)`. I got this from the argument column positions 62, 70 and 78.
- `AnalysisProviderRegistry.js:40` sorts the providers by `identity.id.localeCompare`. The result is alphabetical: ghidra, hopper, ida.
- REA did not show the ID strings. "hopper" and "ida" are an inference.
**2. Omitted provider_id versus `provider_id: "ghidra"`.**
- `registry.js:72`: the selector is the request, or `defaultSelector` when the request is missing.
- `defaultSelector` comes from `config.analysisProvider`. That value comes from `REA_ANALYSIS_PROVIDER` (`parseConfig.js:43`) through `analysisProviderSelectorSchema.default(…)` (`environment.js:9`). REA did not show the default value.
- `registry.js:86-87` branches to `#selectConcrete` or `#selectAutomatically`.
- The concrete path (`:101-125`) runs these steps in order:
1. It looks up the ID. An unknown ID is an error (`:105`).
2. It checks target support. Failure is an error (`:110`).
3. It checks availability. Failure is an error (`:112`).
4. It evaluates the profile. A rejected profile is an error (`:118`).
- The auto path evaluates all providers in parallel (`Promise.all`, `:129`) and filters the usable ones (`:150`). From `:157-159` I concluded "an error when none are usable, otherwise `usable[0]`". **That conclusion was wrong** (see the grading).
**3. Ghidra checks.** There are three groups.
- **Installation checks** (`GhidraInstallation.js:177-211`): configuration (`:178`), platform (`:181`), architecture (`:188`), properties (`:195`), version (`:201`), analyzeHeadless (`:204`), native decompiler (`:210`), java (`:211`). The first failed check gives the provider rejection code (`:94`, `:112`). `GhidraProvider.js:61` returns `installation.rejection.code`.
- **Windows native-authority gate:** `GhidraProvider.js:42-46`.
- **Target-support gates:** `GhidraProvider.js:66-106`. On Windows they go to `inspectWindowsP0TargetSupport` (`:87`, `:162-177`).
- **Codes:** REA gave only the 12 candidate codes (`providerSelection.js:16`). It did not give which check uses which code. Those strings are inside object literals that the graph does not keep.
**4. Fallback.**
- An explicit provider: there is no fallback. `SessionProviderRouter.js:38-41` returns the selection error as it is.
**5. What REA could not resolve.**
- Which code belongs to which check.
- The literal values `"auto"`, `"ambiguous"`, `"unknown_provider"` and `"invalid_options"`.
- The `.default()` value.
- The provider ID strings.
- The result of `hasWindowsNativeAuthority` on this host.
- 2,624 semantic unknowns. Most are dynamic member keys.
- I did not query `GhidraDoctor.js`. That was a scope miss, not a gap in REA.
## Prediction and test
My saved prediction:
- configuration, platform and architecture pass.
- installation fails with `version_unresolved`.
- headless and decompiler fail with `executable_missing`.
- java fails with `version_unresolved`.
- The provider code is `version_unresolved`, and doctor lists all checks.
The real `rea doctor` output for `providerInspections[ghidra]` was `available:false` with these 5 checks:
| Check | Result | Prediction |
|---|---|---|
| configuration | pass | ✅ |
| platform | pass (win32) | ✅ |
| architecture | pass (x64) | ✅ |
| installation | **fail, `executable_missing`** | ⚠️ the outcome was right, the code was wrong |
| java | **pass** (23.0.2, "JDK 21 or newer") | ❌ |
- **Hidden checks:** version, headless and native_decompiler are not shown. `GhidraDoctor.ts:93-99` shows them only when installation passes. My count of 7 checks was wrong: there are 8, and doctor shows 5.
- **Java:** java passes because `GhidraInstallationPolicy.ts:139-140` falls back to minimum 21 when `application.properties` is absent. REA showed the literal `21` at `GhidraInstallationPolicy.js:8`. I had that evidence and did not connect it.
- **Provider code:** the mechanism was right: the first failed check gives the code (`GhidraInstallation.ts:297-300`, `:318`). The code was wrong because installation (position 4) comes before version (position 5).
- `rea.cmd` exited with 1 because doctor reports "unhealthy". This is not a tool failure. `GHIDRA_INSTALL_DIR` was set for that one command and then removed again.
## Grading against `rea-main`
**1. Candidates and order: partly correct.**
- The candidates are confirmed: `binary.ts:30` `[hopper, ghidra, ida],`.
- The sort is confirmed: `:111-112` `left.identity().id.localeCompare(right.identity().id)`.
- But the order is not a "try in sequence" chain. `:69` says "Registration order never affects selection or caller-visible output", and `:268` evaluates all providers with `Promise.all`.
**2. Auto versus "ghidra": partly correct.**
- These parts are confirmed:
- Omitted means the environment default: `environment.ts:12` `REA_ANALYSIS_PROVIDER: analysisProviderSelectorSchema.default("auto")`. The source is `"environment"`. An explicit ID gives `"request"` (`:189`).
- The concrete path is confirmed. `:234` target_unsupported is checked before `:236` `if (initial.availability.status === "unavailable")`.
- These parts were wrong:
- Auto does not take `usable[0]` from several. `:318-319` `if (usable.length > 1) return err(selectionError("ambiguous", …))`.
- When zero providers are usable, there is no error: `:312-317` returns `binding: null`.
- I missed one special case. When `REA_ANALYSIS_PROVIDER` sets a concrete ID and every candidate rejects the target, the session opens with no provider instead of failing (`:172-184`).
**3. Ghidra checks: partly correct.**
- The structure and order are confirmed. `GhidraInstallation.ts:444-489` holds the 8 checks, and `:297` `const failed = checks.find(…)` picks the first failure.
- I did not get the codes from REA. They are:
| Check | Code | Key lines |
|---|---|---|
| configuration | `not_configured` | `:511`, `:519` |
| platform | `unsupported_host` | `:449` |
| architecture | `unsupported_host` | `:457` |
| installation | `executable_missing` | `:464` |
| version | `unsupported_version` / `version_unresolved` | `:636-638` |
| headless | `executable_missing` | `:477` |
| native_decompiler | `executable_missing` | `:617` |
| java | `version_unresolved` / `runtime_missing` / `unsupported_version` | `:666-671` |
- Two more gate groups:
- **Windows authority:** `GhidraProvider.ts:98` `code: "unsupported_host"`. It applies even when the installation is available.
- **Target support** (`GhidraProvider.ts`):
| Gate | Code | Key lines |
|---|---|---|
| target is not an executable | `target_kind_unsupported` | `:139` |
| Windows: target is not PE | `target_format_unsupported` | `:254` |
| Windows: target is not x86/x86-64 | `architecture_unsupported` | `:261` |
| Windows: wrong executable role | `target_role_unsupported` | `:271` |
| Windows: managed target | `managed_target_unsupported` | `:279` |
| Non-Windows: MIPS, universal Mach-O, or other architecture | `architecture_unsupported` | `:149`, `:156`, `:164` |
**4. Fallback: partly correct.**
- No fallback for an explicit ID is confirmed: `SessionProviderRouter.ts:85-87` `return selected.ok ? ok(…) : selected;`.
- The auto-mode result was wrong. With zero usable providers, the session opens with no deep provider. Deep operations then fail later through `unboundOperationError` (`:199-216`), with code `target_unsupported` or `provider_unavailable`. With more than one usable provider, the result is an `ambiguous` error.
## What this says about REA as a tool
REA's JavaScript graph gives reliable file, line and data-flow structure. It does not keep string literals that are inside object-literal arguments. That is where the rejection codes and the `"auto"` and `"ambiguous"` values are. A question about values needs a runtime probe, such as the doctor run. The structure-only view caused my biggest error: I saw `usable.length` comparisons and read them as "the first one wins".
No native session was opened, so `close_binary` was not needed. The scratchpad folder holds `prediction.md`, `doctor.json`, the evidence bundle (68 MB) and the empty test folder.
8. Unknown unknowns, and what was done
These are risks in this process that a reader can easily not see. Each item gives the risk, the consequence, and what was done or what to do.
8.1 The REA graph keeps structure, not string values
Risk. The semantic graph keeps names, property reads and line numbers. It does not keep string literals inside object-literal arguments, such as installationCheck({ name: "platform", code: "unsupported_host" }). It also does not keep branch conditions.
Consequence. Every rejection code, and the values "auto" and "ambiguous", were not visible. Without the conditions, usable.length > 1 → "ambiguous" was misread as "the first usable one wins".
What was done. The doctor run supplied the codes at runtime, and the grading marked the error. What to do next time: for a question about values, plan a runtime probe from the start. Treat any branch reconstructed from grouped labels as an inference, not an observation.
8.2 A doctor test is not an open_binary test
Risk. The question was about open_binary, but the test used rea doctor. GhidraDoctor.ts:93-99 hides the checks version, headless and native_decompiler when installation fails. The provider path of open_binary puts all 8 checks into its diagnostics (GhidraInstallation.ts:539-544).
Consequence. Doctor showed 5 checks, but the provider ran 8. The test proved what doctor shows. It did not prove what open_binary returns.
What was done. The final response separates "ran internally" from "shown by doctor". Open: no open_binary call was made on a real .exe with the empty folder. That call would show all 8 check results and the provider_unavailable error.
8.3 The installed build and the answer key can differ
Risk. REA analyzed compiled dist/*.js. The key is TypeScript source src/*.ts. The line numbers differ, for example AnalysisProviderRegistry.js:40 against .ts:111.
What was done. Both report version 6.3.0. The structure matched at every point that was compared. Not verified: that rea-main is the exact commit used to build the published 6.3.0 package. A later commit can keep the same version number.
8.4 Tools from outside REA shaped the result
Risk. "REA did this" is only partly true. Node scripts written by the agent turned REA's JSON into readable lines. The scripts drop some labels (the noise pattern in g.js) and cut each line at 300 characters. A reviewer model (the advisor) made the grades stricter. Hooks added working rules.
Consequence. A finding can come from the script filter, not from REA. The final grades are partly the reviewer's judgment.
What was done. Section 2 lists every tool, and section 10 gives the scripts word for word. Anyone can rerun them on the saved bundle.
8.5 The size of the evidence is large, and the transport limits are not the same
Risk. Three folder analyses were over the MCP response budget (10,484,736 bytes). Two were under that budget but over the harness display limit. The two limits behave differently: REA keeps the evidence and returns an error, while the harness saves the text to a file.
Consequence. The evidence bundle on disk is 68 MB. The session used about 213,000 context tokens. Each later turn costs more.
What to do next time: run the CLI form rea.cmd analyze-javascript-application <absolute dir> --json and send the output to a file. That avoids both limits and is still a REA tool. Or analyze smaller folders.
8.6 The doctor run executed code on this computer
Risk. Static analysis never runs the target. The doctor run did run REA. It also started the Java probe (spawnSync at GhidraInstallation.js:396) from JAVA_HOME = C:\Program Files\Java\jdk-23.
What was done. The skill says doctor diagnoses "without changing files". The run wrote output only to the scratchpad. GHIDRA_INSTALL_DIR was restored. The REA installation was not changed.
8.7 Not every check was exercised
Risk. The Windows native-authority gate (GhidraProvider.ts:91-106) runs only when the installation is available. Here the installation failed first.
Open: it is not known whether this computer passes hasWindowsNativeAuthority. A real Ghidra 12.1.x installation is necessary to find out. The verified version is 12.1.4 (GhidraInstallationPolicy.js:2).
8.8 Pre-registration was incomplete
Risk. Only the prediction was saved before the key was read. The REA-only answers to items 1–4 were in the agent's working notes, not in a file.
Consequence. You must trust the agent's statement of what it concluded before it read the key.
What to do next time: make the prompt require a file findings-before-key.md, and check its timestamp against the first read of the key.
8.9 How the pasted prompt was treated
Risk. The harness marks pasted text as content that may carry instructions the user did not write.
What was done. The pasted block was the whole user message, and the task matched the working folder (Downloads\REA, REA tools connected). For that reason it was treated as the user's instruction.
8.10 A "no match" result does not mean that the item is absent
Risk. Both traces on "ghidra" returned no match, but Ghidra is everywhere in the package.
What was done. REA itself states: "No graph entity matched the literal seed; this is not evidence that the feature is absent." The run did not use those results as evidence. It changed to node queries over the exported graph.
9. Evidence on disk, and cleanup
Scratchpad: C:\Users\<user>\AppData\Local\Temp\claude\C--Users-<user>-Downloads-REA\<session-id>\scratchpad
| File | Bytes | Content |
|---|---|---|
rea-bundle.json | 68,486,104 | REA export, 6 records (3 analyses, 1 view, 2 traces) |
comp-bundle.json | 2,207,671 | dist/composition analysis, wrapped for the scripts |
cfg-bundle.json | 1,537,357 | dist/config analysis, wrapped for the scripts |
doctor.json | 103,289 | Full doctor output |
prediction.md | 1,299 | The prediction (section 5) |
q.js, g.js | 814, 1,132 | The query scripts (section 10) |
empty-ghidra\ | 0 | The empty test folder |
REA evidence IDs
| Operation | Target | Evidence ID |
|---|---|---|
| analyze_javascript_application | dist/application/binary | ev_bae61db2c6e478add42d5aac713c908d2188acb82e22ac77c2a4fdc1316b9214 |
| analyze_javascript_application | dist/ghidra | ev_9e1be74755a66998097ad94dddd3b0d622f72360d7e0a6b786dcf653db34c788 |
| analyze_javascript_application | dist/contracts | ev_6420013243e961c205421f0ebc1472d6e4deb7ca70493d51a2a5e8fabea83026 |
| analyze_javascript_application | dist/composition | ev_acfb917ec6a60e4686d737c4dd853818f3f5fb07ac0f53bd0b792ec60dfab9c4 |
| analyze_javascript_application | dist/config | ev_59eafd8508ffa278dd65c81bdeded323ddd20868fd1d933d52960509a307d657 |
| trace_application_feature | seed "ghidra" | ev_180119b24108037d0cff9eb6ba9283f1f686442c7f152f81da72a43112f9db1c |
| trace_javascript_semantics | literal "ghidra" | ev_49e586ad3f15bba267fb263ad52f937267cdc21bf75c8de14c1dac0a653707c6 |
| inspect_analysis_view | summary | ev_01cf9c51b20f9fe2fd31c2bcd9fd6bbcb60ecb66c22b7c7c33afd32082a1bbb8 |
The temporary folder is not removed automatically. To keep the evidence, copy the folder first. To delete it, run this command in PowerShell:Remove-Item -Recurse -Force "C:\Users\<user>\AppData\Local\Temp\claude\C--Users-<user>-Downloads-REA\<session-id>\scratchpad"
10. Query scripts (verbatim)
To rerun a query, go to the scratchpad and run, for example, node g.js '^AnalysisProviderRegistry' 60 220. Set B=comp-bundle.json or B=cfg-bundle.json to query the other two files.
q.js: search node labels
// usage: node q.js <regex> [kind] [moduleRegex]
const fs=require("fs");
const b=JSON.parse(fs.readFileSync(__dirname+"/"+(process.env.B||"rea-bundle.json"),"utf8"));
const re=new RegExp(process.argv[2],"i"), kind=process.argv[3]||"literal", mre=process.argv[4]?new RegExp(process.argv[4]):null;
for(const r of b.records){const e=r.evidence||r; if(e.operation!=="analyze_javascript_application")continue;
const root=e.subject.local_path.split("\dist\\")[1];
for(const n of e.normalized_result.semantic_graph.nodes){ if(kind!=="*"&&n.kind!==kind)continue;
const m=n.identity.module_path; if(mre&&!mre.test(m))continue;
const L=String(n.label??n.identity.role_key); if(!re.test(L))continue; const s=n.identity.source_range;
console.log(`${root}/${m}:${s?s.start.line:"?"} [${n.kind}] ${L.slice(0,200)}`);}}
g.js: group node labels by source line
// usage: node g.js <moduleRegex> <fromLine> <toLine> : group node labels per line, dedup, drop noise
const {execFileSync}=require("child_process");
const fs=require("fs");
const b=JSON.parse(fs.readFileSync(__dirname+"/"+(process.env.B||"rea-bundle.json"),"utf8"));
const mre=new RegExp(process.argv[2]), from=+process.argv[3], to=+process.argv[4];
const noise=/^(parameter|argument \d+|variable|write|call|construct|import|return|class|await|member|spread|default)$/;
const lines={};
for(const r of b.records){const e=r.evidence||r; if(e.operation!=="analyze_javascript_application")continue;
for(const n of e.normalized_result.semantic_graph.nodes){const m=n.identity.module_path; if(!mre.test(m))continue;
const s=n.identity.source_range; if(!s||s.start.line<from||s.start.line>to)continue;
const L=String(n.label??""); if(noise.test(L)&&n.kind!=="literal")continue;
const key=m+":"+s.start.line; (lines[key]=lines[key]||new Set()).add((n.kind==="literal"?"LIT ":"")+L);}}
for(const k of Object.keys(lines).sort((a,b)=>+a.split(":").pop()-+b.split(":").pop())) console.log(k, "|", [...lines[k]].join(", ").slice(0,300));