merge: resolve environment changes from main

This commit is contained in:
morluto committed 2026-10-28 05:08:49 +08:00
commit 06cdfe71e9
622 files changed
+24509 -196582

No files matched your search

+195
View File
@@ -286,6 +286,162 @@ instructions into field declarations, leaving an empty or incomplete method
body. The selected method's disassembly preserves those instructions and retains
the same source byte limit and explicit truncation reporting.
## Apply an R8/ProGuard mapping
Release builds shrunk by R8 or ProGuard rename classes and members. Select the
build's `mapping.txt` with `mapping_path` on `inspect_android_class` or
`inspect_android_method`, or `--mapping-path` on the matching CLI command:
```sh
rea inspect-android-class /path/Example.apk a.b --mapping-path /path/mapping.txt \
--detail summary --evidence_output /tmp/class-evidence.json
```
For agent workflows, select `detail: "summary"` on class and method inspection.
It preserves the complete normalized member inventory or selected method body,
including mapping annotations, while retaining duplicate raw provider calls in
the complete parent Evidence. The reply links that parent's identity and digest;
export it with `export_evidence_bundle` or read its summary again through
`inspect_analysis_view`. CLI summaries require `--evidence_output` and return
the portable bundle path. Omitted or `complete` detail preserves complete inline
Evidence. Search and reference operations retain their existing contracts.
Symbol summary views admit at most **1 MiB**, including view context. An admitted
method's source can already consume that amount; a larger combined summary
fails explicitly instead of silently truncating facts. MCP refusal includes
the retained parent ID. Select complete detail, or inspect one method rather
than a whole class, when the requested summary does not fit.
The mapping path must be absolute. Selectors still take the observed names, such as
`a.b`, and observed rows are unchanged. The result adds one `mapping` block with
the file's path, SHA-256 and size, the header values the file reports
(`compiler`, `compiler_version`, `min_api`, `map_version`, `pg_map_id`,
`pg_map_hash`), valid record counts and up to 16 malformed line samples. Its
`symbols` list has one entry per observed class, inner class, method and field;
`inspect_android_method` lists the class and the selected method. `coverage`
counts each resolution status. With summary delivery, these unchanged facts are
under the view's `summary` field; the view separately reports source coverage.
Lookups use the bridge's raw DEX names, not JADX display aliases such as a
renamed reserved word. Each symbol resolves as:
- `mapped`: exactly one record matches. A class needs one record for its raw
name (`verified_by: "class"`). A method must also match its DEX prototype
(`"descriptor"`) and a field its DEX type (`"type"`). R8 residual signature
metadata is used when present; otherwise original types are translated
through the file's class records.
- `ambiguous`: `multiple-records`, `descriptor-mismatch`, `type-mismatch`,
`ambiguous-class` or `residual-signature-conflict`, with `candidate_count` and
up to 8 candidates. Conflicting residual metadata never selects a winner. A name match
whose prototype or type does not verify is never reported as mapped.
- `unmapped`: no valid record matched the raw identity.
- `unknown` with reason `projection-budget`: this claim did not fit the combined
reply budget. The observed raw symbol remains present; `coverage.unknown`
counts these omissions. Inspect that specific class or method separately,
or consult the selected local mapping file for complete source records.
A line continues an inline frame stack when it repeats the previous line's
minified range and obfuscated name and gives a single call-site line, such as
`:7`. Only the outermost original of a stack becomes a candidate. Original
line numbers and source files are not projected. Malformed records are counted
and skipped, so an `unmapped` symbol might be described by one of them. A file
that is missing, not a regular file, not UTF-8, larger than the byte limit or
without any class record, such as `usage.txt` or `seeds.txt`, is refused before
JADX is used. Requests read their mappings one at a time, in the same queue as
the JADX work and under the same operation deadline. A mapping failure therefore never starts or retires the JADX
session, and a different mapping reuses the loaded APK.
## Link native declarations to JNI candidates
When advertised, `inspect_android_jni_bindings` links original `ACC_NATIVE`
declarations from standard root `classes*.dex` members to bundled ELF libraries.
The supplied JAR's DEX reader preserves duplicate declarations before decompiler
deduplication. This operation does not load the APK into the decompiler.
MCP defaults to retained summary delivery when `detail` is omitted; select
`complete` explicitly to receive the full report inline.
```json
{
"path": "/absolute/Example.apk",
"class_name": "org.conscrypt.NativeCrypto",
"method_names": ["EVP_PKEY_new_RSA"],
"abis": ["arm64-v8a"],
"detail": "summary"
}
```
Class/method names and optional `descriptors` select raw DEX identities.
`library_paths` selects exact logical APK paths such as
`lib/arm64-v8a/libconscrypt_jni.so`; each library's `source_path` preserves its
original ZIP spelling. Omitted filters include all scoped facts; `[]` matches
none. Class selection never narrows the global inventory used for table-class
attribution.
Independent `declarations`, `libraries`, `candidates` and `bindings` collections
have stable IDs and source ordinals. Candidates retain APK/library digests,
ABI, function addresses and file offsets. Table candidates also retain section,
table and entry identity plus all three name/signature/function pointer slots
and their relocation sources. JNI exports follow the
[UTF-16 escaping and short-before-long rules](https://docs.oracle.com/en/java/javase/21/docs/specs/jni/design.html#resolving-native-method-names);
failed escaping does not acquire an invented export match. Tables require
NUL-terminated [Modified UTF-8 strings](https://docs.oracle.com/en/java/javase/21/docs/specs/jni/types.html#modified-utf-8-strings),
valid descriptors and executable load mappings. REL, RELA and RELR retain
separate provenance; unsupported packed relocations and unresolved pointer
spans stay explicit unknowns.
A static `JNINativeMethod` entry names no class. Attribute it uniquely only
when exactly one declaration in the complete recorded native inventory has
that `(name, descriptor)` pair. Multiple classes remain ambiguous after a
class filter; a partial inventory cannot establish unique attribution.
Each selected declaration has a row per selected/observed ABI. `matched` means
candidate presence was observed; `ambiguous` means multiple observed targets
or class associations remain. Binding `coverage` is separate: a matched
partial row does not establish complete uniqueness, and an empty partial scan
is `unknown`, never `unmatched`. ELF/ABI label mismatches preserve facts without
asserting a binding. No result establishes library loading, runtime registration
or method invocation.
Summary returns the first 64 selected binding rows, their declaration/candidate
closure and every selected library's identity, coverage and failure reason;
counts and page coverage describe the full selected binding set. Selected
declaration IDs have separate preview coverage, so native declarations remain
visible even when the APK has no bundled ABI libraries. Complete
Evidence stays retained. Read exact fields or further pages without reopening
files or rerunning analysis:
```json
{
"source": { "kind": "retained-evidence", "evidence_id": "ev_<digest>" },
"view": {
"kind": "android-jni",
"collection": "bindings",
"offset": 64,
"limit": 64,
"fields": ["declaration_id", "abi", "status", "coverage", "candidate_ids"]
}
}
```
Collections also include `declarations`, `candidates` and `libraries`, with
exact filters and batch lookup by declaration/candidate/library IDs. `row_index`
is the original parent collection index; DEX and
ZIP source ordinals stay separate. Complete detail returns the full report.
CLI uses the same selection and delivery contracts. Its common one-shot
default remains complete; summary delivery requires an explicit Evidence path:
```sh
rea inspect-android-jni-bindings /absolute/Example.apk \
--query '{"class_name":"org.conscrypt.NativeCrypto","method_names":["EVP_PKEY_new_RSA"],"abis":["arm64-v8a"]}' \
--detail summary --evidence_output /absolute/fresh-jni-evidence.json
```
Optional `--view` accepts the same JSON page and requires summary delivery.
Evidence bundles never overwrite files. Complete declarations and one library
selection are cached within the immutable APK session. Changing ABI/library
selection replaces the scan; class/method selection reuses it. Fresh analysis
still validates current caller APK bytes. Idle/close releases the caches;
retained Evidence remains historical and independently readable.
## Interpret the result
- Package inspection retains decoded manifest XML, summary declarations,
@@ -322,6 +478,11 @@ the same source byte limit and explicit truncation reporting.
`dex_descriptor`, for example `(I)V`. Display names can be JADX aliases;
the descriptor alone is not a complete raw DEX method ID. Overload indices
are tied to this artifact and engine, not stable identities across builds.
- A `mapping` block appears only when the caller selected `mapping_path`. Its
names are claims from that file, not observations of the APK. REA does not
check that the mapping belongs to this build; `pg_map_id` is reported, not
compared. Evidence keeps the selected path in its parameters and the file's
SHA-256 in the result.
- Method inspection reports Java or smali, fallback markers and complete/partial
returned text. Decompiled source is a derived representation. A complete text
result does not establish complete semantics or a byte-identical reconstruction.
@@ -411,6 +572,19 @@ target/configuration changes, failure, timeout, cancellation, MCP disconnect or
server shutdown. One-shot CLI commands always join cleanup before returning.
REA never writes to the original APK or launches target code.
JNI scans reuse the analyzer process without loading the APK into the
decompiler. They admit at most 64 MiB per DEX member and 256 MiB of DEX bytes,
with a 4 MiB native-declaration JSON allowance before MCP string escaping.
Native libraries are processed sequentially: one 64 MiB buffer, 256 MiB total
selected read work, and a 16 MiB parsed-library-facts allowance. ZIP inventory
and library counts also have explicit resource bounds. Unexamined occurrences
remain unknown. A complete relationship report has a separate 16 MiB serialized
allowance; summaries/pages allow 1 MiB including their provenance envelope.
These are resource admissions, not claims about maximum valid Android formats.
Select fewer ABI/library occurrences or smaller retained pages when needed.
Completed parsed library facts survive failed reader finalization in typed
partial observations; the exact reader owner remains available for cleanup retry.
On Windows, stdin writes preserve stream backpressure and run outside the Node
event loop. Cancellation terminates the retained job, including descendants,
before workspace cleanup. On Windows, a detached REA helper owns the private
@@ -496,6 +670,27 @@ complete, case-sensitive search contract. A protocol frame above **8 MiB**, or c
**32 MiB per operation**, fails with a diagnostic instead of returning an incomplete inventory
as complete. These are memory-safety boundaries, not undocumented result caps.
A selected R8/ProGuard mapping is re-read and SHA-256 checked on every call, up
to **64 MiB**. The serialized provider keeps one successful parsed index, keyed
by byte length and digest with fixed parser options. Identical bytes reuse that
index; changed bytes are reparsed, missing files still fail, and each response
preserves the currently selected path. The index is released on provider idle
retirement or close. Omitting `mapping_path` never applies the cached mapping.
During replacement, the old index can coexist with the new parse until it
succeeds; these transient allocations count toward process memory.
Before retaining records, the parser admits an estimated **128 MiB** index
representation and **8 MiB** of expanded mapped claims per residual class,
including shared header facts. The index allowance accounts for strings and
containers; it is not a measured V8 heap cap. Parameter and metadata expansion
is admitted before splitting or JSON decoding. These fixed resource bounds
have no runtime override; refusal reports `resource_constraint`, the source
line and mapping SHA-256. Ambiguous lookups count all candidates while keeping
only the first eight in stable file order. A separate **8 MiB** combined claim
budget applies to the actual query set, including outer and inner classes,
before cloning the report. Claims that do not fit become explicit `unknown`
entries; other admitted claims and all observed identities remain available.
## Real verification
Download the explicit public fixtures once; this installs no tools:
+1 -1
View File
@@ -75,7 +75,7 @@ graph TD
end
subgraph "Domain Layer"
DOMAIN["src/domain/<br/>Pure side-effect-free modules<br/>format declarations, Evidence, comparisons, application graphs"]
DOMAIN["src/domain/<br/>Pure side-effect-free modules<br/>format declarations, provider selection, Evidence, comparisons, application graphs"]
end
subgraph "Configuration"
+49 -17
View File
@@ -497,17 +497,51 @@ comments and unrelated settings. It resolves that file from `HERMES_HOME`, else
from the platform default Hermes itself uses — `%LOCALAPPDATA%\hermes` on
Windows, `~/.hermes` elsewhere — including any `HERMES_DATA_DIR_SUFFIX`. Hermes
connects an entry unless `enabled` is false, so setup states `enabled: true`.
Aliases whose anchor is defined in the `rea` entry retain their original resolved
values when setup refreshes or uninstall removes that entry. Setup does not
redirect another server's alias to REA's new launcher or policy.
This also covers an anchor on the removed registration key. References to
definitions outside the entry keep sharing the original value, including when
another definition later reuses the same anchor name.
Hermes personal skills install under the active Hermes home's `skills` directory.
Setup, doctor and uninstall follow the root's `active_profile` selection, as
plain Hermes CLI commands do. An explicit `HERMES_HOME` pointing to
`profiles/<name>` selects that named profile directly. An invalid, missing or
deleted sticky profile is reported instead of writing into the default home;
it also remains an unresolved skill selection, so setup cannot report a
successful empty installation. Overrides nested beneath the native Hermes home
use that native home's sticky selection and profile root together, matching
[Hermes's root rules](https://github.com/NousResearch/hermes-agent/blob/748c79a1c6873124d84071cdabb4062a75913e1f/hermes_constants.py#L83).
Repair the profile or run `hermes profile use default` before retrying.
Setup, doctor and uninstall share REA's sticky-profile resolver.
A custom `HERMES_HOME` outside the platform default home
reads its own `active_profile`. An override physically inside the default home
uses that default home's sticky selection and profile root together, including
when the final override directory does not yet exist.
For example, with `HERMES_HOME=~/.hermes/sub` and `~/.hermes/active_profile` set
to `coder`, setup selects `~/.hermes/profiles/coder/config.yaml` and that
profile's skills directory. It does not read `~/.hermes/sub/active_profile`.
Without an outer named selection, this override uses `~/.hermes/sub/config.yaml`.
Use a home outside the platform default tree for an independent sticky selection.
This describes REA's current routing, not full Hermes CLI equivalence.
At Hermes commit `748c79a1`, the
[sticky lookup](https://github.com/NousResearch/hermes-agent/blob/748c79a1c6873124d84071cdabb4062a75913e1f/hermes_cli/main.py#L612-L632)
also reads the outer selection, but
[`resolve_profile_env`](https://github.com/NousResearch/hermes-agent/blob/748c79a1c6873124d84071cdabb4062a75913e1f/hermes_cli/profiles.py#L2431-L2461)
resolves it beneath the configured `HERMES_HOME`. For nested overrides, confirm
the profile Hermes actually uses and point `HERMES_HOME` directly at that
`profiles/<name>` directory before setup. Compatibility is tracked in
[#3939](https://github.com/morluto/rea/issues/3939).
REA resolves existing ancestor directories before checking physical containment,
including symlinks and junctions, even when the override directory does not exist.
An override beneath a symlinked default home therefore uses the default home's
sticky selection. If `~/.hermes/link` points outside the default home,
`HERMES_HOME=~/.hermes/link/new` instead uses that override root, whether or not
`new` exists. Previewed destinations retain the configured path spelling.
An unreadable path or dangling alias leaves the home unresolved; repair that path
before configuring Hermes. Inspect setup's previewed destinations before applying
changes.
An explicit `HERMES_HOME` pointing to `profiles/<name>` selects that named
profile directly, bypassing sticky selection. An invalid, missing or deleted
sticky profile is reported instead of writing into the default home; repair the
profile or run `hermes profile use default` before retrying. It also remains an
unresolved skill selection, so setup cannot report a successful empty installation.
REA follows `HERMES_HOME` whitespace trimming, environment-variable expansion,
and home expansion before resolving that profile. Setup previews the resolved
configuration and skill destinations; doctor and uninstall use those same paths.
@@ -524,10 +558,12 @@ installs its skill copy there.
## Factory Droid
For [Factory Droid](https://docs.factory.com/harness/mcp), setup writes
`mcpServers.rea` into the user-level `~/.factory/mcp.json`. Entries carry
`command`, `args`, and `env`. Droid discovers personal skills from
`~/.factory/skills` ([skills](https://docs.factory.com/harness/skills)), so
setup installs its skill copy there.
`mcpServers.rea` into the user-level `~/.factory/mcp.json` (the
`FACTORY_HOME_OVERRIDE` override replaces the home that contains `.factory`).
Entries carry `command`, `args`, and `env`. Droid discovers personal skills
from `~/.factory/skills` ([skills](https://docs.factory.com/harness/skills),
following `FACTORY_HOME_OVERRIDE` when set), so setup installs its skill copy
there.
## Trae
@@ -703,10 +739,6 @@ can override an inherited disable. Lower files and other server entries remain
unchanged; a legacy server named `servers` that conflicts with the native table
requires manual repair before setup can write.
When Hermes aliases refer to an anchored REA entry, refreshing or removing REA
retains the aliases' original resolved values. Setup does not redirect another
server's alias to REA's new launcher or policy.
With no client selected, the interactive skill choice targets the shared
`~/.agents/skills` copy. Selecting a client targets its personal destination
when supported. Use `rea setup --skill-client shared --skill` for the shared
+4 -1
View File
@@ -36,7 +36,7 @@ The MCP tool `project_harmony_application_graph` takes the same
- exact component paths, artifact IDs, and hashes (manifests, Ark bytecode,
resources, native libraries, JavaScript, signing)
- a packaging-model hint: Stage is claimed only from a `module.json` entry
- a packaging-model hint: Stage is claimed only from an exactly cased `module.json` entry
outside a classified resource path, FA only from `config.json` outside a
classified resource path, otherwise `unknown`
- FA resources under `assets/<module>/resources/` as well as Stage `resources/`
@@ -46,6 +46,9 @@ The MCP tool `project_harmony_application_graph` takes the same
- path-based N-API bridge hypotheses between bytecode and `libs/<abi>` native
libraries
An unavailable manifest can still supply a path-based model hint; its content
identity remains unknown. Manifest names follow case-sensitive ZIP member names.
File and slice occurrences supply component and child-package paths; directory
trees and symlinks retain their identities in the source inventory without
being classified as code files. Ark and JavaScript runtime hints summarize the
+1 -1
View File
@@ -240,7 +240,7 @@ The verifier requires the resolved procedure in both inventory and procedure sea
The initial real workflows use stdio registrations for the legacy Windows GUI profile and a Windows x64 headless supervisor with IDA 9.3. The headless workflow has also passed on macOS arm64 with IDA Professional 9.1 (build 9.1.250226) against the same upstream commit `c133c3853faa111a9b00ee615c013b720d0c4acd`, using a source-built arm64 Mach-O fixture; Mach-O C symbols carry a leading underscore, so the procedure name is `_rea_fixture_add`. Streamable HTTP is tested against a loopback MCP fixture with the pinned client SDK; a real IDA HTTP-supervisor workflow remains unverified. Linux headless engine operation, other IDA releases, architectures, and modern attached GUI tools require their corresponding real workflows before support is claimed.
Native Windows verification uses IDA Pro 9.3 with the public two-function fixture and Windows `whoami.exe`, using upstream `c9d15d98e8a143fc74f6e14037c48f63889b6d6c` with a loopback-listener change. It covers production CLI/MCP parity, distinct-function switching, identity and connection cleanup. See the [correctness and performance measurements](https://github.com/morluto/rea/blob/main/experiments/analysis-efficiency/ida-native-windows.md). This follow-up did not run Windows IDA Free or native macOS.
Native Windows verification uses IDA Pro 9.3 with the public two-function fixture and Windows `whoami.exe`, using upstream `c9d15d98e8a143fc74f6e14037c48f63889b6d6c` with a loopback-listener change. It covers production CLI/MCP parity, distinct-function switching, identity and connection cleanup. See the [correctness and performance measurements](https://github.com/morluto/rea/blob/a2e2c29b59e8775b0e6aedb09a9eb16adf32646b/experiments/analysis-efficiency/ida-native-windows.md). This follow-up did not run Windows IDA Free or native macOS.
Headless readiness distinguishes an explicit `auto_analysis_ready: false` from
unknown readiness (`null`). The former returns a retryable capability refusal
+107 -7
View File
@@ -3,13 +3,14 @@
Run REA, connect your selected agents, and check the tools needed for your task.
Setup previews configuration changes and asks for approval before applying them.
| Task | Section |
| --------------------------------- | --------------------------------------------------------------------------------------------------- |
| Connect an agent | [Start setup](#start-setup), then [review changes](#review-setup-changes) |
| Diagnose one task | [Scoped readiness checks](#check-readiness-for-your-task) |
| Configure an analysis engine | [Hopper](#hopper), [Ghidra](#ghidra), the [IDA guide](ida-provider.md), or [Kuna](kuna-provider.md) |
| Update or remove REA | [Maintenance](#diagnose-update-and-remove) |
| Resolve a client-specific problem | [Client configuration](client-configuration.md) |
| Task | Section |
| ---------------------------------- | --------------------------------------------------------------------------------------------------- |
| Connect an agent | [Start setup](#start-setup), then [review changes](#review-setup-changes) |
| Install a Claude Code/Codex plugin | [Agent plugins and marketplaces](#agent-plugins-and-marketplaces) |
| Diagnose one task | [Scoped readiness checks](#check-readiness-for-your-task) |
| Configure an analysis engine | [Hopper](#hopper), [Ghidra](#ghidra), the [IDA guide](ida-provider.md), or [Kuna](kuna-provider.md) |
| Update or remove REA | [Maintenance](#diagnose-update-and-remove) |
| Resolve a client-specific problem | [Client configuration](client-configuration.md) |
## Start setup
@@ -444,6 +445,9 @@ and other MCP servers. Purging removes REA's legacy cache and state under
archives under `${XDG_CACHE_HOME:-~/.cache}/rea/hopper-releases`. Other cache
namespaces remain intact. A caller-selected `REA_KUNA_CACHE_DIR` is retained
and reported because REA cannot establish ownership of its entire contents.
Lexically equivalent spellings of that default directory, including a trailing
separator, a `.` segment, or a repeated separator, share its purge result.
The comparison preserves POSIX `..` traversal and literal backslashes.
A client configuration that is malformed, unreadable, or at an unsafe
path stops the operation before anything is removed, and a client that fails
while being updated stops the remaining removals. Uninstall reports each
@@ -1433,6 +1437,21 @@ current setting. For Windows PE resource analysis, the exact name is
`WindowsResourceReference`. Disabling an analyzer skips the facts it would have
produced.
Ghidra's Objective-C Message Analyzer can log
`Required Objective-C data type not found in data type manager` /
`Try adding libobjc.dylib` when a Mach-O links Objective-C runtime symbols but
defines no class metadata (thin Cocoa glue in a C++ binary is a common case).
That text is non-fatal Ghidra analyzer output: auto-analysis continues, and the
analyzer simply skips `objc_msgSend` call-target recovery. REA's headless import
does not accept a "add this library" option, and on modern macOS
`/usr/lib/libobjc.A.dylib` exists only in the dyld shared cache, so there is no
on-disk library to add. Treat the line as a coverage limitation, not an REA
failure; check the session's `failure_kind` (and related provider errors) for
any real abort. To silence that analyzer, set
`REA_GHIDRA_DISABLED_ANALYZERS="Objective-C Message Analyzer"`. For large
executables that miss the startup deadline instead, raise
`REA_GHIDRA_STARTUP_TIMEOUT_MS` and the [Ghidra heap](#ghidra-heap-and-cpu-controls).
### Ghidra heap and CPU controls
Set resource controls in the environment that launches the CLI or MCP server,
@@ -1534,6 +1553,87 @@ read-only operations, digest identity, and runtime cleanup. The separate
ordinary user. It requires the built Windows native bundle and an existing
Ghidra/JDK installation; neither is inferred from startup alone.
## Agent plugins and marketplaces
REA's npm package is the shared Claude Code and Codex plugin artifact. It
contains `.claude-plugin/plugin.json`, `.codex-plugin/plugin.json`, and the
canonical generated `skills/` tree. Both manifests select `./skills` and run a plain Node bootstrap pinned to
that exact package version. The client must have a supported standalone `node`
on its PATH. Claude expands its documented `CLAUDE_PLUGIN_ROOT` in the script
argument; Codex resolves `cwd: "."` from the installed plugin root before
launching the relative script. The bootstrap verifies the package version and runs its bundled runtime in
the same process, preserving stdio and REA's provider/worker cleanup. It does
not start another package runner or download a different runtime. The existing release skill
generator also pins CLI recovery commands to the same version.
The repository's single-plugin marketplaces are declared in
`.claude-plugin/marketplace.json` and `.agents/plugins/marketplace.json`.
Both select an exact `rea-agents` npm version instead of mutable source-checkout
contents. Release Please updates their selectors alongside the plugin manifests.
No plugin-only publication or generated skill files committed to Git are needed.
The qualified release archive contains a runtime-only manifest and
`npm-shrinkwrap.json` plus bundled runtime dependencies, including PTY packages
for each supported OS and CPU; development dependencies and development-only overrides
stay in the source manifest and lockfile. Both clients receive this same archive.
This distribution becomes installable after the first qualified npm release
containing these plugin manifests. Earlier published packages are not changed
by this source proposal. For local source development, run `npm ci && npm run build:cached` to generate
the runtime and packaged skill before loading the repository as a plugin.
A source plugin uses the locally built runtime and generated skill; rebuild after
source changes. Missing builds and mismatched version pins fail with an actionable
error. The authored `.agents/skills/` tree is not the plugin's shipped skill layout.
Windows source workflows that need native controls still require the
[Windows source-checkout build](https://github.com/morluto/rea/blob/main/native/windows/README.md#build).
In Claude Code:
```text
/plugin marketplace add morluto/rea
/plugin install rea@rea
```
In Codex CLI, add the marketplace and install REA:
```bash
codex plugin marketplace add morluto/rea
codex plugin add rea@rea
```
Each release updates both manifest versions and their pinned server version
together, which is when installed copies refresh the bundled skill and move to
the new server. A plugin does not run `rea setup`, so provider
configuration still follows the sections above. Use the plugin or a
`rea setup` registration for a client, not both, to avoid two `rea` servers.
`rea doctor` does not see plugin-owned state. Its client and skill checks read
only the registrations and skill locations that `rea setup` manages, so for a
plugin-only install `rea doctor --client <id>` and `rea doctor --skill` report
`missing` and recommend `rea setup`. Do not follow that remediation. Check the
plugin with the client instead (`claude plugin list` and `/mcp` in Claude
Code, `codex plugin list` in Codex).
For analysis engines, ask the connected server: have the agent call
`binary_session` and read `result.tool_availability`, which reports what the
server the plugin started can use, with reasons and remediation. See
[MCP contracts](mcp-contracts.md).
A plugin does not put `rea` on your `PATH`. To check an engine from a
terminal, use the package runner at the version the plugin pins:
<!-- x-release-please-start-version -->
```bash
npx -y rea-agents@6.8.0 doctor --provider ghidra --json
```
<!-- x-release-please-end -->
If your installed plugin is older, use the version in its manifest instead.
That report describes the terminal's environment, not the plugin server's.
The server inherits the client's environment, so a setting such as
`GHIDRA_INSTALL_DIR` that exists only in your terminal does not reach it.
## MCP Registry
REA is published in the official MCP Registry as `io.github.morluto/rea`. Registry
+13 -2
View File
@@ -1248,11 +1248,22 @@ failures for such entries name this cause and the operation to rerun with
Advertised input and output schemas contain no reachable recursive references.
Input schemas inline repeated definitions before applying the selected
presentation profile. Both `full` and `compact` start from this self-contained
projection; output schemas can share schema-local definitions. Full inputs
retain nested fields, descriptions and valid literal examples. The compatibility
projection; output schemas can share schema-local definitions. Analyst-authored
full inputs retain nested fields, descriptions and valid literal examples. The compatibility
projections below can relax advertised constraints; canonical runtime validation
still enforces the complete input contract.
`compare_web_captures` declares its complete producer observations as object
operands instead of repeating their full output schemas in the input catalog.
Pass the original `inspect_web_page` normalized result in `before.inspection`
and `after.inspection`, optional complete `discover_webmcp_tools` results in
`webmcp`, or complete `capture_browser_scenario` results in the scenario fields.
Do not construct partial observations from the abbreviated declaration: the
server still validates every canonical field before comparison. Input groups
and analyst-authored normalization rules remain explicit. This presentation
changes no capture bytes, identity, completeness, or comparison semantics and
does not reduce `inspect_analysis_view` selectors or other tools' declarations.
When a host forwards input schemas to the model, their examples consume context.
REA's advertised examples therefore never copy complete producer Evidence.
An embedded Evidence record is shown as its exact `{"kind":"retained-evidence","evidence_id":"ev_..."}`
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
-112
View File
@@ -1,112 +0,0 @@
{
"schemaVersion": 1,
"repository": "morluto/rea",
"version": "6.7.0",
"base": "2c4338db1fdb7773e05376301c558cff2a0b4b80",
"target": "b83b9caf4a5f0241a5fd370792f1aed1bffbb2cf",
"commits": [
{
"sha": "8cbaf6c7021b97dd4de6b5771c35fbed06b240e9",
"subject": "fix(tests): isolate command finalization module mocks",
"body": "fix(tests): isolate command finalization module mocks\n\nMove the five registered WASM finalization cases into the existing per-file isolated project. Preserve all result, exit-code, observation and cleanup assertions. A preloaded shared CLI graph reproduces three failures before the move; the isolated covered packet passes.\n\nFixes #4959\n",
"parents": ["2c4338db1fdb7773e05376301c558cff2a0b4b80"],
"authors": [
{
"name": "morluto",
"email": "76467478+morluto@users.noreply.github.com",
"user": {
"login": "morluto",
"__typename": "User"
}
}
],
"pullRequests": []
},
{
"sha": "6b180783ed04ebc87548c8385c80e05725804c47",
"subject": "fix(tests): budget CLI coverage from parsed options",
"body": "fix(tests): budget CLI coverage from parsed options\n\nUse the pinned Vitest parser for coverage and shard flags. Keep ordinary CLI workers at 384 MiB and covered workers at the tested 512 MiB with unchanged concurrency. Retain complete coverage and add actual-config regressions for false, provider-only and spaced-shard options.\n\nValidation: 27 focused cases; 16 covered cases with CLI preloaded; 5 docs checks; typecheck/lint; exact discovery of 1578 files. Measured covered control: 22.18s elapsed, 46.52s CPU, about 2.33 GiB sampled summed process-family RSS on macOS arm64. Full Linux CI pending. No runtime/device verification claimed.\n\nFixes #4960\n",
"parents": ["8cbaf6c7021b97dd4de6b5771c35fbed06b240e9"],
"authors": [
{
"name": "morluto",
"email": "76467478+morluto@users.noreply.github.com",
"user": {
"login": "morluto",
"__typename": "User"
}
}
],
"pullRequests": []
},
{
"sha": "bb212ffecb47e129cc569b9867b2b6e7c9b65685",
"subject": "fix(tests): inherit server identity deadline policy",
"body": "fix(tests): inherit server identity deadline policy\n\nRemove per-case ten-second overrides from MCP catalog and identity checks. Keep the suite-owned bounded ordinary and coverage deadlines, all assertions, and session cleanup.\n\nFixes #4967\n",
"parents": ["6b180783ed04ebc87548c8385c80e05725804c47"],
"authors": [
{
"name": "morluto",
"email": "76467478+morluto@users.noreply.github.com",
"user": {
"login": "morluto",
"__typename": "User"
}
}
],
"pullRequests": []
},
{
"sha": "b83b9caf4a5f0241a5fd370792f1aed1bffbb2cf",
"subject": "fix(setup): edit the duplicate JSON member that clients read (#4957)",
"body": "fix(setup): edit the duplicate JSON member that clients read (#4957)\n\nJSON.parse, JSONC parse, and native Claude Code keep the last duplicate\nobject member, and parseClientConfiguration therefore validates the last\none. The shared setup/uninstall serializer instead edited text through\njsonc-parser's modify and findNodeAtLocation, which select the first\nmatching member, so setup and uninstall on configurations with repeated\nserver tables or rea members wrote into a shadowed member and failed\nreadback.\n\nLocate every JSON text edit by the effective (last) member in one place,\nreplacing the first-member modify path. Removing an effective member\nthat shadows an earlier duplicate would activate that duplicate, whatever\nits ownership, so the serializer now returns a tagged shadowed_member\nfailure and setup and uninstall refuse before writing a backup or the\nconfiguration.\n\nFixes #4952\n\nCo-authored-by: Claude Opus 5.5 <noreply@anthropic.com>",
"parents": ["bb212ffecb47e129cc569b9867b2b6e7c9b65685"],
"authors": [
{
"name": "Kaoru0822",
"email": "kanadekitauji@proton.me",
"user": {
"login": "kaoru0822-kitauji",
"__typename": "User"
}
},
{
"name": "Claude Opus 5.5",
"email": "noreply@anthropic.com",
"user": {
"login": "claude",
"__typename": "User"
}
}
],
"pullRequests": [4957]
}
],
"pullRequests": [
{
"number": 4957,
"title": "fix(setup): edit the duplicate JSON member that clients read",
"url": "https://github.com/morluto/rea/pull/4957",
"author": {
"login": "kaoru0822-kitauji",
"__typename": "User"
},
"mergeCommit": {
"oid": "b83b9caf4a5f0241a5fd370792f1aed1bffbb2cf"
},
"issues": [
{
"number": 4952,
"url": "https://github.com/morluto/rea/issues/4952",
"author": {
"login": "xu91102",
"__typename": "User"
},
"repository": {
"nameWithOwner": "morluto/rea"
}
}
]
}
]
}
+19 -1
View File
@@ -349,7 +349,10 @@ tag remains in main's ancestry. Keep the released tag unchanged.
The synchronization must include `.release-please-manifest.json`,
`package.json`, both root versions in `package-lock.json`, `CHANGELOG.md`,
`server.json`, and the versioned documentation examples. Run
`server.json`, both plugin manifests (`.claude-plugin/plugin.json` and
`.codex-plugin/plugin.json`), the Codex server declaration (`.codex-plugin/mcp.json`), both
marketplaces (`.claude-plugin/marketplace.json` and
`.agents/plugins/marketplace.json`), and the versioned documentation examples. Run
`npm run docs:generate`, `npm run docs:check`, and the release configuration
tests against the combined tree. After the merge, verify that the released
tag is an ancestor of main:
@@ -383,3 +386,18 @@ tag. Do not dispatch a fresh publish phase to repair an already-created
release: Release Please will not create the same release again. Do not move
the tag, delete the release, or unpublish npm as a retry. A defective public
package requires a reviewed correction and a new version.
### Plugin artifact metadata
Package verification selects files with npm, then packs an owned staging directory.
The release manifest omits development dependencies and development-only overrides;
its generated `npm-shrinkwrap.json` preserves the runtime dependency resolutions
and integrity values from `package-lock.json`. Runtime or unresolved overrides
fail packaging rather than being dropped. Source metadata is never rewritten.
The archive bundles production dependencies because Codex fetches npm plugins
without installing dependencies. The producer includes all locked PTY platform
archives after checking their integrity, without re-resolving the runtime tree.
Optional Frida stays outside the bundle so ordinary npm installation can retain
its host-specific installer. This also lets Claude install locked dependencies under its plugin policy while
retaining development security overrides. Both plugin marketplaces select the
same qualified npm artifact, which the release workflow publishes unchanged.
+20 -5
View File
@@ -5,6 +5,12 @@ lane. Start with the commands below; use [real-toolchain lanes](#real-toolchain-
for engine prerequisites and [client verification](client-verification.md) for
installed-client and agent runs.
Historical verification records may refer to `experiments/analysis-efficiency`.
Those experiment scripts are available in the [archived source tree](https://github.com/morluto/rea/tree/a2e2c29b59e8775b0e6aedb09a9eb16adf32646b/experiments/analysis-efficiency);
they are not included in current checkouts. The retained [test support](https://github.com/morluto/rea/blob/main/tests/support/analysis-efficiency/README.md)
contains deterministic checkers and fixtures. The [allocation report](verification/analysis-efficiency/mcp-integer-allocation.md)
and its original receipt JSON remain documentation, checked by the report producer.
## Developer commands
Use source feedback while editing, explicit boundary checks for the changed
@@ -204,7 +210,11 @@ changing a callback solely to satisfy its declared `void` return type.
Ordinary Knip remains. `check`, `check:pr`, and `check:ci:deep` run reachability
then typed lint sequentially after cached work. Typed lint builds the project
type graph and stays separate from fast feedback. Its default Go settings are
`GOMAXPROCS=2` and `GOMEMLIMIT=1024MiB`; caller-selected settings take precedence.
`GOMAXPROCS=1` and `GOMEMLIMIT=1024MiB`; caller-selected settings take precedence.
One Go worker reuses checker caches across the file queue without changing the
file inventory or TypeScript project scope. The Go memory target is soft and
does not cap the complete process family's memory; measure the full invocation
when enforcing a host budget.
Routine iterations and rebases need focused regressions and relevant checks.
Before handing off a PR, run `npm run check` and generated-document checks when
@@ -1164,8 +1174,8 @@ its metadata regressions against the same checksum-pinned engine and public APK.
It runs on branch pushes affecting its bridge, fixture or engine inputs, and can
also be dispatched manually or called by another workflow. It uses the runner's
JDK with two active processors and a 512 MiB heap; it needs no npm dependency
installation. This lane checks exact raw/display class selectors and the existing
cold-metadata regressions. It does not establish CLI/MCP parity or application
installation. This lane checks exact raw/display class selectors, raw member names and field
DEX types used by mapping lookups, and the existing cold-metadata regressions. It does not establish CLI/MCP parity or application
execution behavior. The broader `verify:android` lane checks CLI/MCP parity;
neither lane runs the APK.
@@ -1211,7 +1221,7 @@ HarmonyOS `.har` libraries are deliberately not suffix-classified because
Synthetic producer regressions run independently:
```sh
npm run test:focused -- tests/boundary/process/jadxIntegration.test.ts tests/boundary/process/androidAnalysisMcp.test.ts
npm run test:focused -- tests/boundary/process/jadxIntegration.test.ts tests/boundary/process/androidAnalysisMcp.test.ts tests/boundary/process/androidMappingMcp.test.ts
```
## Apktool resource decoding
@@ -1374,7 +1384,7 @@ other engine versions and architectures remain unverified; see the
Native IDA MCP verification uses `"protocol": "native"` with an already-running native plugin and requires `--second-procedure other_function --second-marker unique_second_body_text` to exercise GUI function switching and a return to the first function. These options remain optional for other profiles. The literal marker must appear in the second function's pseudocode and be absent from the first. Native cloud/widget failures are reported without hidden retries.
The initial Linux x64 IDA Free 9.4 run used native MCP 1.1.2 with a local navigation correction. Windows x64 verification uses **IDA Pro 9.3**, the two-function public fixture, and the shipped Windows `whoami.exe` application. The native plugin was built from upstream `c9d15d98e8a143fc74f6e14037c48f63889b6d6c`, including the merged navigation fix, with a local loopback-listener change. Production CLI/MCP parity, switching, identity and cleanup passed. Correctness and performance measurements are recorded in the [Windows native verification notes](https://github.com/morluto/rea/blob/main/experiments/analysis-efficiency/ida-native-windows.md). Stock 1.1.2 switching, native macOS, and Windows IDA Free remain unverified. See the [native bridge prerequisites](ida-provider.md#optional-native-ida-mcp-bridge).
The initial Linux x64 IDA Free 9.4 run used native MCP 1.1.2 with a local navigation correction. Windows x64 verification uses **IDA Pro 9.3**, the two-function public fixture, and the shipped Windows `whoami.exe` application. The native plugin was built from upstream `c9d15d98e8a143fc74f6e14037c48f63889b6d6c`, including the merged navigation fix, with a local loopback-listener change. Production CLI/MCP parity, switching, identity and cleanup passed. Correctness and performance measurements are recorded in the [Windows native verification notes](https://github.com/morluto/rea/blob/a2e2c29b59e8775b0e6aedb09a9eb16adf32646b/experiments/analysis-efficiency/ida-native-windows.md). Stock 1.1.2 switching, native macOS, and Windows IDA Free remain unverified. See the [native bridge prerequisites](ida-provider.md#optional-native-ida-mcp-bridge).
## Kuna lane
@@ -1624,6 +1634,11 @@ results after a failure. Available failed-shard reports remain diagnostic only:
failed, cancelled, skipped, or missing selected shards cannot pass the gate or
publish successful timing history. This deliberately avoids relying on an
import graph for generated files, filesystem inputs, subprocesses, or bridges.
The portable report rejects an entirely skipped module inventory and lists
fully skipped modules in its summary. A mixed passed/skipped inventory is
allowed: Vitest's skip state does not establish whether a skipped module was
required on that runner. Review the listed skips against platform and provider
prerequisites; the inventory alone does not prove every required case ran.
Scheduled qualification is admitted hourly at minute 23. A forced full run at
03:23 UTC refreshes runner and dependency drift every day. Other hourly runs
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,150 @@
# Integer validator allocation after MCP materialization
Issue #6236; PR #4360. Linux x64, 2026-10-22.
<!-- BEGIN:allocation-report:provenance -->
Baseline `main@4a4bbab3c337701febeb6b532ace49041c4876e0`; candidate `461f342c71a72ef2be60912391df2393aa4e2ea3`. This is a pinned comparison.
In separate inspector/forced-GC diagnostics, retained REA V8 heap decreases by about 9.6 MiB. The three-pair ranges do not overlap in either profile. Inspector perturbation is included. Normal task timing, CPU and peak memory are reported separately; these observations do not establish a general latency or peak-memory reduction.
<!-- END:allocation-report:provenance -->
Directory discovery uses recorded advertisements after #4424; this study separates it from completed tasks that materialize canonical handlers. Context is unchanged.
## Change and compatibility
Replace 1,247 argument-free `z.number().int()` constructions with `z.int()` across 175 existing source files. Keep current shared schema owners, refinements, bounds, defaults and metadata. AST-token comparison against the baseline, after normalizing this construction and formatting, is equal for every touched source file. No new tools, provider effects, caches or response contracts.
The locked Zod 4.4.3 chained form retains a `ZodNumber` with a separate `ZodNumberFormat` check; direct construction uses the format itself. Incur 0.6.1 coerces argv strings only for `ZodNumber`. Existing CLI declarations and shared-input exceptions therefore retain the chained form. The actual compiled CLI registers 160 leaf commands; 104 numeric argument/option fields have identical results/errors in 974 installed-Incur parser cases across both runtimes, each field with an accepted probe. Fields are isolated from unrelated required options and no handlers are invoked. This is consumer-boundary coverage, not a real-engine claim.
Seven dependency-boundary variants match parsed values and complete error issues for 126 cases, including fractions, nonfinite values, strings/null/undefined and unsafe integers; 14 JSON Schema projections match in draft-07/draft-2020-12. Actual SDK advertisements match byte-for-byte in both profiles before/after materialization and across roles.
## Tasks, artifacts and bounds
Reuse the shared SDK task driver and independent ELF/section/zlib/JSON oracles from [the archived catalog study](https://github.com/morluto/rea/blob/a2e2c29b59e8775b0e6aedb09a9eb16adf32646b/experiments/analysis-efficiency/mcp-input-catalog.md): official Alpine v3.21 x86_64 ripgrep 14.1.1-r0 and Zellij 0.40.0-r0. All 42/422 packages and 88/1,063 edges match the independent oracles. Select serde name/version/kind, repeat exact retained-parent reads twice, verify a canonical negative-offset refusal, export/check complete Evidence and confirm the unchanged directory. `kind: null` stays unknown. Targets are not executed; dependency presence does not establish runtime use. Acquisition URLs, archive/executable/oracle hashes and complete answers are in [the receipts](https://github.com/morluto/rea/blob/main/docs/verification/analysis-efficiency/mcp-integer-allocation.json). Previously acquired public bytes are rehashed, not redownloaded.
Normal cohort: three alternating baseline/candidate pairs for each task × cold/warm server compile cache × default compact/full inputs: 72 trials, plus four excluded full-task warm priming runs. Separate diagnostic cohort: three alternating pairs per profile, cold Zellij tasks, 12 trials. All pass, with identical complete parent IDs/hashes and answers.
Node 24.18.0/npm 11.16.0; fresh client/server and HOME per trial, driver compile cache disabled. Each trial is serial, pinned to one CPU, 4 GiB hard/3 GiB early stop, no swap, at most 192 tasks. Before/after checks bind clean source, all compiled JS/JSON assets (1,990 baseline / 1,990 candidate files), scripts and manifests; producer and resource-observer hashes are also bound. No OOM, resource stop or leftover payload processes. Whole-family measurements include SDK validation, extra calls, source/compiled audits and oracle checking; kernel peak includes file cache. Build/acquisition/oracle generation and priming are excluded. Warm uses a populated role/profile-specific Node cache; hit rate and OS page cache are unmeasured, and audits warm compiled-file pages.
## Normal task costs
Medians, baseline → candidate; all ranges are in the JSON. No inspector or diagnostic GC runs in this cohort.
<!-- BEGIN:allocation-report:normal -->
| Task | Cache | Input profile | Wall s | Family CPU s | Family peak MiB |
| --------- | ----- | ------------- | --------------: | --------------: | --------------: |
| directory | cold | default | 2.739 → 2.239 | 2.553 → 2.075 | 247.4 → 248.4 |
| directory | cold | full | 2.940 → 2.437 | 2.682 → 2.217 | 348.0 → 346.8 |
| directory | warm | default | 2.639 → 2.135 | 2.461 → 1.980 | 251.8 → 246.2 |
| directory | warm | full | 2.840 → 2.338 | 2.666 → 2.144 | 348.6 → 347.2 |
| ripgrep | cold | default | 7.571 → 7.169 | 7.372 → 6.918 | 798.8 → 791.1 |
| zellij | cold | default | 7.876 → 7.373 | 7.623 → 7.134 | 812.2 → 810.5 |
| ripgrep | cold | full | 7.876 → 7.475 | 7.710 → 7.295 | 803.7 → 801.2 |
| zellij | cold | full | 8.079 → 7.573 | 7.852 → 7.378 | 815.7 → 819.1 |
| ripgrep | warm | default | 7.575 → 6.977 | 7.352 → 6.810 | 815.0 → 791.6 |
| zellij | warm | default | 7.774 → 7.178 | 7.581 → 6.981 | 820.9 → 822.0 |
| ripgrep | warm | full | 7.976 → 7.875 | 7.746 → 7.679 | 812.7 → 789.3 |
| zellij | warm | full | 14.995 → 13.978 | 14.742 → 13.718 | 818.5 → 811.5 |
<!-- END:allocation-report:normal -->
### SDK span and audit separation
<!-- BEGIN:allocation-report:sdk -->
Cold/default directory audit wall medians are 1.273 s baseline / 0.782 s candidate; SDK spans are 0.944 → 0.948 s. Whole-family costs include those experimental audits. Their cause and per-span CPU are unmeasured.
SDK span from client connect through task checks and client/server close. Excludes pre/post runtime binding and preflight artifact/oracle loading. Diagnostic spans include inspector and forced GC. CPU is measured for the whole family only; per-span CPU is unmeasured.
| Task | Cache | Inputs | SDK span s, baseline → candidate |
| --------- | ----- | ------- | -------------------------------: |
| directory | cold | default | 0.944 → 0.948 |
| directory | cold | full | 1.067 → 1.102 |
| directory | warm | default | 0.888 → 0.878 |
| directory | warm | full | 1.038 → 1.042 |
| ripgrep | cold | default | 5.778 → 5.752 |
| zellij | cold | default | 5.998 → 5.991 |
| ripgrep | cold | full | 6.077 → 6.119 |
| zellij | cold | full | 6.228 → 6.209 |
| ripgrep | warm | default | 5.743 → 5.656 |
| zellij | warm | default | 5.965 → 5.788 |
| ripgrep | warm | full | 6.100 → 6.492 |
| zellij | warm | full | 11.776 → 11.529 |
<!-- END:allocation-report:sdk -->
Full-profile warm Zellij is slower than its cold condition in both roles in this cohort. Cache hit rate and the cause of this observation are unmeasured, and cold/warm groups run sequentially. These timings support neither a general cache speedup nor a causal regression attribution.
## Separate retained-heap diagnostics
The same completed SDK task launches only its owned REA process with `--inspect=127.0.0.1:0`. Four CDP calls verify the owned transport PID, read heap, force GC and read heap again. The endpoint comes from that child’s stderr; the PID is a cross-check of the owned endpoint, not sole ownership proof. The inspector socket closes before MCP shutdown. These V8 values include inspector perturbation and exclude the client; diagnostic wall/CPU/family peaks include inspector and forced-GC overhead and are not normal-task timing results.
<!-- BEGIN:allocation-report:heap -->
| Inputs | REA V8 retained MiB | Baseline range | Candidate range | Diagnostic wall s | CPU s | Family peak MiB |
| ------- | ------------------: | --------------: | --------------: | ----------------: | --------------: | --------------: |
| default | 189.835 → 180.242 | 189.823–189.855 | 180.208–180.366 | 15.490 → 14.291 | 15.246 → 14.123 | 814.1 → 786.3 |
| full | 191.260 → 181.693 | 191.165–191.304 | 181.588–181.848 | 8.979 → 8.377 | 8.769 → 8.142 | 810.3 → 824.9 |
<!-- END:allocation-report:heap -->
A mismatched PID is rejected; removing only that guard makes the negative oracle fail. Both connections in that check use the same owned REA endpoint, with no foreign-process attachment. Aggregation rejects diagnostic GC in a normal trial. Historical #4424 receipt reaggregation preserves previous metrics and adds the separately reported SDK-span fields; its aggregator source hash changes.
## Context accounting
<!-- BEGIN:allocation-report:context -->
tiktoken==0.14.0/cl100k_base; estimates, not model usage. Actual model usage is unmeasured.
| Profile | Input directory once | Directory task | ripgrep task | Zellij task |
| ------- | -------------------: | -------------: | -----------: | ----------: |
| default | 49,670 | 50,840 | 54,029 | 53,949 |
| full | 205,744 | 206,914 | 210,103 | 210,023 |
Raw SDK directory costs per list: 756,165/912,436 tokens and 2,935,148/3,587,792 bytes for default/full.
Question, explicit recipe, server instructions, one complete name/description/input-schema directory, every tool argument and text reply, plus prompt directory for discovery. This projection assumes the client retains a single unchanged directory. Raw catalog result costs include every observed tools/list result separately, with output schemas and without JSON-RPC envelopes. SDK initialization/framing, reasoning and actual model usage are unmeasured. The analysis task includes retained-read, rejection, export/oracle and post-materialization directory checks.
<!-- END:allocation-report:context -->
## Validation and reproduction
- Both runtime builds and all six static checks plus architecture guards pass. Before the final main synchronization, 5,972 source/contract/public CLI/MCP tests pass across 323 files; four skip for unavailable/guarded coverage. After synchronization, both builds/static checks repeat successfully, 77 affected producer and CLI/MCP tests pass, and all 974 registered numeric-parameter parser cases match again. Includes real SQLite CLI/MCP record/column selection and JavaScript heap/deadline preservation. A first direct schema-validity invocation lacked generated catalog metadata; the supported focused entry generates it and all 15 cases pass. A later affected-test invocation selected two nonexistent paths; the corrected three-file producer/consumer run is the 77-case result above. Neither setup failure was counted as a passing test, and no production or timeout change was needed.
- All 84 measured tasks and four warm priming tasks pass independent oracles, canonical refusal, parent/directory identity and owned cleanup checks. This does not verify external Hopper/Ghidra/IDA, Android devices, all providers or foreign OS behavior. The earlier macOS numbers in #4360 describe a different revision/platform and are not combined with these results.
The historical cohort commands below use the retired experiment layout. Run them
from the matching recorded producer checkout; the [archived source tree](https://github.com/morluto/rea/tree/a2e2c29b59e8775b0e6aedb09a9eb16adf32646b/experiments/analysis-efficiency)
provides the former files for reference. Current checkouts retain the report and
its conformance producer, rather than the full measurement suite. Moving these
files does not requalify the historical measurements against current REA.
Build the pinned baseline/candidate in separate clean worktrees with the locked toolchain. Use the candidate’s producer scripts for both roles. `measurement_inputs` in the JSON records the complete matrix, questions/recipes, runtime bindings and producer hashes; extract it unchanged as the bindings file. Historical receipts require their matching producer revision. One normal task:
```bash
NODE_DISABLE_COMPILE_CACHE=1 node experiments/analysis-efficiency/mcp-input-catalog.mjs \
--runtime "$CANDIDATE_RUNTIME" --bindings "$MEASUREMENT_INPUTS" --role candidate \
--artifact "$RIPGREP_BINARY" --oracle "$RIPGREP_ORACLE" \
--output "$NEW_CASE_DIRECTORY" --profile default --phase analysis --cache cold
```
Run each task through `resource-run.py --observe-payload-exit --` to obtain whole-family counters. For warm runs replace `--cache cold` with `--cache warm --cache-root <role/profile cache>` after one complete priming task. For the separate Zellij diagnostic tasks add `--diagnostic-heap` and use cold cache. Keep their case names/logs aligned with `measurement_inputs`; aggregate both cohorts with:
```bash
python experiments/analysis-efficiency/mcp-startup-costs.py "$RECEIPT_ROOT" \
--output mcp-integer-allocation.json
```
The aggregator requires the recorded tiktoken version, exact catalog/parent/runtime/producer/guard identities, clean source, successful payload cleanup and matching matrix coverage. Default driver behavior continues without an inspector; the heap flag is confined to the experiment.
In a current checkout, regenerate or verify every numerical section directly
from the committed JSON:
```bash
node tests/support/analysis-efficiency/mcp-integer-report.mjs
node tests/support/analysis-efficiency/mcp-integer-report.mjs --check
```
The documentation conformance gate checks current receipts, rejects stale normal/heap tables, and refuses missing producer markers without writing.
@@ -0,0 +1,320 @@
# Static Android JNI verification, 2026-10-27
Issue #6403 asks which bundled native functions could implement original DEX
native declarations. The implementation reads immutable APK bytes, preserving
declarations, library occurrences, exported/table candidates and selected
per-ABI bindings as independently queryable facts. No target, library,
`JNI_OnLoad` or `RegisterNatives` code was executed.
## Revisions and independent oracles
The full qualification and alternating delivery measurements below use
implementation `4a1839de6f091ade19e82557874c358ed34b8e36`, based on main
`03f17cf41d3c844f9383d7792d9b1a14330d0bb2`. Subsequent merge
`e8a8a50725cd384ae38ce66a94bafdaba1138108` incorporates main
`d6f88becd531cabafca1d968b89cf7fc77a80041`; its browser input-presentation and
SQLite test changes are separate from JNI analysis. Post-merge confirmation is
recorded below. The native parser's built SHA-256 is
`3bf0a2c342f1c404f5861b7c913f7f1b931f740a8c808b518474fc8de20821d5`.
Linux x86-64, Node 24.22.1/npm 11.16.0 and JDK 21.0.12.1 qualified the workflows.
JADX MCP 0.7.1 remains the supplied unmodified JAR, SHA-256
`6e5eacf500b64292bfb73c49797c1958f6ee44646e43e868039ae7feb573ff75`.
The REA metadata bridge is version 9. JNI declarations use the JAR's existing
dexlib2 reader before decompiler deduplication, with no `load_apk` call.
The genuine Android NDK r27d package is revision 27.3.13750724, clang/LLD 18.0.4.
Its official Linux ZIP is 663,956,036 bytes, publisher SHA-1
`22105e410cf29afcf163760cc95522b9fb981121`; observed SHA-256 is
`601246087a682d1944e1e16dd85bc6e49560fe8b6d61255be2829178c8ed15d9`.
Pins come from the [official repository manifest](https://dl.google.com/android/repository/repository2-3.xml).
The package was streamed, hashed and selectively extracted into a private task
cache, without installing or changing global tools. The selected compiler,
sysroot and four additional official libunwind archives total 319,933,537 bytes.
An initial link failure exposed those omitted archives; the corrected fixture
build passed in 4.606 seconds, CPU 6.026 seconds, peak family RSS 110.4 MiB.
[Committed source fixtures and reproduction](https://github.com/morluto/rea/blob/main/tests/fixtures/android/jni-bindings/README.md)
build native declarations, javac headers and two libraries for arm64-v8a,
armeabi-v7a, x86 and x86_64. Ordinary REL/RELA, RELR and Android packed APS2
variants include overloads, BMP/supplementary names, explicit Modified UTF-8,
`.data` and `.data.rel.ro` tables, repeated exports in two libraries, an
unmatched declaration and two classes sharing one registered pair. Source,
`javac -h`, independent DEX flag/index reads, readelf and a separate byte/relocation
oracle establish expected facts. Oracle SHA-256 is
`fa634493bc8559a80eda3b70f01d44550c2f06e176cfc2a7cc7e10794b1eb793`.
| Static APK | SHA-256 |
| -------------------- | ------------------------------------------------------------------ |
| Ordinary | `c4eb50ef8bdecc6f56c454294c0c8f198fa51a4174d101dc720a849976546649` |
| RELR | `5a2ce56191f633575e87bd239702f988d0d1ad82795a203e13a4f907f8624658` |
| APS2 | `d2f0d613bc8f22ed1a6435f13b5ae1f088e904216aa8a04dd70c27c5cb606778` |
| Conscrypt derivative | `5985c40d5a6b14b64c202c80335d851adb7e8a987e6154a1488a194253856385` |
The Conscrypt derivative packages the public Android 2.8.0 AAR's original
library bytes with its class files transformed by D8 8.0.40. It is a static
test container, not a vendor APK or a claimed launchable application. The AAR
SHA-256 is `752044c289bc98e2dcabc00a1f88ca158b3aa3f505b38f124552bf4b2a0902b1`.
The original-name DEX is used, not an R8-renamed DEX paired with unchanged JNI
strings. D8's missing platform-adapter dependency warning remains a qualification
limit; no full application linkage or signature verification is claimed.
## Executed correctness and Evidence checks
The built pure parser passed independent checks over 25 real libraries:
24 genuine NDK outputs plus Conscrypt arm64. All 33 golden table entries match
their strings, slot addresses/file offsets, relocated values, encodings,
function addresses and code offsets. Conscrypt yields 367 structural entries;
the requested `EVP_PKEY_new_RSA ([B[B[B[B[B[B[B[B)J` candidate is `0xfa11c`.
Its name/signature/function slots are `0x2ada68`, `0x2ada70`, `0x2ada78`,
with relocated values `0x749a8`, `0x7affa`, `0xfa11c`. This built-artifact
qualification took 0.684 seconds, CPU 0.736 seconds, peak family RSS 99.5 MiB.
Real SDK/stdio MCP and CLI checks independently validate:
- All 12 source native declarations across four ABIs, exact library digests,
overloads, UTF-16 names and per-library alternatives. Ordinary/RELR return
32 matched, 12 ambiguous and four unknown rows. The source-known unmatched
method remains unknown because symbolic writes/NOBITS leave scanning partial.
- APS2 preserves 20 observed export matches, four ambiguous export rows and
24 unknown rows. It explicitly reports unsupported table relocation encoding,
retaining each selected library's reason even without a positive candidate.
- Class selection never hides the other class sharing a registered pair.
All selected native declarations survive independently of available ABI rows;
the no-library selection case is covered by a domain regression, not claimed
as a separate real SDK artifact qualification.
- All four retained collections support exact fields, empty filters and batch
ID retrieval. Parent-array `row_index` remains separate from raw DEX/ZIP
source ordinals. Advertised converted schemas validate with the real SDK/Ajv.
- Complete portable parents preserve the same normalized facts through summary
and CLI delivery. Ordinary and each Conscrypt cohort copy the source to an
exclusively created owned file, inspect it, remove only that copy, then read
retained summary, exact table slots, binding fields and a full export.
Original fixture bytes remain unchanged. Post-removal success is observed;
hidden internal engine activity is not directly instrumented by this harness.
The first three four-ABI cohorts execute 107 SDK calls and three cold CLI parity
checks. Each Conscrypt cohort adds 28 investigation calls, five separately
accounted retained-removal calls and one CLI parity check. Four alternating
Conscrypt cohorts preserve the same normalized fact digest:
`7e4738d2dd9e518277d5268ce5c334d9d25728198f66b2102cdc3411865f2d46`.
Raw parent transcripts show one native inventory RPC on the first investigation,
then only JVM-status RPCs on later main queries; normalized native facts remain
present. No JNI parent contains `load_apk`. Library reuse is owned by the same
immutable APK session; ABI/library selection replaces one scan, while class
and method selection reuses it. Fresh analysis admission still hashes current
caller APK bytes. Historical views never reopen them.
Type/lint/build and 396 tests across ten exact files pass together: native/domain
projection, producer parsing, real ZIP occurrence/cleanup boundaries, unchanged
JADX session/idle/cancellation behavior, public contracts and existing IL2CPP
decoding/ownership. The gate took 215.198 seconds, CPU 257.470 seconds, peak
family RSS 4396.1 MiB. A full real JDK/JADX metadata run additionally passed
native reads before any APK load, malformed/duplicate DEX and CRC negatives,
raw aliases, constants, absence facts, manifest identity and downstream CLI/MCP
bodies/direct-invoke parity from both DEX entries: 154.890 seconds, CPU 233.275
seconds, peak family RSS 984.8 MiB.
Finalization regressions retain already parsed library facts/digests in typed
partial observations before ZIP close. Caller/deadline-first cancellation,
failed close, blocked admission and retry preserve their first cause and exact
resource lease. Interrupted later libraries preserve earlier completed facts.
Stable table candidate identity uses physical pointer-slot address/file offset,
not inferred run grouping. Known ZIP size claims stay separate from verified
read size/hash.
## Completed-task costs
Earlier main cannot decode/register JNI tables with equivalent coverage, so
manual export lookup is not an equivalent before workflow. The controlled
comparison is complete versus summary delivery of the same new investigation,
same bytes, query, selected pages and proof export. Conscrypt trials alternate
summary/complete/summary/complete, each with one cold and one warm task.
Each fixture task has four investigations, 12 selected collection pages and a
proof export (17 calls). Each Conscrypt task has one investigation, the same
12 page checks and proof export (14 calls). Task time includes public calls and
local oracle checks; one-time startup/catalog, separate retained-removal
qualification, final cleanup and a cold CLI parity run are included in the
whole-process columns, not hidden in task timings. Ordinary includes removal
qualification; RELR/APS2 do not, so their whole-process costs are not a paired
performance comparison. All four Conscrypt cohorts use the same phase flags.
| Cohort | Cold task s | Warm task s | Whole s | CPU s | Peak family MiB | Warm text tokens | Warm SDK tokens |
| -------------------- | ----------: | ----------: | ------: | -----: | --------------: | ---------------: | --------------: |
| Ordinary | 11.058 | 0.606 | 32.658 | 45.124 | 1215.7 | 58,319 | 401,681 |
| RELR | 12.140 | 0.797 | 29.122 | 38.375 | 1266.2 | 58,231 | 401,502 |
| APS2 | 12.578 | 0.917 | 30.892 | 41.071 | 1268.0 | 50,900 | 386,634 |
| Conscrypt summary | 11.237 | 0.444 | 33.999 | 46.953 | 1282.0 | 32,265 | 349,128 |
| Conscrypt complete | 10.846 | 0.456 | 32.907 | 45.948 | 1398.3 | 224,954 | 737,723 |
| Conscrypt summary 2 | 19.205 | 1.200 | 57.545 | 77.644 | 1456.2 | 32,223 | 349,055 |
| Conscrypt complete 2 | 20.978 | 0.925 | 64.513 | 81.218 | 1502.0 | 224,526 | 736,950 |
Warm text estimates are about 85.7% lower for summary delivery. Cold text
estimates are 32,087/32,202 versus 247,593/247,912. SDK-inclusive warm estimates
are about 52.6% lower. Latency/CPU/peak-memory samples vary substantially and do
not establish a general improvement. Cold costs include the first generic
view-schema compilation in the client; subsequent tasks reuse it. Startup/
catalog alone ranges 2.692–4.955 seconds across these Conscrypt cohorts.
Token estimates use tiktoken 0.14.0/o200k_base. Every task counts its analyst
question, discovered input catalog once, arguments and all text replies,
including pages and export receipts. SDK totals additionally count output
schemas and structured-content duplication. Qualifications and CLI response
costs are reported separately by the cost script. Exported file contents are
verified locally and are not counted as model context unless actually returned
inline. Actual billing, hidden prompts, client rendering/pruning and OS cache
state are unmeasured.
### Latest-main confirmation
After merging main `d6f88becd` into `e8a8a5072`, type/lint/build and the eight
input-presentation/public-contract cases pass. The first direct schema-suite
invocation could not load its generated test catalog; `test:focused` supplied
the required artifacts and all 16 schema tests then passed (63.310 seconds,
CPU 72.830 seconds, peak family RSS 1943.5 MiB). No schema assertion was relaxed.
Fresh full Conscrypt SDK/CLI cohorts use the same proof, 12 pages and owned-copy
removal flags. Both retain the identical fact digest above:
| Delivery | Cold task s | Warm task s | Whole s | CPU s | Peak family MiB | Warm text tokens | Warm SDK tokens |
| -------- | ----------: | ----------: | ------: | -----: | --------------: | ---------------: | --------------: |
| Summary | 11.739 | 0.680 | 36.712 | 51.209 | 1395.1 | 31,932 | 348,522 |
| Complete | 11.161 | 0.459 | 34.692 | 48.319 | 1385.2 | 224,954 | 737,723 |
Summary text estimates remain about 85.8% lower. This pair does not show a
latency or peak-memory improvement. Each adds 33 SDK calls and one cold CLI
parity check, bringing the executed public qualification to 305 SDK calls and
nine CLI checks across the pinned cohorts.
`docs:check`, `format:check` and `docs:build` pass together: 44.365 seconds,
CPU 56.935 seconds, peak sampled family RSS 1300.6 MiB. Generated catalogs,
schemas, packaged skills and provider binaries remain uncommitted outputs.
All expensive checks use CPU affinity 0,1, bounded heaps and one active process
family, with an 8 GiB RSS guard and 4 GiB host-availability guard. Peak memory is
the sum of family RSS sampled every 100 ms: shared pages can be double-counted
and short peaks can be missed. No guard stop or OOM occurred.
### Default-summary delivery confirmation
The new MCP operation now defaults to retained `summary` when `detail` is omitted.
Explicit `complete` remains supported; the CLI retains its common explicit-detail
policy. The actual SDK regression verifies both modes, caller selectors and
reading an unselected declaration from the complete parent after source removal.
Type/lint/build and all five cases in that MCP boundary file pass: 80.012 seconds,
CPU 110.240 seconds, peak sampled family RSS 4747.6 MiB. An earlier attempt stopped
at the test-function length lint rule; the fixture helper was extracted before
this successful gate.
A fresh genuine Conscrypt workflow omits `detail` on JNI investigation calls and
passes 33 SDK calls, all 12 retained pages, proof export, one cold CLI parity
check and the separate owned-copy removal qualification. The same investigation
fact digest above is retained. Cold/warm task times are 15.608/0.481 seconds;
whole process-family time is 45.239 seconds, CPU 61.038 seconds, peak sampled RSS
1431.2 MiB. Warm text/SDK token estimates are 31,928/348,516 using the same cost
method. This adds a tenth CLI check and brings the successful public qualification
total to 338 SDK calls. Explicit-complete removal qualification remains separate
from the investigation task; default summaries do not discard the full parent.
Final `docs:check`, `format:check` and `docs:build` also pass: 77.269 seconds,
CPU 70.506 seconds, peak sampled family RSS 1191.1 MiB.
The updated harness adds `--omit-detail` (with `--detail summary`), SHA-256
`c2a5d090f32429c852e3c974083bb9c21a0dc6a608407687031f155a722b53d7`.
The earlier cohort tables use the original frozen harness below. Default-run
summary/catalog/call receipt SHA-256 values are respectively
`c10e4d702b9694b0ccc69926a4e1f688fa8dea33a3de2e2837ea15190a706b37`,
`0c36244f5ff788fabd5491144b0711e99ad211ce3c09e1812d1d4da5753624ac`, and
`e1f021f0a7a92f101a037a1467a3428b4c620482eb556d81501a5ea64226ab82`.
### Review corrections and final qualification
Four review findings were reproduced or confirmed at their producing boundaries:
- The shared relocation reader had charged every file-backed write against a
sparse retention limit. Its single immutable decoder now streams writes;
IL2CPP applies them to its existing private copy and retains only bounded
unresolved/zero-fill state and a file-offset bitmap. JNI keeps its bounded
sparse provenance index. The two actual IL2CPP registration-read regressions
each include 65,537 extra file-backed relative writes (AArch64 and x86-64)
and verify the original bytes remain unchanged.
- GNU version headers could replay shared auxiliary chains and names. The shared
export owner now bounds total record visits from section bytes and the existing
export-row allowance, caches decoded/failed string offsets within 16 MiB, and
yields checkpoints through the same generator used by synchronous callers.
Refusal retains exports with partial coverage and unknown version names.
Both GNU table variants exercise 10,000 headers replaying 65,535 auxiliary
records and a 65,535-byte name, verifying one decode and cooperative yields.
- A named mapped-string read-limit failure now distinguishes budget exhaustion
from invalid text. JNI preserves later candidates, marks tables partial and
projects absent candidates as unknown. Both long names and descriptors are
covered. An additional actual JNI-consumer regression queues cancellation
after GNU name decoding begins and stops before export-symbol decoding.
- The static verifier now requires the Conscrypt oracle only when expectations
advertise it. Executed controls pass with 25 libraries/33 entries when present,
and 24 libraries/32 entries for NDK-only expectations; an advertised missing
oracle rejects with ENOENT and writes no success receipt.
Type/lint/build and 330 cases in 13 related domain, parser, archive, IL2CPP and
actual CLI/stdio MCP files pass together: 192.437 seconds, CPU 224.376 seconds,
peak sampled family RSS 4190.2 MiB. After adding the during-version cancellation
case, type/lint and all 38 JNI parser cases pass: 39.842 seconds, CPU 67.581
seconds, peak RSS 3928.0 MiB. These overlap earlier gates; counts are not summed
as unique tests.
The final built parser again passes the independent 25-library/33-entry oracle:
0.975 seconds, CPU 1.009 seconds, peak RSS 103.9 MiB. Built parser SHA-256 is
`7c0638751427b2b282b8e55bb21c95f6c3f05c246b0ea66a31d0280694f9ffeb`;
its receipt also fingerprints every shared parsing dependency. The fixed oracle
SHA-256 above is unchanged. Large-capacity IL2CPP verification uses valid producer
fixtures, not a newly qualified real IL2CPP application.
A fresh Conscrypt investigation qualification again passes 33 SDK calls,
12 pages, proof, a cold CLI check and owned-copy removal. Investigation facts
retain the same digest above. Cold/warm task times are 23.529/1.015 seconds;
whole time 66.284 seconds, CPU 77.940 seconds, peak RSS 1402.6 MiB. Warm text/SDK
estimates are 31,955/348,569. Host/time variation does not establish a general
latency improvement. Cumulative successful public qualification is now 371 SDK
calls and eleven CLI parity checks, across the explicitly separated revisions.
The final cohort summary/calls SHA-256 values are
`84df4f4ed97f6d1a931010db8e16420e5f06df371940ed9225eb99a7a7c01094` and
`2c22b89e5ec4b26f15f0e9c5f8fdfd4af8583ad0926ad32edb359ecd991cd7fd`;
its catalog hash matches the default-summary cohort. No resource guard or OOM
occurred. Final `docs:check`, `format:check` and `docs:build` pass in
38.602 seconds, CPU 49.448 seconds, peak sampled family RSS 1286.2 MiB.
## Reproduction and limitations
Build the runtime, generate the source fixtures using selected existing JDK,
NDK and D8 paths, then run the maintained
[pure parser verifier](https://github.com/morluto/rea/blob/main/tests/support/analysis-efficiency/android-jni-library.mjs)
against the built parser. The
[public workflow harness](https://github.com/morluto/rea/blob/main/tests/support/analysis-efficiency/android-jni.mjs)
accepts `--variant ordinary|relr|aps2|conscrypt`, `--detail complete|summary`,
`--proof --pages --cli` and optional `--retained-removal`, explicit selected
repository/artifact/JDK/JAR/oracle paths and a fresh output directory. Frozen
harness SHA-256:
`14836895659de0f161c479b2b013ec39d754d172fd9935898872ab9cb5937035`.
The [cost script](https://github.com/morluto/rea/blob/main/tests/support/analysis-efficiency/android-jni-costs.py)
consumes every case's catalog, calls and summary receipts, fingerprints them
and keeps qualification costs separate.
Initial source integration type/lint errors were corrected before qualification.
A first CLI verifier attempt incorrectly treated its summary wrapper as flat
Evidence; the CLI had written its valid bundle. The corrected harness saves raw
stdout/stderr before assertions and validates the unwrapped named contract.
One driver used the wrong Conscrypt derivative filename and failed before
analysis; corrected cohorts use the pinned file above. A direct schema-suite
invocation lacked generated test catalog metadata; artifact-aware verification
is recorded separately. These attempts are not counted as successful runs.
Only Linux/JDK21/JADX0.7.1 and the fixed genuine NDK variants/public Conscrypt
arm64 sample are qualified. Cross-ABI ELF fixtures do not establish real
Windows/macOS provider lifecycle, device/emulator operation or runtime JNI
registration. Existing IL2CPP regressions cover the shared relocation refactor;
no new real IL2CPP application qualification is claimed. Runtime-built tables,
obfuscated strings, unsupported encodings and missing split/runtime-loaded code
remain unknown. Byte digests identify artifacts, not authenticity or build
ownership.
@@ -0,0 +1,366 @@
# Caller-selected Android mapping investigation — 2026-10-27
## Question, revisions and oracle
Apply a caller-selected local R8 mapping while preserving observed DEX identities,
original mapping-derived names/types, overloads, uncertainty, both artifact
identities and complete source Evidence. Inspect the requested inventory and
method bodies, then export complete Evidence. APKs and native code are never
executed, installed or modified.
Both controls use main `4e67bc399951768737eeaf539f7bc9fa8373b244`:
- Before: `3a588662d916f801a8c444dd86f1c65c81c76bf9`, the integrated
#7137 mapping implementation, without parsed-index reuse or retained symbol
summaries. This is a feature-equivalent optimization control, not a claim
that unmodified main already supported mappings.
- After: `6b65d3a4a1a8307417365b9a1e9060e607538aa3`, bounded/cached mapping
and explicit retained summary delivery. Later documentation changes do not
alter the measured implementation. Following main’s experiment archive, the
maintained harness/fixtures now live under tests and this report under
docs/verification; the original measured scripts remain available at that pinned revision.
- The original #7137 author is 555aaditya; integration retains their co-author
credit and preserves #7379's shared manifest contracts/default-package tests.
The [source fixture](https://github.com/morluto/rea/blob/main/tests/fixtures/android/r8-mapping/README.md) has four classes,
eight fields and fifteen methods, including constructors, overloads, Unicode
names, nested/default-package classes, arrays and renamed types in prototypes.
The same actual R8 invocation emits DEX and mapping. Handwritten source
expectations are checked against pre-minification `javap -s -p`; an independently
compiled dexlib2 reader inventories raw DEX identities without REA's parser,
bridge or JADX aliases. All four classes and fifteen selected bodies are checked
on each investigation. Seventeen valid method mapping records include three
line ranges for one constructor; they are not seventeen distinct DEX methods.
The public-library task transforms the shipped `classes.jar` from Conscrypt
Android 2.8.0 with actual R8. Its 949,641-byte mapping has 530 class, 1,263 field
and 10,342 method records. A separate D8 conversion of the original class files
provides the unminified descriptor oracle. Every selected `NativeCrypto` field
(28) and method (390), plus inner-class identity, is verified; bodies are selected
for `EVP_PKEY_new_RSA`, `EVP_DigestSignInit`, `SSL_new`, `SSL_free` and
`SSL_get_error`. These are static DEX-only ZIP containers, not runnable APKs.
D8 warned that a platform adapter's `com.android.org.conscrypt.OpenSSLSocketImpl`
was missing; complete dependency/runtime linkage is unverified. R8 additionally
used `-dontwarn **`, with shrinking/optimization disabled and name minification
allowed. Stable R8 did not consume an existing APK's DEX.
## Pinned artifacts
| Artifact | Observed SHA-256 |
| ----------------------------- | ------------------------------------------------------------------ |
| R8 8.0.40 Maven JAR | `ab1379835c7d3e5f21f80347c3c81e2f762e0b9b02748ae5232c3afa14adf702` |
| JADX MCP 0.7.1 JAR | `6e5eacf500b64292bfb73c49797c1958f6ee44646e43e868039ae7feb573ff75` |
| Android 36 platform JAR | `d9eb9da824d9e247a352f570f01e1169e725b2954bca9e283a71786c59b59f9a` |
| Source fixture input JAR | `88f3d46018ac2effb166ceed79fdd6e8b58b6bb08d2649cd0aa4f90dc260260c` |
| Source fixture DEX | `0b4c49cf3d706f5c2c4c15a10ff27dfbabb9b07f463d508f6f348eafb3245909` |
| Source fixture static APK | `f8c0849978ed7eceeeb293223cf2139c1749f672148dce1e16d018fa727445c9` |
| Source fixture mapping | `5ca162587794af1bd1da00f79ee64fd4ecde6f7c91048ea5596260104df6e483` |
| Conscrypt 2.8.0 AAR | `752044c289bc98e2dcabc00a1f88ca158b3aa3f505b38f124552bf4b2a0902b1` |
| Conscrypt shipped classes JAR | `2cd9b6bc444cfca69048e8327383b7f5a13fc7978465e6e0fd5111a2000822f6` |
| Conscrypt R8 DEX | `cd4d7eb9b05e1a84d064f7f9e4b33d68a8f92a9f58a6a62e4041972964ef6d27` |
| Conscrypt static APK | `9a0224400b91b030213e0f6ba63d899a4ad6333f930414c768ce88bbef423e91` |
| Conscrypt mapping | `67cd69e706afee50a1cc4e13bd9fd26056932c569d0ea7706dfe6fe8215a2e3c` |
R8 publisher and independent official Bazel checksum pin are linked from the
fixture README. Conscrypt was downloaded from
[Maven Central](https://repo.maven.apache.org/maven2/org/conscrypt/conscrypt-android/2.8.0/conscrypt-android-2.8.0.aar)
and verified against its publisher SHA-256 sidecar. No generated binary or
mapping file is committed. JAR/ZIP timestamps can change a source rebuild's
artifact hash; the table identifies these measured bytes.
## Owning changes and safe bounds
The provider keeps one successful parsed index. Each call still performs a
stable regular-file read and SHA-256 check. Only identical digest/length with
fixed parser options reuses parsed facts; current path provenance comes from
the new read. Changed content, including same-size/same-mtime rewrites, is
reparsed; missing or cancelled input never falls back to stale facts. Idle
retirement and close release the index. During replacement, old and new parsed
indices can briefly coexist. APK/query results are not cached by this mechanism.
The domain parser scans lines without a whole-file split, accounts retained
strings/containers before admitting records, and admits parameter/metadata
expansion before split/JSON parsing. The fixed 64 MiB input, estimated 128 MiB
index and 8 MiB per-residual-class claim allowances are resource constraints,
not format claims or measured V8 heap caps. Replaced metadata, deduplicated
range splits and overwritten inline frames are not cumulatively billed.
Ambiguity scans count all alternatives but retain only eight stable candidates;
conflicting residual metadata stays ambiguous. A separate combined 8 MiB claim
budget covers actual queries including all inner classes; omitted claims become
explicit `unknown`, preserving every raw identity and source SHA. It is not a
whole-report/wire-byte cap.
Existing shared retained-Evidence projection now supports class/method summaries
through both CLI and MCP. They preserve complete normalized facts and selected
bodies, omitting duplicate raw RPC metadata from the inline response. Complete
parent Evidence remains exportable. Default/complete delivery is compatible;
summary is explicit, and CLI requires a non-overwriting portable output bundle.
Whole summary views admit 1 MiB including context: an admitted one-MiB body may
therefore require complete detail. Refusal preserves the MCP parent ID and is
nonretryable for the same selection; no facts are silently truncated.
## Completed-task measurements
Linux x86-64, Node 24.22.1/npm 11.16.0, Temurin JDK 21.0.12.1, actual JADX
0.7.1, one analysis worker, 1 GiB JVM heap and two active processors. Harness,
server and children were restricted to CPUs 0/1; Node heap was 3 GiB. Expensive
checks ran serially under an 8 GiB sampled family-RSS stop and a 4 GiB host-free
memory guard. No guard stopped a completed task and no OOM occurred.
The [public stdio MCP harness](https://github.com/morluto/rea/blob/main/tests/support/analysis-efficiency/android-mapping.mjs) uses named live tool
contracts and Ajv 2020 validation of SDK-converted advertised outputs. All
correctness comparisons, hash reads, discovery and cleanup are included in the
whole-harness cost. First-provider calls use fresh processes/JVMs; OS file caches
were warm, and the host was shared. Conscrypt order was before/after/after/before.
Each small task makes 19 inspections and one proof export; each library task
makes six inspections and one proof export. Every exported parent is checked
against the exact inline normalized facts, source parameters and APK SHA, and
must retain raw calls. The complete/summary lanes use the same requested facts,
provider, options, source bytes, discovery families (`android,session`) and proof.
Peak memory is summed process-family RSS sampled every 100 ms, including client,
server, JVM and children. Shared pages can be counted twice and short peaks can
be missed; this is not PSS, kernel peak accounting or an enforced production
heap measurement. CPU is completed-family user+system time.
| Cohort | First task s | Warm task s | Whole harness s | CPU s | Peak family MiB |
| ----------------- | -----------: | -----------: | --------------: | -----: | --------------: |
| small-before | 7.342 | 1.611 | 12.060 | 16.680 | 781.7 |
| small-after | 7.960 | 1.842 | 12.945 | 17.413 | 791.5 |
| conscrypt-before | 8.057 | 1.015, 0.893 | 13.264 | 17.774 | 920.0 |
| conscrypt-after | 6.975 | 0.833, 0.817 | 11.743 | 17.242 | 855.0 |
| conscrypt-after2 | 6.987 | 0.725, 0.748 | 11.561 | 16.553 | 872.6 |
| conscrypt-before2 | 7.046 | 0.917, 0.886 | 11.855 | 17.093 | 949.7 |
Warm Conscrypt task medians were 0.905 s before and 0.783 s after (about 14%
lower across four warm samples per implementation). Cold/whole-task and memory
samples vary; no universal startup or memory improvement is established. The
small fixture pays extra summary/retention overhead: 12.060 to 12.945 seconds
for the whole harness, with CPU 16.680 to 17.413 seconds. Explicit complete
delivery remains available.
An earlier separate **cache-only** cohort used complete responses without proof
export, on the same base. Conscrypt warm six-call medians fell from about
0.981 to 0.765 s; before ranges were 0.950–0.996 s and after 0.743–0.850 s.
Whole harness samples were 12.861–13.876 to 11.641–11.647 s, CPU
16.916–18.297 to 16.454–16.893 s, and peak family 910.1–938.1 to
853.1–876.8 MiB. Small before/after whole costs were 18.493/16.110 s,
17.495/16.589 CPU s and 763.4/776.3 MiB. These samples motivated reuse but
are not substituted for the completed proof-export task above.
## Whole-task token estimates
[android-mapping-costs.py](https://github.com/morluto/rea/blob/main/tests/support/analysis-efficiency/android-mapping-costs.py) executed with tiktoken
0.14.0/o200k_base. Text projection counts a fixed task question, all discovered
name/description/input schemas once per investigation, every RPC argument and
all text replies, including proof export. SDK transport additionally counts
output schemas and structured-content duplicates. Exported file contents are
checked offline rather than injected into model context; export IO/CPU/memory
is included above. Prompt replay, host pruning/rendering, hidden prompts,
protocol handshake/framing and actual model billing are unmeasured. Estimates
are not billed usage. Each table row is one warm completed investigation.
| Cohort | Estimated text tokens | Estimated SDK transport tokens |
| ----------------- | --------------------: | -----------------------------: |
| small-before | 43,526 | 167,094 |
| small-after | 41,755 | 159,507 |
| conscrypt-before | 180,213 | 510,351 |
| conscrypt-after | 69,836 | 217,646 |
| conscrypt-after2 | 69,908 | 217,789 |
| conscrypt-before2 | 177,902 | 505,723 |
Conscrypt text estimates fall about 61%, from approximately 178–180k to 70k,
while preserving all requested facts and complete proof. Small tasks improve
only about 4%; summary context has its own cost. The cache-only cohort kept
similar token counts (~172–174k library text tokens without proof export),
revealing that parse caching alone did not address repeated raw class metadata.
Full inventories still cost more context than a direct known-method summary.
Normalized investigation digests match exactly across all before/after lanes:
- Source fixture: `ec76d1e086c8561370fc1ed5dfef67bb88b185e8fb57c7e30afe9f1f153634eb`.
- Conscrypt: `7ebf70795dfc23bf2b0d0e813837325fa221f15560ca2384fdb06f26d69d78a5`.
## Post-review ownership and final workflow confirmation
Greptile found three concrete boundaries subsequently fixed in
`160573cb36f478a088b8a5284fa571cd97dc9a86`: translated prototypes could expand
before comparison; a warm request without a mapping could bypass a pending
mapping descriptor's cleanup; and timeout classification inspected the outer
cleanup wrapper. Type matching now compares incrementally within the observed
descriptor, retained file cleanup gates shared request admission and idle
retirement independently of engine cleanup, and the first interruption is
classified before cleanup composition. All 126 related cases pass serially:
55 parser, five file-reader, four first-cause/close outcomes, three cleanup,
52 session/idle/cleanup and seven MCP mapping cases.
A separate bounded diagnostic found and corrected source-string retention.
On Node 24.22.1/V8 13.6.233.17-node.58, four small selected class/method results
from four distinct mappings containing 10 MiB of ignored comments each retained
41,944,028 extra external string bytes through the actual immutable Evidence
ledger. Clearing it released that backing; detached-string controls retained
zero extra external bytes. The parser now owns admitted UTF-16 strings once,
sharing section names and copying only retained outermost/deduplicated facts.
It preserves lone surrogate code units and never clones the entire index or
copies names per lookup. The identical post-fix ledger experiment retained
zero extra external bytes; its RSS delta was 151,552 bytes, versus 62,898,176
before. These are engine-specific observations, not portable heap guarantees.
The unchanged Evidence/ledger modules and changed parser are fingerprinted by
[the diagnostic](https://github.com/morluto/rea/blob/main/tests/support/analysis-efficiency/android-mapping-retention.mjs).
Before/after whole diagnostic costs were 2.995/2.582 seconds, CPU 4.976/4.382
seconds and peak sampled family RSS 145,903,616/113,287,168 bytes, with explicit
256 MiB old-space selection and a 1 GiB family guard.
Final equivalent Conscrypt investigations compare the integrated unoptimized
control `5ecc6ccc662678c00a45bd171f40798634774155` with fixed `160573cb3`, both
on main `e99b6c2c3bcc5f4f050b51b432145144d0d47dc0`, using the same artifacts,
facts, seven calls including full proof export, and before/after/after/before
order. Every normal-fact digest remains
`7ebf70795dfc23bf2b0d0e813837325fa221f15560ca2384fdb06f26d69d78a5`.
The final source-fixture summary/proof/negative qualification also passes all
43 calls from the maintained harness path.
| Final cohort | First s | Warm s | Whole s | CPU s | Peak family MiB | Warm text estimate |
| ----------------- | ------: | -----------: | ------: | -----: | --------------: | -----------------: |
| conscrypt-before | 7.187 | 1.027, 1.006 | 12.370 | 17.307 | 917.7 | 177,855 |
| conscrypt-after | 7.376 | 0.813, 0.866 | 12.402 | 17.522 | 868.7 | 69,819 |
| conscrypt-after2 | 6.878 | 0.707, 0.726 | 11.439 | 16.383 | 883.4 | 69,902 |
| conscrypt-before2 | 7.111 | 0.989, 1.186 | 12.566 | 17.479 | 930.1 | 177,719 |
The final warm ranges were 0.989–1.186 seconds before and 0.707–0.866 after;
text estimates remain about 61% lower, ~177.7–177.9k to ~69.8–69.9k. Whole-task
and cold samples overlap and are retained alongside the original cohort above;
these measurements do not establish a general startup or memory advantage.
### Follow-up complexity review
Two additional review findings exposed repeated linear work despite bounded
retention. The parsed owner now records one agreed residual alias (or explicit
conflict) per original type, while preserving every class record and its count.
Method identities have a direct residual-signature index; missing signatures
still merge into the first record, including the initial unsigned-to-signed
transition. Superseded alias arrays and candidate searches were removed.
The representation allowance includes the new index containers/entries.
The reproducible
[complexity diagnostic](https://github.com/morluto/rea/blob/main/tests/support/analysis-efficiency/android-mapping-complexity.mjs)
compares the built parser from `160573cb3` with the follow-up parser SHA-256
`9c4c24f7a10a17b8ec08e74b2588b9ba87bc30f3e3d16690c3e07c03fbce233b`.
The copied before module only rewrites its three unchanged dependency imports
to absolute URLs. Each scenario verifies late matches, counts and answer digest;
answers match across revisions. Node 24.22.1, two CPUs, 512 MiB old space,
8 GiB sampled family guard, one before/after trial per size:
| Scenario | Revision | Parse ms | Lookup ms | Whole s | CPU s | Peak family MiB |
| ----------------------------------------------------- | -------- | -------: | --------: | ------: | ----: | --------------: |
| 20k identical aliases, 1k candidates × 255 parameters | before | 212.60 | 5169.61 | 5.635 | 5.690 | 149.3 |
| same | after | 207.31 | 22.84 | 0.449 | 0.559 | 117.1 |
| 40k distinct signatures of one original method | before | 8032.93 | 5.70 | 8.300 | 8.371 | 117.5 |
| same | after | 321.58 | 5.43 | 0.562 | 0.532 | 117.8 |
The smaller alias cohort (1k aliases, otherwise identical) looked up in
289.18/24.64 ms before/after. The smaller signature cohort (10k) parsed in
785.65/89.68 ms. These synthetic cases establish removal of the reported
growth, not universal latency or memory gains; they involve no agent tokens.
All 60 parser, two report and seven public MCP mapping cases pass serially,
along with type/lint/build (79.084 s, CPU 110.273 s, peak family 4393.9 MiB).
The final indexed implementation also passes the real source fixture's
43 summary/proof/negative calls (13.838 s, CPU 18.212 s, peak 797.5 MiB),
and the Conscrypt 21-call investigation including proof export. The latter's
normal-fact digest remains the value above: first task 7.670 s, warm tasks
0.860/0.981 s, whole 12.706 s, CPU 17.870 s, peak 868.8 MiB. Warm whole-task
text estimates are 69,940, SDK-inclusive 217,975; tokenizer/scope remain the
same. Complete class/method facts and provenance remain accessible.
For CI head `160573cb3`, all failed/cancelled shard logs and source-test logs
were audited. Its executed merge `ee2398458bd8c56666ecaf120c1c3c21f350eea4`
uses main parent `0676ad254c206871e0f4b120ba300cbbf9383b62`. The 90 observed
failed test identities were selected on a pristine worktree at that exact
parent: 88 reproduced in the primary run (128.320 s, CPU 147.775 s, peak
2593.3 MiB), then the two newline-bearing ZCode titles reproduced in an exact
follow-up (2.388 s, CPU 2.788 s, peak 416.9 MiB). No feature code was present
in either control. These are baseline failures; cancelled cases without a
completed result remain unverified by CI, with local verification above.
## Executed verification and limits
- Parser/reader/protocol/provider mapping regressions cover raw names, exact
descriptors/types, overloads, inline outermost frames, malformed/partial
records, stable candidate samples, preallocation refusal, repeated metadata,
cache freshness/path aliases/deletion, cancellation and descriptor ownership.
- Real R8 source and public-library stdio MCP tasks verify all selected symbols
and advertised schemas. Actual CLI complete and summary inspections verify
raw/mapped identity, exact normalized parity and complete portable parents.
- Actual Java metadata regression and downstream CLI/MCP body/direct-invoke
parity pass, including reserved raw names, frozen aliases, default packages,
encoded constants, native absence and classes.dex/classes2.dex identity.
- Summary regressions preserve partial/unknown facts and optional historical
omissions, validate SDK output contracts, read retained data after APK/mapping
deletion, reject missing portable CLI output and exercise the one-MiB boundary.
- Type/lint/build, named schema/profile checks and relevant source/boundary checks
are executed separately. Final PR validation records the current command set.
The final combined 12-file Android run passed 219 of 220 cases, including all
52 session/idle/cleanup integration cases. One existing package-summary SDK
case hit its 30-second limit under combined load. The unchanged four-case file
then passed with one worker: its SDK case took 11.479 seconds; whole isolated
harness 31.867 seconds, CPU 35.064 seconds and peak family 1,551,081,472 bytes.
No assertion or timeout was relaxed. The combined timeout remains a recorded
resource/concurrency limitation rather than being reported as an all-green run.
Earlier failed attempts are retained rather than counted as passes: a resolver
refactor used `only.mapped` after `only` already held the mapped fact; its
regressions failed and were fixed. Tagged-error migration exposed one stale
string-only test. New SDK schema test typing and a redundant JSON preadmission
were corrected at their owning boundaries. A first summary MCP case timed out
while enumerating unrelated full catalog contracts; selecting the actual
Android/application/session families preserves every assertion and passes
without raising timeouts. An initial library harness requested absent
`SSL_shutdown` and skipped it; the harness now rejects absent requests, and
reported final cohorts explicitly verify all five requested methods.
The mapping cancellation regression distinguishes caller-first from deadline-
first finalization; the original implementation relabelled caller cancellation
as timeout (one failing caller-first case and one passing deadline-first case
on the unchanged optimization control). An initial test's public deadline code was corrected to the actual
`provider_timeout` contract. Fixed code preserves the first cause and cleanup.
Only Linux x86-64/JDK21/JADX0.7.1 and R8 8.0.40 mapping-format 2.1 were qualified.
Residual-signature conflicts use derived negative mappings, not a real mapping-
format 2.2 producer. No Windows/macOS, emulator/device, app execution, full APK
rebuild/linkage, source/line recovery, APK signature verification or model-usage
trial is claimed. Mapping/build association remains explicitly caller-selected.
## Reproduction
Generate source/DEX/oracle using the fixture README with selected existing tools.
Keep separate before/after worktrees, pin their revisions above, build both and
use identical target/mapping/oracle bytes. Then run the same harness for each:
```sh
node tests/support/analysis-efficiency/android-mapping.mjs \
--repository "$SELECTED_REPOSITORY" --target "$STATIC_APK" \
--mapping "$MAPPING_FILE" --output "$FRESH_RESULTS" \
--java-home "$SELECTED_JDK" --jar "$JADX_MCP_JAR" \
--oracle "$RAW_DEX_ORACLE" --expectations "$ORIGINAL_EXPECTATIONS" \
--proof
```
Add `--detail summary` only on the after implementation. The small fixture
selects all classes/bodies. For Conscrypt add `--class-name U0 --repeats 3` and
`--methods EVP_PKEY_new_RSA,EVP_DigestSignInit,SSL_new,SSL_free,SSL_get_error`;
`U0` is the measured artifact's raw class identity, not a universal residual name.
Use the original NativeCrypto D8 descriptor inventory for that expectation file.
Run `--variants` separately on the source fixture to exercise derived malformed,
partial, duplicate and conflicting mappings; those extra qualification calls
are not part of the timing/token table's repeated task.
```sh
python tests/support/analysis-efficiency/android-mapping-costs.py \
--output "$TOKEN_RESULTS" "$BEFORE_RESULTS" "$AFTER_RESULTS"
```
Capture elapsed/CPU/family RSS with the bounded host runner or an equivalent
process-family sampler; alternate trials and keep each label's raw receipts and
failed attempts. Complete Evidence stays in the exported per-pass bundles.
File diff suppressed because it is too large. Load diff
@@ -4,10 +4,14 @@ Issue [#4772](https://github.com/morluto/rea/issues/4772). Qualified runtime
`b49ee40bf330b7bf1c5180b14dcc104c1c613a8e`, tree `0e5f8fe2feae1796c340c07c26e0e28e5d4328d8`,
integrates main `c8ed32853881b08aa83459d4da55a28a4e09b0c8`. Recorded at
`2026-10-22T08:30:58.269Z`. Subsequent verification-only
commits change these reports. The [JSON record](javascript-route-inventory-2026-10-21.json)
commits updated these reports. The [archived JSON record](https://github.com/morluto/rea/blob/7d0efef62d56d0a10134bea62d709302daa1565f/docs/verification/javascript-route-inventory-2026-10-21.json)
pins original inputs, exact commands, profiles, failed controls and historical revisions.
Current-head GitHub CI and automated review must be checked separately.
The full JSON archive is 2,681,223 bytes, with SHA-256
`9d7ea2582a6f33ff0741578aec9f01b9327b3256ce50c582437164868e544d51`. It remains available in Git
at the linked immutable revision; this page retains the historical summary.
The latest main contracts, selected environment and ASAR admission remain shared
by CLI and MCP. The staged-code hook runs lint fixes before formatting, resolving the
producing order behind the earlier CI format failure.