Compare commits

...
Author SHA1 Message Date
Kaoru0822andClaude Opus 5.5 f5b01ef756 test(javascript): assert parameter-only request targets skip the member index
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 01:47:15 +00:00
Kaoru0822andClaude Opus 5.5 3ebab7ab45 perf(javascript): index member effects only for resolvable request targets
Static analysis built the whole-source member-mutation index whenever a file
had a pending fetch/XHR/WebSocket target. Only a target with a stable earlier
initializer can resolve, and that proof reads definition facts alone, so check
it first and build the index only when such a target needs its effects. Every
other request stays partial without the index.

On pdf.js's openjpeg_nowasm_fallback.js the static phase drops from 242 s and
4.2 GB RSS (heap exhaustion in the worker) to 0.6 s, so its static facts are
retained again. Static facts are byte-identical across 126 real files.

Refs: #2498

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 01:26:19 +00:00
morluto 1bd5c5c87e fix(adb): retain and resolve owned hash cleanup
Keep exact returned hash and directory failures linked to their cleanup owners. Retain input staging while its descriptor remains unresolved, and resolve the linked errors only after successful release. Preserve typed cleanup and partial acquisition facts through file, package, and bugreport consumers instead of wrapping or flattening them away.

Validated 189 affected tests across 13 files, build, typecheck, lint, and docs. Three larger integration cases were excluded to respect the CPU limit. New real-file fault controls reproduce on the old source and verify persistent failure, owner identity, cancellation, retained output, and recovery. No physical ADB device or real JADX engine verification. Refs #4266, #4274.
2026-10-16 09:06:26 +08:00
morluto 6e74704f93 fix(android): resolve hash cleanup through its owner
Bind the typed hash failure to its retained descriptor and resolve cleanup uncertainty only when that exact owner closes. Preserve the original failure through immediate retirement and later shutdown recovery. Controlled real-file regressions cover immediate and persistent close faults; no real JADX engine was launched. Refs #4266, #4274.
2026-10-16 09:06:19 +08:00
morluto 3ed925e547 fix(errors): exclude recovered cleanup resources
Compose only unresolved primary resources into a later cleanup failure, retaining both original causes and diagnostics. Document exact-owner recovery and cover resolved and unresolved composition controls. Refs #4266, #4274.
2026-10-16 09:06:13 +08:00
morluto ca7ee018b2 fix(providers): own shared startup and retained cleanup
Give concurrent Ghidra and Hopper callers independent waits over one owned startup. Join final-caller cleanup, preserve failed cleanup owners, and derive readiness from the current startup result and live transport. Distinguish provider closure from caller cancellation and clean up interrupted final lineage observation.

Reuse the Ghidra socket fixture, replace stale cancellation health assertions with cold and warm reuse controls, and cover public MCP peer cancellation. Validation: 315 affected tests, build, typecheck, lint, docs, and 25 relocated startup controls passed. Controlled child bridges only; real Ghidra JVM and Hopper GUI verification remains unperformed. Refs #3983, #4227, #4274.
2026-10-16 08:51:11 +08:00
morlutoandVinícius DS Andrade 808339f0b1 fix(native): distinguish undecoded Swift spelling adjustments
Recognize known undecoded producer spellings without treating them as successful demangles. Preserve actual producer output and derive symbol types from the domain contract instead of duplicating its validation schema.

Compare retained output with the real demangler through MCP, while preserving CLI malformed-input and line-ending coverage.

Refs: #4215, #4287

Co-authored-by: Vinícius DS Andrade <95101505+viniciusdsandrade@users.noreply.github.com>
2026-10-16 08:26:19 +08:00
morlutoandHenglie 2f6b9ecdc6 fix(native): preserve slice identity when selecting UUID rows
Carry the selected slice name and the existing tool owner's dwarfdump spelling separately. Compare observed row labels exactly, preserving explicit ABI versions and uncertainty for missing rows.

Correct the production-boundary fixture and retain wrong-version and mixed-row controls. Real Apple multi-version selection behavior remains unverified.

Refs: #4195, #4288

Co-authored-by: Henglie <ebhenglie@gmail.com>
2026-10-16 08:26:18 +08:00
morluto 5f0d18a534 fix(ghidra): retain request limits and startup cleanup
Preserve the full request options through the FIFO and settle interrupted or rejected final lineage observation through owned startup cleanup. Keep the startup diagnostic checkpoint until finalization succeeds.

Validate typed expired ping, cancellation, child exit, runtime removal, retry, and MCP recovery without running a native Ghidra instance.

Refs: #4289, #4274
2026-10-16 08:26:18 +08:00
morlutoandHemang Varshney 018ab2c5d9 fix(javascript): retain unresolved network targets with provenance
Resolve stable lexical and URL targets using shared effects, and preserve unresolved request expressions as partial facts. Carry their uncertainty through workers, artifact graphs, and the CLI. Scope partial endpoint identity to the source site.

Refs: #3451, #3534

Co-authored-by: Hemang Varshney <218273197+hemv-857@users.noreply.github.com>
2026-10-16 08:14:52 +08:00
morlutoandGeronimo 1b9b725c4c fix(javascript): unify loader binding and mutation ownership
Model the CommonJS wrapper binding and query shared lexical identity, mutation, and evaluation timing at each call. Remove duplicate importer interpretation and preserve uncertainty for with scopes, prototypes, repeated writes, and opaque object escapes.

Validate generated fixtures against bounded Node oracles; no analyzed target execution is added.

Refs: #3982, #4072

Co-authored-by: Geronimo <varungore1@gmail.com>
2026-10-16 08:14:52 +08:00
morlutoandGeekwolf b67baede2e fix(electron): unwrap transparent inline window options
Use the shared expression owner for BrowserWindow options and webPreferences. Retain overwrite and spread uncertainty through CLI and MCP evidence.

Refs: #4250, #4281

Co-authored-by: Geekwolf <geekwolf@163.com>
2026-10-16 08:14:51 +08:00
morlutoandGeekwolf 38f92d108e fix(browser): match exact WebMCP receiver segments
Inspect AST property segments instead of flattened suffixes. Reject dotted-key and suffix collisions while retaining page-declared trust and source evidence through the CLI.

Refs: #4205, #4279

Co-authored-by: Geekwolf <geekwolf@163.com>
2026-10-16 08:14:51 +08:00
b3389a5b08 fix(mcp): preserve nested JSON values in compact array schemas
Give array unions an unrestricted item schema and validate converted tools/list schemas for both advertised profiles. Keep client constraints separate from JSON Schema validity.

Refs: #3838, #3921, #3842

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: 冰水泡枸杞 <xingzihai@users.noreply.github.com>
2026-10-16 08:14:50 +08:00
morlutoandalfredo1491 4fd28fc9e0 fix(setup): preserve JSONC trivia when removing registrations
Remove properties and their separators using parsed JSONC token spans. Preserve unrelated comments, BOM, CRLF, backups, and repeated removal behavior.

Refs: #4244, #4284

Co-authored-by: alfredo1491 <208325033+alfredo1491@users.noreply.github.com>
2026-10-16 08:14:50 +08:00
morlutoandSyed Rishad d52a488476 fix(ghidra): retain scoped failures through process finalization
Drain output within caller bounds at asynchronous completion owners and classify JVM exhaustion from the failing operation only. Preserve raw-byte scope, credential redaction, configured heap provenance, cancellation and exact cleanup failures. Validation uses controlled processes and provider fixtures; native Ghidra OOM execution was not repeated.

Refs #4241, #4275

Co-authored-by: Syed Rishad <rishadzzzz@gmail.com>
2026-10-16 07:25:23 +08:00
morlutoandLiu Jie 43bc6e1d23 fix(javascript): preserve canonical rest-array property keys
Reuse the semantic array-index parser so leading-zero keys stay unresolved instead of selecting positional arguments. Preserve canonical indices, literal fallback and dynamic-key uncertainty.

Refs #4242, #4272

Co-authored-by: Liu Jie <maxazure@gmail.com>
2026-10-16 07:25:23 +08:00
morlutoandogutdgn 89dd71f0f0 fix(analysis-view): preserve native relationship coverage
Carry the parent native coverage facts through inline and retained views independently of row-page exhaustion. Preserve uncertainty, optional diagnostics and source Evidence.

Refs #3891, #4263

Co-authored-by: ogutdgn <79801848+ogutdgn@users.noreply.github.com>
2026-10-16 07:25:22 +08:00
morlutoandY0UW1SH a05092726f fix(go): distinguish dependency coverage from parse completeness
Explain unknown dependency coverage when embedded metadata has no main-module record. Preserve decoded dependencies and distinguish GOPATH-style omissions from observed module-mode empty lists.

Refs #4191, #4267

Co-authored-by: Y0UW1SH <29498139+Y0UW1SH@users.noreply.github.com>
2026-10-16 07:25:22 +08:00
morlutoandPolly Labs 79e83cbd67 fix(setup): resolve empty Claude profile paths consistently
Use the default home registration and personal skill roots for an empty profile override. Share marker and skill path resolution while preserving nonempty caller paths, backups and idempotence.

Refs #4262, #4270

Co-authored-by: Polly Labs <pollychen.lab@gmail.com>
2026-10-16 07:25:21 +08:00
morlutoandLiu Jie 862a0b2631 fix(artifacts): reject colliding extraction paths before writing
Preserve duplicate archive occurrences in inventory and reject an ambiguous materialization before acquiring readers or output resources. Keep path collisions distinct from integrity failures.

Refs #4271, #4273

Co-authored-by: Liu Jie <maxazure@gmail.com>
2026-10-16 07:25:21 +08:00
morluto 839410b663 fix(session): report retained file and candidate cleanup
Report cleanup from the owning snapshot and provider-candidate resource scopes without retrying or starting work. Keep status accurate before a client exists and through cleanup retry.

Refs #4277, #4274
2026-10-16 07:25:20 +08:00
morluto 93c620c3e7 fix(browser): own transport cleanup through finalization
Own CDP transports from admission through observed close, retain failed
owners for retry, and keep active peers separate from pending cleanup.
Preserve primary errors and completed facts through final cancellation.
Forward passive Electron CLI cancellation and dispose browser, runtime and
scenario owners from CLI and MCP shutdown.

Refs #4255, #4256, #4257
2026-10-16 06:46:26 +08:00
morluto 3b698645a0 fix(evidence): retain completed observations on late cancellation
Validate completed reports before cancellation projection, retain valid
Evidence and captured command diagnostics, and convert raw cleanup partials
at their application owner. Record partial Evidence consistently across MCP
handlers. Keep page inspection separate from unfinished bundle analysis.

Refs #4261
2026-10-16 06:46:25 +08:00
morluto 131fd5ddcd fix(filesystem): retain stable hash cleanup owners across callers
Preserve completed digests and primary failures when descriptor close fails.
Carry the exact handle owner through provider, readiness, cache, doctor and
verifier boundaries, and retry cleanup without rehashing or retrying a raw fd.
Close candidate-owned resources even when no target client was opened.
Keep verifier workspaces when provider shutdown remains incomplete.

Refs #4266
2026-10-16 06:46:25 +08:00
morluto 034293df14 fix(docs): resolve link roots before containment checks
Normalize repository and generated-site roots at the owning boundary so
symlink aliases do not reject valid local links. Preserve missing-fragment
and escaping-path rejection.

Refs #4264
2026-10-16 06:46:24 +08:00
morluto 139d86b05c fix(verifiers): report adb operation and cleanup failures together
Close the provider on every verifier path and report primary and cleanup
failures before printing success. Preserve operation failures when shutdown
also fails instead of swallowing cleanup or exiting before it completes.

Refs #4260
2026-10-16 06:46:24 +08:00
morlutoandDeryFerd c7b70896b1 fix(dev): retain exclusive ownership through command shutdown
Terminate the directly owned Windows command on cancellation and retain the
SQLite lock until child close. Record post-spawn errors without releasing
ownership early or leaving later error events unhandled.

Refs #4258

Co-authored-by: DeryFerd <deryferdikao125@gmail.com>
2026-10-16 06:46:14 +08:00
morlutoandCodex 6183544e8b fix(javascript): select timer handles at cancellation reads
Use the shared binding-at-read owner instead of grouping schedules across the
whole binding lifetime. Match selected initializer nodes, retain uncertain
origins as candidate links, and preserve immutable closure captures.

Adapt the diagnosis in PR #3794 by @Xinglan233, whose source commit was authored
by Codex. Verify stale writes, later schedules, replacements and CLI graph links.

Refs #3358, #3794

Co-authored-by: Codex <codex@openai.com>
2026-10-16 06:06:11 +08:00
morlutoandabdooo2235 ebd5a82c43 fix(web): read WebMCP inputs from schema properties
Remove the fallback that interpreted schema metadata as parameter names.
Preserve exact literal property keys, including empty names, without inventing
names for missing or dynamic properties. Keep this an observed name projection.

Refs #3331, #3765

Co-authored-by: abdooo2235 <abdalrhmantheiv15@gmail.com>
2026-10-16 06:06:07 +08:00
396b89c5e2 fix(javascript): attribute service workers to intact platform paths
Check the complete navigator.serviceWorker.register identity through the shared
ambient-write owner, including replaced methods and aliased receiver mutations.
Reuse the module URL reader for intact registrations while preserving unknown
document-relative strings. Cover the unresolved review case and the CLI graph.

Refs #3841

Co-authored-by: Chandan Chaudhary <chandankkumar156@gmail.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-16 06:06:06 +08:00
morlutoandYi Han 880b3a510b fix(javascript): bound static getter reference expansion
Replace recursive getter histories with a query-owned worklist and memo. Bound
precise expansion at 512 states and widen growing receiver cycles; retain
escaped roots and constructed children rather than silently dropping suffixes.
Carry the resource limit through semantic coverage, worker transport and graph
Evidence while preserving direct writes, imports and unrelated literal facts.

The small source probe reduced 65/326 paths to one widened reference for five
and six getters. The original large Claude artifact was not rerun. Validate
receiver/alias/mutation controls and compiled CLI/MCP consumers.

Refs #3839, #3848

Co-authored-by: Yi Han <yhan606@aucklanduni.ac.nz>
2026-10-16 06:06:00 +08:00
morluto 461eb16128 fix(adb): retain cancellation at screen publication
Stop before publishing when screencap returns after cancellation. If the file
has already committed, preserve its path, digest and dimensions in cancellation
Evidence. Reuse the execution-to-Evidence owner and remove a duplicate check.

Validated both sides of publication using a valid PNG and real temporary
filesystem writes; provider and process replies are simulated.
Refs #4251.
2026-10-16 05:44:20 +08:00
morluto 1342127e91 fix(adb): bind transfer evidence to consumed snapshots
Push and install now consume private copies whose stable hashes identify the
transferred bytes. Preserve original caller paths, remote side-effect receipts
and cancellation through final cleanup. Retain failed snapshot owners in the
existing directory cleanup lifecycle and remove redundant stored identity.

Validated path replacement through real subprocess stubs and a public CLI
negative/positive reproduction, plus capacity, cancellation and cleanup retry.
The small cold/warm CLI comparison does not establish a speed improvement.
Real Android devices and Windows private-root behavior were not exercised.
Refs #4252.
2026-10-16 05:44:12 +08:00
morluto 8873b39770 fix(mcp): retain shutdown signals until cleanup succeeds
Give process signal registration a named owner and keep SIGINT/SIGTERM
listeners until successful cleanup unregisters them. Reuse existing shutdown
coalescing and failed-owner retry behavior.

Validated repeated signals with mixed cleanup results through the production
registration boundary. No real OS signal integration claim.
Refs #4253.
2026-10-16 05:43:39 +08:00
morluto 321cacb84a fix(wasm): retain cancellation through final cleanup
Observe cancellation after releasing the private runtime root and preserve
completed Evidence. Keep cleanup failure, cancellation and retry ownership
separate. Construct partial Evidence only when the outcome needs it.

Validate both successful cleanup and abort plus cleanup failure/retry. Type
the scenario tag map and give cleanup assertions live counters to prevent
missing-tag and stale-counter test debt. Tests use simulated WABT executables.
Refs #4254.
2026-10-16 05:43:31 +08:00
morlutoandLiu Jie 6b991d3aa7 fix(electron): snapshot script frame identity at ingestion
Keep each parsed script's observed frame after context destruction or ID reuse.
Remove the later context lookup so record identity and deduplication use the
same historical fact. Unknown and unauthorized frame mappings remain null.

Validated event-to-record behavior with real local files and simulated CDP
events, plus existing provider tests; no real Electron engine was exercised.
Refs #3823.

Co-authored-by: Liu Jie <maxazure@gmail.com>
2026-10-16 05:43:24 +08:00
morlutoandShitK 0ef938f4c5 fix(javascript): version syntax-aware structural fingerprints
Retain bigint/directive values, assignment operators, fixed public keys and
execution-affecting syntax flags in v3. Preserve local/private binding renames
and isolate structural matching by algorithm version. Remove the redundant
test grouping and document structural matching as heuristic evidence.

Validated AST, worker admission, archived-version matching, reconstruction and
public CLI comparisons. Auto-accessors are tested at the AST boundary because
the production JavaScript parser does not enable their Babel plugin.
Refs #3952, #4020.

Co-authored-by: ShitK <1724059440@qq.com>
2026-10-16 05:43:14 +08:00
c0d550b231 fix(reference): resolve runtime imports from their owning syntax
Distinguish call-form require from TypeScript import-equals before resolving
inventory paths. Probe CommonJS files before indexes and honor package main
without requiring an index. Retain selected manifest directory identities and
reuse the path lookup owner for canonical ambiguity.

Validated with source/reader tests and a public CLI fixture controlled by the
pinned Node loader. Windows no-follow reader behavior remains unverified.
Refs #3895, #3896, #3973, #4017.

Co-authored-by: hhang <169027046+xu91102@users.noreply.github.com>
Co-authored-by: IssueHunter Worker <issuehunter@users.noreply.github.com>
2026-10-16 05:43:04 +08:00
morlutoandhhang 2202e3f0ba fix(apple): preserve exact Interface Builder plist integers
Reuse byte-derived numeric metadata after graph classification so emitted
attributes preserve exact integers without changing UID links or identities.
Reserve metadata and attribute projection within the shared decode budget.

Validated six CLI cases covering XML, binary, collisions, real values and
incomplete metadata, plus decode-budget regressions. A small cold/warm CLI
comparison showed similar costs; it does not establish a speed improvement.
Refs #3876, #3972.

Co-authored-by: hhang <169027046+xu91102@users.noreply.github.com>
2026-10-16 05:17:59 +08:00
morlutoandLiu Jie 0123d59768 fix(browser): compare accessibility only with observed coverage
Return unknown before fingerprinting incomplete accessibility trees. Keep
known structural changes comparable when only text permission is absent,
and preserve independent changes in other dimensions.

Validated through seven CLI cases and existing network comparison semantics.
Refs #3385, #3824.

Co-authored-by: Liu Jie <maxazure@gmail.com>
2026-10-16 05:17:51 +08:00
4cc044be68 fix(browser): retain malformed inventory uncertainty
Distinguish missing CDP inventories from observed empty arrays, retain valid
accessibility and worker siblings, and admit required fields before scope
inference. Stop interpreting DOM documents without a usable string table.

Validated producer semantics and simulated CDP provider boundaries; this does
not establish real Chrome or Electron engine behavior.
Refs #3376, #3379, #3850.

Co-authored-by: 李云周 <154221037+YunzhouLi-hub@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-16 05:17:50 +08:00
morlutoandSriram-PR 5a590e61b1 fix(apple): scan keyed archive root values in context
Treat top-level names separately from encoded values so scalar roots named
UID or CF$UID cannot become references. Keep one shared root traversal budget
and deterministic reference paths.

Validated projection and filesystem reader regressions. The aggregate-budget
stress regression is retained for CI and was not executed locally.
Refs #3913, #4019.

Co-authored-by: Sriram-PR <160517347+Sriram-PR@users.noreply.github.com>
2026-10-16 05:17:42 +08:00
morlutoandRudy Celekli 153017d750 fix(reconstruction): preserve residual replay uncertainty
Keep overall verification unknown when an unchanged process comparison cannot
prove the absence of divergence. Preserve independently complete dimensions
and known changes. Let the named comparison schema own status consistency.

Validated through stdio MCP comparison and verification, plus domain tests.
Refs #3980, #4160.

Co-authored-by: Rudy Celekli <rudy@gradiahq.com>
2026-10-16 05:17:36 +08:00
morluto 0e1f0bf2d0 fix(lifecycle): preserve ownership, cancellation, and cleanup outcomes (#4238)
* fix(cleanup): preserve owner recovery instructions

Keep cleanup remediation through combined errors and direct or managed one-shot analysis while retaining primary diagnostics.

Refs: #4227

* fix(hopper): recover late startup and retained cleanup

Authenticate initial and cleanup handshakes through the same identity owner. Expose startup and cleanup activity, retain health after target retirement, and expire abandoned unauthenticated listeners without timing out authenticated work.

Refs: #4227

* fix(providers): retain composite lifecycle facts across retries

Preserve observed child health with incomplete peer coverage, retry only unresolved clients, and retain all child cleanup recovery actions.

Refs: #4233

* fix(process): honor cancellation through capture cleanup

Check the request signal after cleanup and before final publication, retaining completed observations under typed cancellation.

Refs: #4005

* fix(native): preserve failed LLDB launch ownership and exit proof

Publish failed-launch process identity and observed exit separately from object validity. Retire only the exact owned process and retain uncertainty when exit cannot be established.

Refs: #4232

* fix(native): honor cancellation after trace finalization

Check cancellation after runtime-root cleanup and retain collected trace observations as partial cancellation evidence.

Refs: #4009

* fix(cli): preserve analysis results when provider cleanup fails

Reuse the existing one-shot finalizer across thirteen command families to retain completed Evidence and primary diagnostics on close failure. Cover success, denied cleanup, and combined primary/cleanup failure through the public CLI.

Refs: #3935

* docs(lifecycle): define completion invariants and regression scenarios

Document distinct ownership/readiness/exit facts, late cancellation and atomic publication boundaries, partial child cleanup, and diagnostic retention. Link representative regressions and route contributor guidance through AGENTS.md.

Refs: #4227

* fix(session): retain run identity through pending cleanup

* fix(cli): preserve original cleanup resource diagnostics

* fix(native): retain completed facts through finalization failure

* fix(providers): report retained cleanup paths at their owners
2026-10-16 05:09:35 +08:00
morluto 25c22222ab fix(reference): retain unverified symlink target provenance
Keep the observed target path and failed directory in the unreadable-link diagnostic without promoting either to proven graph identity. Verify the retained path through the importer boundary.

Refs: #4013, #4053
2026-10-16 04:52:19 +08:00
morluto 921fa03fca docs(browser): explain CDP detach effects on caller emulation
Describe Chromium platform-emulation reset across connected page workflows and recovery by the owning client. Browser survival does not imply state preservation; the upstream limitation remains unresolved.

Refs: #4089
2026-10-16 04:41:40 +08:00
morlutoandTomas Galmarini f25c41b832 fix(dmg): classify localized verification failures safely
Use untranslated checksum status and nonblocking file admission for localized verification failures. Keep ambiguous readable targets unknown and bounded retries intact; retain failed descriptor ownership and cancellation.

Refs: #4146

Co-authored-by: Tomas Galmarini <tomigalmarini@gmail.com>
2026-10-16 04:41:40 +08:00
morlutoandXY-code1 d6e4fe4a4d fix(reference): preserve observed directories and unreadable links
Continue validating child directories observed before a parent enumeration failure. Reconcile unobserved link targets at the reader using canonical path identity and the nearest failed ancestor, preserving exclusions and cancellation.

Refs: #4013, #4053, #4060

Co-authored-by: XY-code1 <1611761391@qq.com>
2026-10-16 04:41:40 +08:00
morlutoandTotopo27 ee250816e1 fix(reconstruction): share artifact inspection source validation
Accept inspection Evidence through the inventory semantic owner, retaining nested substep, subject and page validation. Verify the complete CLI/MCP comparison workflow.

Refs: #3984, #4137

Co-authored-by: Totopo27 <35828647+Totopo27@users.noreply.github.com>
2026-10-16 04:41:39 +08:00
morlutoandrudrakshkarpe 10e9448d89 fix(evidence): anchor deterministic replay to unknown creation
Match repeated derived Evidence against the immutable creation revision while retaining analyst updates, conflict rejection and imported history.

Refs: #3925, #4159

Co-authored-by: rudrakshkarpe <rudraksh.karpe@gmail.com>
2026-10-16 04:41:39 +08:00
morlutoandVinícius DS Andrade 4f87f02bb1 fix(wasm): retain cooked directive literals in glue references
Collect leading directive literals using Babel cooked values, preserving source locations and selected candidate identities. Exercise escaped directives through the existing provider workflow.

Refs: #4001, #4149

Co-authored-by: Vinícius DS Andrade <95101505+viniciusdsandrade@users.noreply.github.com>
2026-10-16 04:41:26 +08:00
Ysopwnable 0cc56493be fix(javascript): preserve values captured before later assignments (#4196)
Select binding facts at reads without mixing whole-binding and temporal values. Share conditional activation rules, preserve partial expression order, and bound immutable ancestry work. Memoize temporal results within each query using initializer identity, capture point, budget and dependency proofs; preserve depth limits and source Evidence.

Fixes #3881
2026-10-16 04:35:54 +08:00
morluto e2db768012 test(pcap): verify timestamps through the named summary contract
Move the capinfos oracle to inspect_pcap_capture, whose summary retains timestamps. The field-inspection contract does not expose them. Check both CLI and MCP results against the independently observed producer values.
2026-10-16 04:05:12 +08:00
morluto edfd5025b0 test(pcap): align timestamp oracle with the provider locale
Use the same C locale as the production capinfos command so the independent real-tool oracle compares equivalent producer output.
2026-10-16 03:59:25 +08:00
morlutoandKET ⚡ 95957d748d fix(pcap): retain timestamps from supported Wireshark 4.x labels
Accept the First/Last labels used by Wireshark 4.0 and 4.2 at the capinfos metadata owner. Preserve producer values and omitted timestamps, and verify real MCP timestamp projection against direct capinfos output.

Refs #4186, #4180

Co-authored-by: KET ⚡ <ket.patil77@gmail.com>
2026-10-16 03:58:32 +08:00
morlutoandVinícius DS Andrade d9aa06d93d fix(gdb): validate complete thread-group inventories before cleanup
Parse quoted MI fields and container syntax once before interpreting live PIDs. Keep malformed or duplicate inventories unknown, preserve compatible extension fields, and bound parser state rather than treating partial syntax as proof of no inferiors.

Refs #4150

Co-authored-by: Vinícius DS Andrade <95101505+viniciusdsandrade@users.noreply.github.com>
2026-10-16 03:49:44 +08:00
morluto 94d9bffaf6 test(windows): hold sharing conflict through first close attempt
Keep the delete-sharing-denied reader held until the next event-loop turn and assert close is still pending before releasing it. This distinguishes an internal retry from a holder that releases before cleanup starts.
2026-10-16 03:49:44 +08:00
306d531f6f fix(packaging): distinguish tar line endings from filename bytes
Parse one listing separator so CRLF output matches packaged bridge paths while literal CR bytes in LF listings remain part of filenames. Retain generated-bytecode rejection.

Refs #4154

Co-authored-by: abdooo2235 <abdalrhmantheiv15@gmail.com>
Co-authored-by: Ysopwnable <161784452+N0zoM1z0@users.noreply.github.com>
2026-10-16 03:49:44 +08:00
morlutoandjackie_cqz c209dcb209 fix(artifacts): classify ZIP offsets beyond EOF as malformed
Reject negative bounded reads before allocation while retaining short-read and exact-EOF behavior. Exercise the corrupted central-directory offset through the public CLI.

Refs #4006, #3953

Co-authored-by: jackie_cqz <88996311+jackie-cqz@users.noreply.github.com>
2026-10-16 03:49:44 +08:00
morlutoandVinícius DS Andrade bfacfbac30 fix(ida): share string-page failure interpretation
Preserve nullable producer errors in the shared page contract and reject explicit failures before returning matches. Retain both successful and failed replies as partial evidence.

Refs #4151, #3990

Co-authored-by: Vinícius DS Andrade <95101505+viniciusdsandrade@users.noreply.github.com>
2026-10-16 03:49:44 +08:00
bc51d0ea5d fix(sqlite): preserve snapshot admission failure semantics
Refuse oversized database and WAL inputs before acquisition with exact observed bytes and export guidance. Keep initial path failures separate from file-set changes and retain cleanup and partial observations on late growth.

Refs #4208, #4188, #4141, #4127

Co-authored-by: hima <1ep23ic023@eastpoint.ac.in>
Co-authored-by: Royce <roycelam@umich.edu>
2026-10-16 03:49:44 +08:00
YsopwnableandKaoru0822 85eac7b92b fix(javascript): preserve analysis applicability after interruption (#4207)
Preserve applicable artifact processing phases and entered/unvisited status after interruption. Centralize JavaScript semantic applicability for full and incremental builders; retain native byte identity, actual JavaScript unknowns and structured-file provenance.

Fixes #3948. Part of #4027.

Validation:78 distinct focused cases across10files, full static/docs, and real Joplin timeout/default public CLI/MCP controls passed. Source pins, every attempt/outlier and compact resource/token projections are retained. No target/native provider execution or speedup claim.

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-16 03:46:00 +08:00
morlutoandSwatantra Yadav 663d07f02c fix(server): retain session admission through investigation publication
Admit all four derived investigation workflows until Evidence recording settles, so close drains earlier work before clearing the ledger. Honor cancellation after awaited terminal progress before publishing a result.

Refs #4002, #4174

Co-authored-by: Swatantra Yadav <maverickswatantra@gmail.com>
2026-10-16 03:13:20 +08:00
morlutoandHarsH a2e30168a0 fix(mcp): relax Boolean literals without narrowing valid inputs
Normalize only advertised schema positions while keeping canonical validation. Preserve branch guidance and relax nonmonotone constraints as a unit. Verify full/compact catalogs and canonical examples after SDK conversion.

Refs #4025, #4087

Co-authored-by: HarsH <harshasalkar7@gmail.com>
2026-10-16 03:13:14 +08:00
morlutoandDeryFerd 0679a7972b fix(windows): retry owned runtime cleanup on sharing conflicts
Bound retries of native close conflicts at the retained runtime owner. Preserve exact failures and later retry authority after exhaustion. Attempt all verifier cleanup while retaining primary failures; extend held-file Windows verification.

Refs #4136, #4185

Co-authored-by: DeryFerd <deryferdikao125@gmail.com>
2026-10-16 03:13:10 +08:00
morlutoandArjun Prabhulal 8d8257928b fix(cli): validate integer tokens before coercion
Validate decimal tokens at the CLI owner before safe-number parsing. Apply the same rule to positional indexes/generations and repeated record selectors, retaining leading-zero decimals and canonical MCP inputs.

Refs #4182

Co-authored-by: Arjun Prabhulal <arjun.prabhulal@gmail.com>
2026-10-16 03:13:06 +08:00
morluto 08a897b2cd chore(ci): remove the redundant exact-test runner entry
The workflow action-pinning gate now discovers this runner through its explicit command, so Knip no longer needs a duplicate manual entry. Preserve its normal unused-code checks.
2026-10-16 02:48:45 +08:00
morluto 2ff086796a fix(browser): disclose connected platform-emulation teardown limits
Preserve the CDP detach limitation through successful and retained partial observations at their shared projector. Do not infer or replay unavailable caller user-agent/client-hint state.

This documents the reported Chromium platform reset; it does not prevent it. Source composition tests ran, without a new real-browser reproduction. Issue #4089 remains open.
2026-10-16 02:47:04 +08:00
morlutoandGabriel Dias 045c84db76 fix(pe): retain empty resources at virtual section endpoints
Map a zero-byte payload at a section endpoint only when no section contains that RVA, preserving adjacent-section ownership and raw backing checks. Extend the real CLI and stdio MCP workflow across PE32/PE32+ empty and nonempty resources.

The portable parser and public adapter regressions ran locally; Windows API loader behavior and GNU-produced images were not independently rerun.

Refs #3970

Co-authored-by: Gabriel Dias <gabrieldiasnevess@outlook.com>
2026-10-16 02:47:04 +08:00
morlutoandAdkid-Zephyr 6b1c36e336 fix(artifacts): normalize ZIP current-directory prefixes at the reader
Preserve original member spellings and central-directory identities while omitting actual root directory markers. Derive entry kind once so Unix symlink facts and size metadata agree; retain traversal and non-directory root rejection.

Verify public inspect/extract workflows for prefixed and root-only archives, distinct duplicate occurrences, and malformed/symlink controls.

Refs #4124

Co-authored-by: Adkid-Zephyr <laufs0310@163.com>
2026-10-16 02:47:04 +08:00
3406dc4d85 ci: pin Cutter actions and guard workflow-only changes (#4199)
Pin the remaining Cutter upload/download actions and enforce immutable GitHub/Docker references at actual action call sites. Run the same exact guard for workflow-only changes without preparing runtime artifacts.

Co-authored-by: Govindu Sasanka <31107592+GovinduSasanka@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-15 18:38:19 +00:00
morlutoandakushonkamen 5e30969cc5 fix(mcp): advertise self-contained tool inputs
Inline reused input schemas at their projection owner and remove the superseded reference-budget fallback. Validate actual SDK input and output schemas while preserving canonical parsing, output references, and projection cache options.

Claude Desktop and affected Windows connector behavior remain unverified; the SDK catalog has no schema-local input references.

Co-authored-by: akushonkamen <akushonkamen@163.com>
2026-10-16 02:23:25 +08:00
morlutoandgongfpp 5fb6edc34b fix(target): confine app program directories to their canonical bundle
Validate the canonical programs directory before resolving its executable, preserving containment failures even when the external child is missing. Retain in-bundle directory and bundle aliases.

Co-authored-by: gongfpp <22683417+gongfpp@users.noreply.github.com>
2026-10-16 02:23:25 +08:00
morlutoandArjun Prabhulal fb51960d7d fix(native): preserve Mach-O operand boundaries and integer limits
Consume signed and unsigned LEB operands through their terminators while validating 64-bit payloads and bounded extension. Preserve independent decoded metadata when a malformed stream fails.

Co-authored-by: Arjun Prabhulal <arjun.prabhulal@gmail.com>
2026-10-16 02:23:25 +08:00
claudetrail2026-droidandKaoru0822 1b828dbb10 ci(ghidra): compile the packaged bridge source cohort (#3142)
Compile the complete packaged Java source cohort against the verified SDK in selected CI. Share release and source ownership with runtime/package consumers, fail closed on drift, and remove compiler scratch on failure.

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-15 18:11:19 +00:00
YsopwnableandKaoru0822 c7f6b8bad1 fix(javascript): bind native handoffs to canonical byte identity
Require the native node owner's content digest instead of borrowing an
observation artifact digest, which can identify the containing ASAR. Keep
unavailable nodes and their path/provenance/partial facts in the trace,
without a false actionable target or native Evidence link. Remove the old
fallback and allocate the query index only for eligible native targets.

Verify real public CLI/stdio MCP workflows, known/missing companion inputs
and shipped Joplin controls. Preserve reproducible scripts, source pins and
compact correctness/resource/token records without archiving applications.

Fixes #3926. Part of #4027.

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-15 17:08:49 +00:00
ShuaixinHuang 633e4ff434 fix(javascript): retain static paths through TypeScript wrappers (#3998)
Normalize erased TypeScript wrappers through existing expression/string owners for static paths, call targets, collection/configuration receivers and global aliases. Preserve binding/mutation guards and explicit unknown targets, with CLI and application-boundary regressions.

Original contribution by @ShuaixinHuang.

Co-authored-by: ShuaixinHuang <1836656514@qq.com>
2026-10-16 01:04:24 +08:00
ShuaixinHuang d67d895c26 fix(jeb): isolate invalid timeout from shared startup (#3964)
Keep malformed JEB-only timeouts isolated from shared CLI/MCP startup while strict registration and JEB configuration checks remain authoritative. Cover built CLI discovery and the production stdio MCP refusal and peer requests.

Original contribution by @ShuaixinHuang.

Co-authored-by: ShuaixinHuang <1836656514@qq.com>
2026-10-16 01:03:39 +08:00
af6845cddb test(browser): preserve resource uncertainty through MCP (#3416)
Preserve resource uncertainty through production MCP registration and the real SDK. Test surviving resource fields/exclusions, forward/reverse/self comparisons, and complete-empty controls. Production behavior is unchanged.

Baron approved the unchanged head 07ea877a388ee2cd0586f04b1fb75bd104b484be; current required CI passes.

Original contribution by @ShuaixinHuang.

Co-authored-by: ShuaixinHuang <1836656514@qq.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
2026-10-16 01:01:18 +08:00
7b86d5caf6 fix(process): preserve unrelated trace comparison uncertainty (#4178)
Only apply a different trace verdict to its attributed dimensions. Preserve
unrelated unknown coverage and independently observed additions/removals in
the shared CLI/MCP comparison owner.

Fixes #3931

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
2026-10-16 00:56:55 +08:00
Kaoru0822andClaude Opus 5.5 62bf09809e fix(kuna): answer a repeated single-function request without re-running Kuna (#4169)
* fix(kuna): answer a repeated single-function request without re-running Kuna

A session dropped each single-function run once it settled, so a repeat
request for the same procedure re-ran `kuna decompile --addr`, which
analyzes the whole image again. After a failed export every pseudocode
request uses that path for the rest of the session.

Keep each completed text by entry address, within a 16 MiB least recently
used budget, until the export's reader is adopted or the session closes.
Failed and cancelled runs are not kept. The export's text still supersedes
a kept one.

Refs #1957

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(kuna): pin that a kept text's hit refreshes its recency

With two 9 MiB texts only one fit the budget, so a repeated lookup could
not change which entry was dropped. Use three 6 MiB texts: re-reading main
leaves parse least recently used, so keeping broken must drop parse.

Refs #1957

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 09:36:05 -07:00
Kaoru0822andClaude Opus 5.5 8686b32209 fix(browser): omit erased type-only import-equals from source-map dependencies (#4165)
Source-map original-source parsing reported `import type X = require("m")`
as a require dependency, although TypeScript erases the declaration. The
fetcher re-derived import-equals targets instead of using the existing
value-only rule. That rule now lives with the other type-only module
declaration predicates in javascriptModuleDeclaration, and the fetcher,
static analysis, and semantic analysis all use it.

Fixes #3932

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 09:34:18 -07:00
Kaoru0822andClaude Opus 5.5 0cfced12ea fix(reference): resolve extensionless CommonJS requires to JSON files (#4164)
The reference-source resolver probed only JavaScript and TypeScript
extensions, so require("./data") stayed unresolved when the tree held
data.json, which Node's CommonJS loader selects. Resolution now uses
extension slots: a requires edge also tries X.json after X.js and
before any directory index, and X/index.json after X/index.js,
including a package main. ESM and TypeScript imports still need an
explicit .json extension.

Fixes #3915

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 09:34:00 -07:00
Kaoru0822andClaude Opus 5.5 d816470bcb fix(apple): count Interface Builder hierarchy records beyond the object limit (#4162)
The ibtool dictionary parser cut the hierarchy array at a fixed 20,000
entries without counting what it discarded, so an identified record past
that position vanished while the document still reported complete
hierarchy coverage. The graph walker already owns the hierarchy limit;
the parser now passes the decoded array through.

The walker applies max_objects per identified record and keeps walking
past the limit to count omitted records, instead of stopping on a
pending structural entry. Coverage now reports the omitted count, and a
hierarchy that exactly reaches the limit is no longer marked partial.

Fixes #3950

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 09:33:02 -07:00
Guilherme Dias FusariandKaoru0822 89faa863ed test(applications): import capture fixtures with file URLs (#3050)
Convert the selected filesystem path at the fixture execution boundary. Exercise spaces and hash characters without changing real execution, CLI/MCP comparisons or the test timeout. Verified the full Linux and native Windows files with real ESM-loader negative controls.

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-15 16:18:04 +00:00
Guilherme Dias FusariandKaoru0822 5b16f24310 test(applications): pass Evidence requests to the CLI through files (#3049)
Use existing test-owned request files for the remaining full Evidence CLI inputs. Preserve complete inputs, semantic assertions and inline transport coverage. Verified the full Linux file and native Windows baseline failures plus corrected public CLI cases.

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-15 15:51:03 +00:00
YsopwnableandKaoru0822 9f8659e053 fix(javascript): enforce semantic byte bounds for literals
Fixes #3933. Part of #4027.

Normalize exact literals and configuration defaults through the shared primitive owner. Preserve tagged limits through member/destructuring projection and record encountered bounds in per-analysis state independently of later selected values. Remove the retained-value rescan and duplicate default reader. Preserve source Evidence, bounded/null values, aggregate wire capacity and complete endpoint lookup identities for query/trace/comparison.

Validated 66 focused and 932 semantic tests on the restored identical runtime; final public selection 7/7 adds an augmented large-endpoint acceptance (67 distinct focused cases across invocations). Full static/docs and fresh real Obsidian controls passed. Compact measurements and rejected attempts remain recorded.

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-15 23:43:05 +08:00
KenmegeandKaoru0822 40f73f62bc test(rizin): own fake provider cleanup in shared fixtures (#3735)
Retain native fixture ownership through observed bounded cleanup and preserve files and handles when cleanup cannot be confirmed. Verified all seven files on Linux and native macOS, including cleanup failure and exit-wait controls.

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-15 15:24:55 +00:00
Ysopwnable 6ae59f3d19 fix(verifiers): launch npm through Node on Windows (#4153)
* fix(verifiers): launch npm through Node on Windows

* test(verifiers): cover Windows npm fallback
2026-10-15 23:12:29 +08:00
Kaoru0822andClaude Opus 5.5 9d6651e5b5 fix(artifacts): never treat a directory named *.map as a source map (#4131)
Artifact inventory decided source-map edges from the `.map` path suffix,
so an archive directory such as `main.js.map/` received a maps-source
edge whose parent was the sibling `main.js`, although the same
occurrence was classified as a directory container. Edge construction
now uses the occurrence's own artifact_kind, and the duplicate suffix
rule in relationFor is removed; such directories are contained by their
parent, and real source-map files keep maps-source.

Fixes #3909

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 07:41:04 -07:00
Kaoru0822andClaude Opus 5.5 a90c9eac6a fix(apple): keep Interface Builder objects whose archived class is unresolved (#4118)
* fix(apple): keep Interface Builder objects whose archived class is unresolved

Interface Builder keyed-archive projection skipped every object-table
entry without a resolved class name, so an archived object whose $class
reference dangled vanished from the objects, its count and its coverage,
which still reported complete; as a hierarchy root it lost its label.
Entries that carry their own $class are archived objects: they are now
kept with class_name null and the generic kind, their fields and labels
preserved. Scalars and class descriptors remain excluded.

Fixes #3958

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(apple): keep the dangling class reference of an unknown IB object

Review follow-up. An archived object kept with an unresolved class lost
its original `$class` reference, because class resolution replaces it
with a null class name. The object's attributes now retain that raw
reference when the class cannot be resolved. A graph-level test covers
the reported hierarchy-root case: the unresolved root keeps its label,
unknown kind, containment edge, object count and coverage.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 07:40:10 -07:00
Kaoru0822andClaude Opus 5.5 cfd3c35a50 fix(kuna): keep the real lane's wide-string marker independent of layout (#4116)
On macOS arm64 the lane's UTF-8 marker ends right before the UTF-16 array,
and its last byte plus NUL decode as the UTF-16LE unit 'r'. Kuna therefore
reports "rREA wide marker", and the lane, which requires the exact text,
failed with "Required string \"REA wide marker\" was not recovered". A
leading NUL unit now ends any run the preceding bytes start, so the marker
is recovered exactly on any layout.

Refs #1957

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 07:38:33 -07:00
Kaoru0822andClaude Opus 5.5 3776f218e5 fix(apple): bound Interface Builder XML plists by shared value depth (#4114)
* fix(apple): bound Interface Builder XML plists by shared value depth

The Interface Builder preflight rejected XML archives whose raw element
depth exceeded 128, but the shared plist parser bounds zero-based value
depth at 128; the plist envelope and dictionary keys add elements, not
value depth. Archives whose deepest value sat at depth 127 or 128 were
therefore omitted as over budget although keyed-archive inspection
decodes them. The preflight's duplicate depth rule is removed; the
shared parser's depth limit is mapped to the existing budget omission,
so deeper archives stay explicitly omitted.

Fixes #3949

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(apple): return the decode reservation of a depth-rejected archive

Review follow-up. The removed element-depth preflight rejected an archive
before its plist representation was reserved; the shared depth limit now
rejects it during decoding, after the reservation. A large over-depth
archive therefore kept its share of the aggregate budget and could
starve later valid archives. A document omitted by the budget after
reserving now releases that reservation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 07:38:07 -07:00
Kaoru0822andClaude Opus 5.5 f7250fe35f fix(kuna): say why Kuna cannot answer instead of a generic message (#4113)
Kuna capability gaps such as "no code at this address" or "no function node
at this address" were built without a user message, so MCP and the CLI
showed only "This analysis is unavailable for the current target. Choose
another analysis or target." and kept the reason in details. One Kuna
helper now presents the reason as the message for every such gap, and the
unsupported-operation reason names call relationships and xrefs, which Kuna
now answers.

Refs #1957

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 07:37:20 -07:00
Ysopwnable cd64e24ff2 fix(verifiers): align WABT operation timeout (#4117)
* fix(verifiers): align WABT MCP request timeout

* fix(verifiers): bound packaged WABT workflow
2026-10-15 22:29:15 +08:00
Abdul Samad SiddiquiandKaoru0822 a40ee741c2 test(ghidra): align MIPS verifier ABI and snapshot expectations
Fixes #3415. Retain independently inspected GNU attribute facts and compare exact snapshot wire fields. Preserve strict metadata comparisons, specific incomplete-ABI refusals and source immutability checks.

Original implementation by @AbdulSamad94.

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-15 22:00:16 +08:00
YsopwnableandKaoru0822 e9a71efa31 fix(javascript): declare owned analysis worker effects
Advertise internal worker launches and temporary source writes through the
canonical effects owner so MCP hints and session availability agree with
existing analysis behavior. Preserve non-destructive, idempotent semantics.

Verify public metadata boundaries and a real Obsidian CLI/MCP workflow,
retaining compact reproduction and cost records.

Fixes #3867

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-15 21:36:12 +08:00
Kaoru0822andClaude Opus 5.5 fe7bcc3fbe fix(apple): report __proto__ entries omitted from OpenStep property lists (#4105)
* fix(apple): report __proto__ entries omitted from OpenStep property lists

The pinned OpenStep decoder assigns a `__proto__` dictionary key to the
object's prototype, so the entry vanished before REA could count it and
OpenStep keyed archives reported no omission, unlike the same archive in
XML. The OpenStep pre-scan, which already walks every key for the depth
limit, now counts keys that decode to `__proto__`; quoted keys with
escapes are decoded by the pinned decoder itself. The rebuilt value still
drops any prototype a key replaced.

Fixes #3892

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(apple): count OpenStep compound keys that coerce to __proto__

Review follow-up. A compound dictionary key such as `(__proto__)` is
coerced by the decoder's `obj[key]` assignment to the property name
__proto__, but the scan only checked scalar keys. Containers opened in
key position are now decoded by the pinned decoder on close and counted
when their property-name coercion is __proto__.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 06:12:15 -07:00
Kaoru0822andClaude Opus 5.5 736aa946bc fix(kuna): suggest no restart when a fixed buffer stopped the export (#4104)
A failed export's capability error appended "restart REA, then reopen the
target" to every resource-constraint remediation. Since output overflows
became fixed `memory` bounds, an export whose own output overflowed REA's
buffer was told both that repeating fails the same way and that a restart
would help. The restart sentence now follows only settings-backed
remediations such as the cache size.

Refs #1957

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 06:12:00 -07:00
Kaoru0822andClaude Opus 5.5 d929543198 fix(artifacts): never count occurrences on an unknown duplicate path as unchanged (#4100)
Artifact comparison subtracted change rows from the left occurrence
count to derive summary.unchanged. A duplicate logical path produces one
unknown row that stands for every occurrence on that path, so the extra
occurrences were reported as affirmatively unchanged. The summary now
counts only left occurrences on paths without a left-side change row.

Fixes #3956

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 06:11:46 -07:00
e598b4656a fix(ghidra): use resolved type addresses for NativeAOT metadata
Fixes #3327.

Reuse parsed Ghidra type identities for NativeAOT metadata lookup, preserve
caller spelling and address spaces, and retain supplied engine metadata
without deriving another source snapshot. Verify actual compiled NativeAOT
PEs through real Ghidra and public CLI/MCP workflows.

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
2026-10-15 21:05:33 +08:00
YsopwnableandKaoru0822 ec394a2bdf feat(javascript): add filtered retained module reference views
Expose filtered canonical module identities, retained source-range counts and mixed support through the existing shared CLI/MCP analysis view. Preserve full-item access, source selection, provenance and partial coverage while allocating only the selected page.

Verify exact facts and CLI/MCP identity on shipped Obsidian, record complete-task BPE/resource measurements and reuse owned-process supervision for optional tokenizer cleanup.

Fixes #4092
Part of #4027; decision #4091.

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-15 21:05:00 +08:00
7054bbb9ab feat(kuna): add cached string inventory and search (#4095)
Add cached, reference-free Kuna string inventories through existing CLI/MCP operations. Preserve exact addresses, typed facts, selected Evidence and bounded query lifecycles; validate native fields and caller-specific failures. Verified with real Linux SQLite, zlib, printf and Unicode/empty ELF targets, 194 focused regressions and all 38 CI checks.

Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-15 13:04:27 +00:00
YsopwnableandKaoru0822 b2257653ca test(ghidra): isolate source admission failure sessions
Fixes #3405. Isolate each negative source-admission case with a cold Ghidra session while retaining valid snapshot readback coverage.

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-15 20:45:35 +08:00
Kaoru0822andClaude Opus 5.5 b3b5c8b133 fix(kuna): report output beyond REA's buffer as deterministic and final (#4098)
Kuna output that overflowed REA's fixed buffer was a `capacity` constraint,
which projects as `retryable: true` with "wait for an active analysis to
complete, then retry". The same target and executable overflow the same
buffer every time; on a 169 MB Mach-O, each retry of a call query repeated a
113 s whole-program graph walk and failed again. The overflow is now a
`memory` constraint whose remediation says repeating fails the same way, a
session answers later call queries from a graph overflow it already saw, and
the cut-off document's stdout tail is no longer returned.

Refs #1957

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 05:23:43 -07:00
Kaoru0822andClaude Opus 5.5 befbc024c9 fix(apple): select only entries named Assets.car as asset catalogs (#4094)
inspect-asset-catalog selected every bundle entry whose relative path
ended in Assets.car, so an unrelated NotAssets.car directory was listed
as a skipped catalog and, with no real catalog present, failed the
inspection instead of returning the empty inventory; a regular file such
as OldAssets.car would have been sent to assetutil. The selector now
compares the final path component exactly.

Fixes #3914

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 05:21:17 -07:00
Kaoru0822andClaude Opus 5.5 fc41b24dc4 fix(native): keep a category whose class reference cannot be read (#4093)
An Objective-C category whose nonzero class reference pointed outside
mapped metadata failed as a whole, discarding its readable name, method
selectors and implementations. The category decoder now resolves the
local class name in its own boundary: on failure the category keeps its
facts with class_name null and class_source unresolved, and a
category_class_unresolved issue records the unreadable reference in the
category coverage.

Fixes #4012

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 05:21:04 -07:00
Kaoru0822andClaude Opus 5.5 2c90082a69 docs(mcp): describe the 100-value completion reply limit (#4086)
* docs(mcp): describe the 100-value completion reply limit

The prompt guide said completion returns all matching values. A reply
carries at most 100 values, the MCP limit, with total and hasMore
reporting omitted matches. Describe that and how to reach omitted values
with a longer prefix.

Fixes #3866

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(mcp): qualify longer-prefix completion recovery

Review follow-up. A longer prefix reaches an omitted value only when it
excludes other matches; values whose normalized forms share a prefix
with more than 100 matches stay unreachable.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 05:20:50 -07:00
Kaoru0822andClaude Opus 5.5 82763824dc fix(domain): keep a decoded Swift alias category beside undecoded aliases (#4085)
categorizeSwiftTypes counted a recognized Swift alias whose category was
not decoded as a null vote, so one undecoded same-address alias erased a
class category established by another alias: the procedure moved to
other, and a classes filter returned nothing. Only decoded categories now
vote; undecoded aliases stay in mangled_names as evidence.

Fixes #3934

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 05:20:35 -07:00
Kaoru0822andClaude Opus 5.5 41e4c06397 fix(native): record an empty Objective-C protocol name as a protocol failure (#4078)
* fix(native): record an empty Objective-C protocol name as a protocol failure

A protocol declaration whose name was an empty NUL-terminated string was
accepted as decoded, so whole-result validation of objc_protocols[].name
failed and inspect-native-dispatch-metadata discarded every decoded
class from the same image. The protocol decoder now rejects the empty
name inside its per-protocol boundary, as the class decoder already
does, so the protocol becomes a recorded partial-coverage failure and
independently decoded classes are retained.

Fixes #3873

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(native): decode adopted protocols before rejecting an empty parent name

Review follow-up. The empty-name check ran before the adopted-protocol
list was traversed, so a protocol declared only through an empty-named
parent's list was dropped. Adopted protocols are independent
declarations; traverse the list first, then reject only the parent.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 05:20:22 -07:00
Kaoru0822andClaude Opus 5.5 a5024a9710 fix(kuna): report a failed export as not retryable in its session (#4070)
* fix(kuna): report a failed export as not retryable in its session

A session never rebuilds a failed project export, yet requests that need it
returned the export's raw failure. For a timeout that projected as
`retryable: true` with "Try a smaller request", and every retry returned the
same stored failure. Those requests now fail with a non-retryable
capability error that keeps the export failure as its cause and diagnostics
and names what to change (the deadline or jobs for a timeout) before
reopening the target. Cancellation and unproven cleanup keep their own
errors.

Refs #1957

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(kuna): keep a failed export's output and name the restart it needs

The capability error for a failed export dropped the export's captured Kuna
output, which the MCP projection only reads from the top-level error. It
also told callers to change a setting and reopen the target, but REA reads
Kuna settings once at startup. The error now carries the captured output
and partial observation, and its recovery text says to change the setting
where REA starts and restart REA.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(kuna): keep resource limits on a failed export's capability error

An export larger than REA_KUNA_CACHE_MAX_BYTES, or a Kuna command at its
output cap, fails with a resource constraint whose resource and reported
limits the projection exposes only on the top-level error. The wrapper now
copies them into its diagnostics under the same names.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor(kuna): carry a failed export's whole projection on its wrapper

The capability error for a failed export copied the cause's facts field by
field (captured output, resource limits), and each missing field, such as an
artifact change's path and boundary, dropped evidence callers could not
reach through `cause`. The wrapper now carries the export failure's own
projected code and details in its diagnostics, so every typed field survives
in the shape its own projection gives it, and the duplicated top-level
copies are removed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 05:20:08 -07:00
Kaoru0822andClaude Opus 5.5 fc791bb606 fix(paths): select caller paths through symlinks and .. as the filesystem does (#4067)
* fix: select caller paths through symlinks and .. as the filesystem does

CLI adapters, binary target resolution, the reverse-engineering service
and the objdump and Rizin command builders made caller-selected paths
absolute with path.resolve, which collapses `link/..` lexically. On
POSIX the filesystem follows `link` first, so these commands inspected,
hashed or launched a different file and returned internally consistent
Evidence for it.

absoluteCallerPath now owns that step: on POSIX it prefixes a relative
path with the working directory and keeps an absolute path as spelled;
on Windows, where `..` resolves lexically, it keeps win32.resolve.

Fixes #3864
Fixes #3911
Fixes #3912

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: keep firmware, WASM list and output-root paths on the selected file

Review follow-up. The CLI already passed firmware and WASM paths through
absoluteCallerPath, but snapshotFirmware and SafeOutputTree.create
re-resolved them lexically, and the WASM glue and candidate lists still
used resolve, so those inputs could still select a sibling file or
create output beside the intended directory.

absoluteCallerPath now also drops `.` segments and repeated or trailing
separators on POSIX, which never change the named file, so selected
paths such as `./module.wasm` keep matching their glue references
exactly. Every `..` is still left for the filesystem.

SafeOutputTree keeps host path syntax; its platform argument selects only
the Windows name policy.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: keep directory requirements and relative cwd in caller paths

Review follow-up. absoluteCallerPath stripped a trailing `/` or `/.`, so
`file/` selected the regular file instead of failing with ENOTDIR; it
now keeps one trailing separator. A relative cwd option was appended to
the root; it is now made absolute without collapsing `..`. WASM glue
matching resolved local literals lexically, so a module selected through
`link/..` matched only by basename; the adapter now compares both sides
through absoluteCallerPath and still reports each candidate as given.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 05:19:43 -07:00
Kaoru0822andClaude Opus 5.5 8ed3d1df72 fix(kuna): list a cold target from the export's own inventory (#4062)
* fix(kuna): list a cold target from the export's own inventory

On a cold cache the session ran `kuna functions --json` for listings while
the background export ran the same inventory as its first step, so every
cold target was inventoried twice at once. On a 254 MB Mach-O each run took
about 66 s of CPU. The quick run was also bound by the single-function
deadline.

The export's status now carries its parsed inventory, the cache replays the
latest status to requests that join a running build, and listings wait for
that inventory. The session runs its own inventory only when the export ends
without one.

Refs #1957

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(kuna): replay a running build's status after its waiter can cancel

The cache replayed the latest status to a joining request before it
registered that request's abort listener, so a listener that cancelled on
the replayed status was ignored and the request waited for the export to
finish. Replay now happens once cancellation is registered.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* perf(kuna): publish the export's inventory without parsing it twice

The export parsed `functions.json` for its status and then parsed the file
it had just written again to publish, so two expanded inventories coexisted
while the export finished. Publishing now reuses the inventory parsed from
that same text. Cached lookups still parse the published file.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(kuna): exercise export inventory sharing from pseudocode requests

Inventory-only requests no longer start the export, so a cold request that
shares the export's inventory is now a text request. The tests and the
guide describe that trigger.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(kuna): let a listing wait for an export that is still starting

A listing that arrived while a concurrent text request was still looking up
the cache saw no export inventory yet and started its own `kuna functions`,
and the export then started another. The listing now waits for that startup
decision before falling back to its own run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 05:19:25 -07:00
Ysopwnable 2b5ee72d26 feat(ghidra): admit ARM PE targets on Windows (#3965)
Admit Windows ARMNT/Thumb-2 and ARM64 PE targets using preserved COFF machine facts. Keep unsupported machine gates explicit and distinguish loader-selected versus configured language provenance.

Verified with real Windows Ghidra 12.1.4 CLI/MCP workflows, ARM fixtures and Microsoft-signed Coreinfo ARM64 / MSVCR120 ARM inputs. Resource limits, analyzer overrides, startup timeout observations and verification gaps are documented in the PR. Final CI and automated review passed.

Fixes #2186
2026-10-15 20:11:26 +08:00
Ysopwnable 2a2a33ad92 fix(browser): confirm service-worker cancellation cleanup (#4010)
* fix(browser): confirm service-worker cancellation cleanup

* fix(browser): restore policy after setup failure

* fix(browser): transfer cancelled navigation cleanup

* fix(browser): retain cancellation ownership across retries

* fix(browser): confirm storage navigation cleanup
2026-10-15 20:08:30 +08:00
c7697595d8 fix(browser): report attached locale coverage limits
Fixes #3395.

Disclose attached locale coverage in complete and retained partial captures,
clarify the shared locale contract, and verify actual CLI/MCP caller-language
preservation and browser cleanup. Launch supplies full page/request locale.

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
2026-10-15 19:37:19 +08:00
c9f9b83c6f feat(kuna): add bounded call relationships and exact xrefs
Add session-cached call relationships and exact incoming xrefs using a body-free graph and bounded native queries. Reuse existing graph/path tools, preserve coverage and exact u64 identities, and retain native cleanup owners and admission leases.

Validated with 136 tests, real Linux Kuna workflows on SQLite/zlib and source-owned fixtures, independent native xref comparisons, resource measurements, CI and review.

Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-15 11:12:47 +00:00
YsopwnableandKaoru0822 55cef2566a perf(tests): reuse CLI compilation for exported binding checks
Reuse the existing CLI compilation policy in the exported binding acceptance
matrix. Own and clean a fresh cache per file while preserving caller cache
and coverage choices, all 92 cases, and every live CLI/MCP request and oracle.

Fixes #4079

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-15 19:00:04 +08:00
Kaoru0822andClaude Opus 5.5 f255899ee8 fix(kuna): keep the run slot of an unproven stop until close proves it (#4052)
A single-function run whose process tree could not be proven stopped
released its run slot immediately, so the next queued run started while
the old tree might still run, and repeated unproven stops bypassed the
bound. The retained run now carries its slot, and close releases it only
after a retried stop is proven.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 03:42:22 -07:00
Kaoru0822andClaude Opus 5.5 65726afaaa fix(native): report an object file's unnamed segment by its empty name (#4051)
inspect-macho projected an empty Mach-O segment or section name as the
display label "unknown", so an exhaustive segment inventory for an
ordinary MH_OBJECT reported a segment literally named "unknown" while
the raw LC_SEGMENT_64 field showed "". Object files place every section
in one unnamed segment, so the empty name is a source value. Segment and
section names are now reported exactly as observed.

Fixes #3875

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 03:42:15 -07:00
Kaoru0822andClaude Opus 5.5 30ba5e99f9 fix(native): keep a dylib's own install name out of its dependencies (#4050)
inspect-macho listed the LC_ID_DYLIB command of a dylib as a dependency,
so every dylib reported a self-dependency and an inflated exhaustive
count, while trace-dylib-resolution correctly treats that name as the
image identity. The dependency collector now skips LC_ID_DYLIB; the
command, with its install name, remains in the raw load commands.

Fixes #3872

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 03:42:03 -07:00
Kaoru0822andClaude Opus 5.5 b6529bf1af fix(kuna): verify a warm export once for concurrent first requests (#4049)
Every request a session admitted checked `#project` before awaiting the
cache lookup, so concurrent first requests, such as batch_decompile's
parallel resolutions on a fresh server, each re-hashed every export file
and re-parsed the export. Concurrent first requests now share one lookup.

Refs #1957

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 03:41:27 -07:00
1b8a9c831b fix(reconstruction): require comparable cited Evidence for coverage
Fixes #3357

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
2026-10-15 18:39:57 +08:00
Kaoru0822andClaude Opus 5.5 cb18d4c9c4 docs(objdump): show debug-link flag forms that keep their meaning (#4068)
The DWARF example passed `--follow-debug-links false`. The CLI parses
the option as a Boolean flag and drops the separate `false` word, so the
documented command enabled debug-link following (`-WK`). Show the
default and the bare flag, and name `--no-follow-debug-links` and
`--follow-debug-links=false` as the forms that keep it disabled.

Fixes #3893

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 18:07:09 +08:00
Kaoru0822andClaude Opus 5.5 47b4628bac fix(native): retain every repeated otool load-command field (#4048)
`inspect-macho` stored each `otool -l` row in a flat record, so a key
printed more than once kept only its last value. A two-string
LC_LINKER_OPTION lost `-framework` and kept `#2 Foundation`, and a
multi-tool LC_BUILD_VERSION kept only its last tool, while the
collection still claimed exhaustive coverage.

Linker-option strings are now keyed by otool's own `string #n` label
with the exact option text and must match the declared count. Any other
repeated key is labeled `key #n` in print order. Dylib encoding checks
read the parsed rows, so repeated `options` rows are still rejected.

Fixes #4008

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 02:29:06 -07:00
Kaoru0822andClaude Opus 5.5 670ad6bd96 fix(kuna): share and bound single-function decompile runs (#4045)
While the project export builds, every pseudocode request started its own
`kuna decompile --addr` process, and each one analyzes the whole image
again. A cold batch_decompile therefore launched one process per address,
including duplicates, with no bound.

Requests for one entry now share one run, which stops when its last waiter
is cancelled; that waiter still receives the run's outcome, including
unproven cleanup. A provider-wide pool admits at most the host's cores
beyond REA_KUNA_JOBS (at least one) at once, and a queued request is
answered from the export when it finishes first.

Refs #1957

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 02:24:29 -07:00
Kaoru0822andClaude Opus 5.5 c61c108b52 perf(catalog): record default catalog digests at build time (#4041)
Every `doctor`, `mcp doctor` and MCP server that reports or compares its
catalog identity rebuilt all advertised JSON Schemas and canonicalized
them to compute the default digests, about 200 ms of CPU per process.
The default (full input and output schema) catalog is a deterministic
function of the compiled contracts and pinned schema converter, so
compilation now records it once into dist and the runtime reuses it.
Compact and output-schema-off identities, and source runs, still compute
their digests on first use.

Refs #3813

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 02:19:38 -07:00
d7651b6a0d fix(javascript): preserve mapped runtime digest disagreements
Fixes #3316

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
2026-10-15 17:04:49 +08:00
YsopwnableandKaoru0822 e18fccc548 fix(ci): reject false browser environment cancellation passes
Require typed cancellation and confirmed external-browser cleanup in the
real environment verifier. Close its provider and CDP observer while
preserving primary and cleanup failures. Cover six controlled outcomes
through the verifier's actual cancellation path.

Fixes #4033

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-15 16:54:58 +08:00
morluto e45eb33f03 fix(ci): await complete browser fixture restoration
Wait for the restored document's matching CDP load event before closing the observer. Drain storage verifier providers and surface cleanup failures so the next scenario receives a settled page.
2026-10-15 16:47:29 +08:00
337d2e33c5 fix(kuna): preserve ownership through cancellation and failed verification (#4029)
Let individual requests cancel their shared assembly-index wait while the session aborts and drains the owned producer. Preserve verifier workspaces until provider, transport and process cleanup are confirmed; report failed runs with original errors and lineage evidence.

Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-15 08:26:13 +00:00
6688ed6db7 fix(javascript): retain standalone GraphQL source operations
Retain named operations in GraphQL Source bodies with locationOffset metadata.
Use final property ownership and parsed lexical facts to suppress copies only
when a recovered document, its containers and source aliases have proven
private metadata ownership. Preserve independent observations after writes,
exports, opaque uses, classic-script global exposure or direct evaluation.

Share interpretation across application and captured-web analysis. Build
optional reference indexes once per source and checkpoint examined records,
including suppressed findings, to retain cooperative cancellation.

Fixes #3311

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
2026-10-15 15:59:42 +08:00
c1fd496e6a feat(kuna): serve procedures, pseudocode and assembly from Kuna exports (#3854)
* feat(kuna): serve procedures, pseudocode and assembly from Kuna exports

Register Kuna as a bring-your-own deep-analysis provider. It answers
list_documents, list_procedures, search_procedures, procedure_address,
address_name, procedure_pseudo_code, procedure_assembly and
analyze_function (and batch_decompile through composition) from the
verified project export. While a cold export builds in the background,
pseudocode comes from a single-function `kuna decompile` run on a private
copy and says so; assembly and data names wait for the export.

Each answer records its source and file span in raw_result, limitations
are scoped per operation, and the session-owned export is stopped on
close unless another session still waits for it. The literal/regex
procedure search predicate moves from the IDA runner into a shared
domain helper. `npm run verify:kuna` adds a real-provider lane with an
optional Ghidra comparison.

Refs #1957

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(kuna): refuse a Ghidra comparison without caller-built targets

Refs #1957

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(kuna): retry owned cleanup and keep answers bound to the opened Kuna

- Re-verify the Kuna executable's digest before every launch (memoized by
  file identity), so no answer names one build while another produced it.
- Keep the process supervisor of an unproven stop, and retry it on every
  later close in both the session and the cache; a failed close keeps
  failing until the stop is proven instead of reporting success.
- Report an abandoned export's retained cleanup from session close.
- Forget and rebuild an adopted export whose files another REA process
  evicted, instead of failing every later read.
- Close a private copy whose setup failed, and retry failed removals.
- Let the session own the shared quick inventory; each caller only stops
  waiting when it cancels.
- Report a failed export as `failed` with its reason.
- Lane: release the MCP client when open_binary rejects a target, and accept
  either valid source for the first answer.

Refs #1957

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(kuna): drain session requests before releasing workspaces

Combine caller cancellation with session shutdown and wait for active reads before releasing their process supervisors or private inputs. Retain unproven cleanup for later close attempts. Measure UTF-8 reply bytes without implying model token usage.

Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>

* perf(kuna): keep document metadata reads free of target analysis

Listing the selected path no longer initializes the project cache or starts an export that the request never consumes.

Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>

* fix(kuna): reject verification matrices with missing functions

Validate requested matrix coverage before building the fixture or opening providers. Empty matrices, unmatched names and partially matched function sets must fail instead of reporting an unperformed comparison as successful.

Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>

* fix(kuna): retain component provenance in function dossiers

Record each source facet as one file span and preserve both pseudocode and assembly digests/ranges in combined reports. Keep direct text source fields compatible and verify every combined span through the real MCP lane.

Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>

* fix(kuna): keep retired file cleanup bound to its export

Retain the export key with each failed file removal so a cancelled session reports and retries its own stopped resources. Preserve that association across sweeping and process-cleanup recovery without attributing unrelated cache failures to the session.

Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>

* docs(kuna): document the Kuna provider and list it in the catalog (#3855)

* docs(kuna): document the Kuna provider and list it in the catalog

Add the Kuna provider guide (configuration, answered tools, cold and warm
paths, provenance, cache ownership, verification lane), link it from the
README and its translations, the CLI, installation and index pages and the
site navigation, describe `npm run verify:kuna` in the testing guide, credit
Kuna in THIRD_PARTY_NOTICES, teach the agent skill when to select Kuna, and
register its identity and operations in the generated product catalog.

Refs #1957

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(kuna): state the Linux cache fallback, raw_result scope and comparison inputs

Refs #1957

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(kuna): note that cache pins hold within one REA process

Refs #1957

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(kuna): describe retired staging and retried cleanup

Refs #1957

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(kuna): supervise regex and bind reads to one export

Share a cancellable ECMAScript matching worker with Hopper while preserving its error contract. Bind C and assembly indexes to immutable project readers, retry whole dossiers after eviction, and enforce the existing buffered-text capacity before allocating cached spans. Document the verified Linux scope and keep complete exports accessible.

Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: N0zoM1z0 <161784452+N0zoM1z0@users.noreply.github.com>
Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
2026-10-15 00:53:26 -07:00
Kaoru0822andClaude Opus 5.5 6fcfc5b5e0 fix(browser): keep malformed Electron CDP inventories incomplete (#3901)
inspect-electron-page read missing or non-array DOMSnapshot documents and
node arrays, frame-tree resource lists, and Target.getTargets target lists
as empty inventories while reporting complete_within_window.

The web capture path already validated these replies. Its parsing now has
one shared owner in CdpCaptureDocuments: snapshotDocuments,
snapshotDocumentNodes and frameTreeResources report unreadable parts
through a callback, and both the web and Electron inspections consume
them. Worker discovery records a missing target list or non-record entry as
an invalid_protocol_value exclusion. The Electron copies of this parsing are
removed.

Fixes #3379

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 00:09:38 -07:00
pablodiazjorge 35e17a7172 fix(captures): admit schema-valid HAR numbers that evidence cannot publish exactly (#3159)
* fix(captures): admit schema-valid HAR numbers that evidence cannot publish exactly

* fix(captures): judge overflowing HAR integers by their lexeme
2026-10-15 16:09:07 +09:00
pablodiazjorge f8f3593a8c test: resolve script paths from file URLs with fileURLToPath on Windows (#3266)
* test: resolve worker and CI script paths with fileURLToPath on Windows

* test: skip the fake gh lookup failure on Windows

* test: resolve the release helper and Hopper probe paths with fileURLToPath
2026-10-15 16:07:57 +09:00
Aaditya ed4a1c7d95 docs(installation): list every client that first shipped in 6.5.0 (#3465)
docs(installation): list every client missing from npm 6.4.0

The installation guide named only dsh, or dsh, kimi and workbuddy, as
needing a newer release. The rea-agents-6.4.0 tag also lacks droid,
qoder, trae and zcode, so setup against @latest rejects all seven as an
unsupported agent integration.

List all seven client IDs in both the registry-check note and the
supported-agents table note.
2026-10-15 16:07:42 +09:00
Wade 7a83af3cba fix(process): keep observed symlink targets in filesystem snapshots (#3418)
Snapshots stored resolve(dirname(link), readlink(link)), a lexical
absolute path. Two different targets such as aaa/../target and
bbb/../target collapse to the same value, and with equal lengths the
before/after comparison reported a retargeted link as unchanged even
when the new link is broken. Store the target exactly as readlink
returns it.

Fixes #3303
2026-10-15 16:07:28 +09:00
Ysopwnable f5b1dce881 docs: celebrate 250,000 GitHub stars 🎉 (#3971) 2026-10-15 15:07:07 +08:00
Rudy Mizrahi CelekliandClaude Sonnet 5.5 73c72d5745 fix(mcp): keep a snapshot open ahead of later calls on the same connection (#3661)
open_binary read snapshot_path before it entered the session's lifecycle
order, so a current_document or close_binary dispatched after it could
run first: the read returned Evidence for the previous target and the
close left the snapshot target open. The session now loads the snapshot
inside the open's own turn.

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-15 16:05:44 +09:00
ShuaixinHuang ce89706145 fix(javascript): preserve exact static property keys (#3480)
* fix(javascript): preserve Electron options with static empty keys

* fix(javascript): retain exact computed member keys
2026-10-15 15:59:06 +09:00
Harsh Asalkar 3e88e21705 fix(electron): reject active scenario capture when inspect fuse is disabled (#3453) (#3605)
* feat(electron): add EnableNodeCliInspectArguments fuse pre-flight helper

* fix(electron): reject active scenario capture when inspect fuse is disabled (#3453)

* docs(electron): document inspect fuse pre-flight refusal for scenario capture (#3453)

* test(electron): add PE, ELF, and abort-signal coverage for fuse pre-flight (#3453)

* fix(electron): resolve macOS framework binary and enforce startup deadline on fuse preflight (#3453)
2026-10-15 15:57:42 +09:00
Wade d94ef22d70 fix(cli): route app bundles with trailing separators to native analysis (#3511) 2026-10-15 15:55:44 +09:00
Lucien c61a24b8c5 fix(browser): bound CDP handshakes and command responses (#3555)
* fix(browser): bound CDP handshakes and command responses

* test(browser): expire stalled deadlines after fixture readiness

* docs(browser): explain handshake deadline ownership
2026-10-15 15:55:29 +09:00
Kaoru0822andClaude Opus 5.5 fc712b3750 fix(javascript): keep releases through a replaced handle as candidates (#3897)
A release call was linked to its acquisition through the receiver's lexical
binding alone, so `stream = { destroy() {} }; stream.destroy()` still
produced a resolved `releases` edge to the original stream and entered the
default ownership trace.

A release is now complete only when every value written to the handle
binding is the linked acquisition itself (through parentheses, TypeScript
wrappers or `await`) or a nullish placeholder such as `let h = null`, which
cannot be released. Any other write, conditional replacement, or
destructuring keeps the linked release as a candidate (partial).

Fixes #3313

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 23:40:14 -07:00
Kaoru0822andClaude Opus 5.5 ab45bf641e fix(ghidra): refuse PSP targets with unexamined ABI declarations (#3904)
The generic Ghidra MIPS lane refuses a target whose ABI declaration
inspection is incomplete, such as a section-header table beyond the
captured file, because unexamined headers may hold declarations that
would refuse it. The PSP Allegrex lane skipped that check. It admitted
the target and resolved a supported profile while target inspection
said those declarations remained unknown.

The incomplete-declaration rule is now one shared helper,
ghidraMipsIncompleteDeclarationsReason, used by both the generic MIPS
and the PSP lanes.

Fixes #3329

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 23:30:12 -07:00
Kaoru0822andClaude Opus 5.5 57f30029b1 fix(javascript): reject Webpack getters replaced through any identity write (#3860)
The canonical `n.n` getter proof scanned for direct `loader.n =` assignments
and four Object methods on its own. `Reflect.set`, `Reflect.defineProperty`,
`delete`, destructuring and unknown-key writes left the proof intact, so a
replaced or deleted getter still attributed BrowserWindow calls to Electron.
Any Object method call on the loader also rejected the proof, even for an
unrelated key.

The proof now uses the shared identity-write owner
(`collectJavaScriptIdentityWritesSteps` with the module interop's intrinsic
resolution), as ambient writes already do. A write whose path may select `n`
(the key itself, an unknown key, or the whole loader) counts; exactly one
canonical assignment installed before entry dispatch remains required.

Fixes #3315

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 23:25:18 -07:00
morluto 22ec8bdb2f Merge remote-tracking branch 'origin/main' into codex/ci-followups-20261015 2026-10-15 14:11:13 +08:00
morluto a715fb9e6d test(browser): bound atomic environment polling across navigation 2026-10-15 14:11:13 +08:00
morluto 85788452dd test(cli): batch redundant client lifecycle dry runs 2026-10-15 14:11:13 +08:00
Kaoru0822andClaude Opus 5.5 1b036ecbff fix(flutter): check section-backed symbol tables against PT_LOAD (#3859)
* fix(flutter): check section-backed symbol tables against PT_LOAD

The section-backed ELF path read .dynsym and .dynstr only through their
section file offsets. The stripped PT_DYNAMIC path and snapshot symbols
resolve through the PT_LOAD map. A second PT_LOAD interpretation over an
allocated symbol or string table was therefore ignored, and
inspect-dart-aot reported complete coverage from metadata the loader may
read differently.

An allocated (SHF_ALLOC) dynamic symbol table and its string table now
need a single file-backed PT_LOAD interpretation that matches the section
offset, the same check the dynamic path applies to its tables. Otherwise
the symbol inventory is refused with the mapping reason, and
inspect-dart-aot reports that no recognizable snapshot selection remains.
Non-allocated tables keep their section-offset reading.

Fixes #3346

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(flutter): cover allocated ELF32 symbol-table mapping checks

Add an ELF32 (ARM) counterpart of the synthetic ELF64 image and run the
allocated-table cases for both classes, so the ELF32 section-header reads
in the mapping check are exercised: matching mappings, conflicting symbol
and string mappings, and a disagreeing section offset.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 23:02:49 -07:00
933f216698 feat(kuna): add Kuna readiness, doctor and owned project cache (#3832)
Add caller-supplied Kuna readiness, scoped doctor support and a verified whole-program export cache. Index function byte ranges without introducing analysis-tool responses, and retain unresolved process ownership through cleanup failures. Separate retryable file cleanup from unproven process cleanup.

Verified real Kuna 1.833 on Debian 12 x86-64 with coreutils, SQLite and zlib; 5,207 exported function spans, cold/warm performance, raw-export parity and lifecycle failures. Refs #1957.

Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: N0zoM1z0 <161784452+N0zoM1z0@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 05:56:56 +00:00
Kaoru0822andClaude Opus 5.5 cf0a38ad97 fix(managed): project metadata with empty names without a display label (#3853)
The managed application graph copied metadata names into observation
labels. The graph schema requires a non-empty label or null, so Evidence
whose Module name was the string heap's empty entry made
project-managed-application-graph fail with invalid_request at
observations.0.label. The inspector accepts that Evidence as complete,
and no graph was returned.

Labels derived from metadata names (assembly, module, type, member,
P/Invoke import and native implementation) now become null when the name
is empty. The node's properties keep the source value, so display
formatting no longer rejects identity facts.

Fixes #3352

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 22:22:59 -07:00
a1ca8c76af fix(website): give navigation room and refresh contributors (#3851)
Give the header its own 1320px container while preserving body/article widths, move the complete navigation below the controls on smaller screens, and refresh the dated public contributor snapshot from 214 to 216 accounts.

Validated shared HTML/translations/links/assets and 16 maintenance tests; local Chromium acceptance covered 20 languages, 15 viewport widths and both root/project paths. CI and Greptile review passed.

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
2026-10-15 05:18:54 +00:00
Kaoru0822andClaude Opus 5.5 034342ba28 fix(reference): select a package main before its directory index (#3844)
* fix(reference): select a package main before its directory index

import-reference-source resolved a relative directory import such as
require("./dep") from path candidates alone, so it reported dep/index.js
as the internal, parsed dependency even when dep/package.json declared
"main": "actual.js". Node loads actual.js.

parseReferenceSourceEntries now reads each inventoried package.json once.
When an internal specifier resolves through a directory index, a
non-empty string main is loaded first, as a file and then as a directory
index, as Node does. A main whose target is absent falls back to the
index, matching Node's legacy fallback. An unreadable or invalid
manifest leaves the relationship unresolved instead of asserting the
index.

Fixes #3383

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(reference): resolve package main the way path.resolve does

A trailing separator in main ("lib/") kept directory intent, so the
declared entry selected lib/index.js where Node loads lib.js. An absolute
main was joined under the package directory instead of leaving the
imported root. Strip trailing separators after joining, and report an
absolute main as unresolved. Both cases join the Node-oracle CLI test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 22:02:45 -07:00
Kaoru0822andClaude Opus 5.5 788964d52c perf(javascript): index object literal members for exact-key reference lookup (#3836)
semanticObjectPropertyReferences scanned every property of an object
literal on each call, and member-mutation tracking calls it once per
write, so N writes to an N-property literal cost O(N^2).

For an exact string or number key, a property with another static name
only updates override bookkeeping for that other name, which the key
never reads. Index each literal once by static name, keeping spreads,
computed keys and __proto__ data entries for every key, and walk only
the relevant members in reverse source order. Wildcard and selection
keys keep the full scan.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 21:44:14 -07:00
Kaoru0822andClaude Opus 5.5 acca91d22b fix(javascript): keep members unknown after a write through a local setter (#3831)
* fix(javascript): keep members unknown after a write through a local setter

Assigning to a key that a setter of the binding's own object literal
defines runs that setter, which may replace any member of its receiver.
The member-mutation collector recorded it as an ordinary write to that
one key, so sibling literals such as value: 1 stayed known and
export-shape comparisons reported complete, unchanged shapes.

Record such a write with an unknown key, which invalidates the object's
known members. A computed setter key may match any written key.

Fixes #3312

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(javascript): follow nested setters, skip deletes and cache setter keys

Resolve the written receiver along nested object-literal members so a
write through a nested setter widens that receiver's members. A delete
never calls a setter, so it keeps its exact path. Setter keys are
computed once per object literal, including the no-setter result.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(javascript): ignore setters replaced by a later data property

Build cached setter keys from the effective definitions: a later data
property or method with the same static name replaces the accessor, so
assigning that key no longer runs a setter.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 21:39:39 -07:00
Kaoru0822andClaude Opus 5.5 0323ce0a28 fix(javascript): keep the request endpoint on a uniquely consumed response (#3822)
* fix(javascript): keep the request endpoint on a uniquely consumed response

collectResponseConsumers always produced response-consumer operations
with endpoint null, so a json()/text() consumer that resolved to exactly
one request lost the request's literal endpoint in the semantic graph.

When the consumer resolves to one request through that request's own
result binding, the same condition that already makes it complete, it
now carries that request's endpoint. Ambiguous consumers stay null.

Fixes #3328

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(javascript): drop the endpoint when another write can replace the response

Copy the request's endpoint only when the request is the binding's sole
write; any other assignment, update, loop or destructuring target can
replace the response before the consumer reads it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 21:39:26 -07:00
Kaoru0822andClaude Opus 5.5 089ef732e8 fix(apktool): own the version probe's process group and keep its timeout typed (#3808)
* fix(apktool): own the version probe's process group and keep its timeout typed

The version probe ran its launcher through execFileOutput, which waits
for every inherited output pipe to close. A shell launcher's child that
holds those pipes outlived the stop request, so the probe stayed pending
past its deadline and its grace period, and cancellation hung the same
way. An expired probe also surfaced as a non-retryable unusable-version
refusal.

Run the probe through runOwnedCommand like the decode run, so the owned
process group is stopped with its children. Report its deadline as
AnalysisTimeoutError with timeout_ms and retain an unresolved cleanup
owner for close. Apktool launchers are interpreter scripts, so both the
probe and the decode run prove ownership by run token instead of the
launcher's argv prefix. A close with unresolved process cleanup reports
a typed ProviderCleanupError instead of an untyped AggregateError.

Fixes #3342
Fixes #3347
Fixes #3337

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(apktool): carry unresolved probe cleanup through both provider entry points

A probe failure other than cancellation or timeout, such as an output
overflow whose launcher child survives, now keeps its cleanup reason and
resources on ApktoolConfigurationFailure. inspectAvailability and execute
report it as ProviderCleanupError instead of ordinary unavailability or
capability_unavailable.

Add a real-process lifecycle test: an interpreter launcher starts a child
that holds the inherited output pipes, the probe is cancelled after
launch, and both processes must be stopped within the bound.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(apktool): ignore zombies and always clean up the probe lifecycle fixture

Treat a zombie as exited when checking the launcher and child, bound the
cancellation wait so a regressed probe still reaches cleanup, and stop
any remaining fixture processes in a finally block.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(apktool): treat a failed process inspection as unknown, not stopped

Only ESRCH, a zombie state or ps reporting no match counts as exited; a
ps launch failure leaves the process presumed live so the assertion
fails and the finally block still stops it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 21:37:52 -07:00
Kaoru0822andClaude Opus 5.5 f09f454f45 fix(native): retain valid Objective-C properties beside a malformed record (#3833)
* fix(native): retain valid Objective-C properties beside a malformed record

readObjcPropertyList decoded a whole property_list_t in one operation.
When a later entry's name pointer was unmapped, the list-level catch
discarded the properties already decoded. An empty property name passed
decoding and then failed schema validation of the whole result, so
inspect-native-dispatch-metadata exited 1 with no metadata.

Each property_t entry is now decoded on its own. A failing entry, or an
empty name, becomes a property_decode_failed issue at the entry address,
and sibling properties are retained. Incomplete-attribute issues also
report their entry address. List-header failures still fail only that
list.

Fixes #3336

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(native): cover property record isolation on every platform

Move the synthetic Objective-C property Mach-O into a shared fixture and
decode it through decodeAppleDispatchMetadata in a portable test, so
Linux and Windows runs also catch a regression that drops valid sibling
properties. The macOS CLI cases keep exercising the built command.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 21:22:40 -07:00
70cd4cbfb8 fix(hopper)!: verify current Linux releases with the publisher key
Replace fixed Linux Hopper release pins with current vendor feed metadata,
complete detached-PGP package verification under the stable publisher primary
key, and authenticated package evidence for offline admission. Share parsed
publisher facts across approved setup, doctor and the pre-execution guard.
Preserve existing ownership, cancellation, private-display and cleanup rules.

Fixes #3577

Normalize empty XDG settings, preserve backups during approved cache repair,
and retain publisher cleanup through cancellation and shutdown. Exercise cold
offline verification without reusing process-local proof state. Serialize
approved cache publication across processes, restore old evidence on failure,
and share the existing lease owner from process without changing its identities.

Validation: 438 focused tests (six platform-related skips), full static/documentation checks, real Ubuntu
24.04 official Hopper 6.6.0 setup/CLI/MCP, offline ordinary-user admission,
launcher mutation rejection, and cold/warm verification measurements. Official
RPM metadata mismatch is tracked separately in #3578 and fails closed.

BREAKING CHANGE: Older Linux Hopper builds admitted by the fixed allowlist
now require retained publisher-signed package evidence or an approved update
to the current public demo. Update explicit HOPPER_LAUNCHER_PATH values when
migrating; the live feed exposes only its current release.

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
2026-10-15 12:15:55 +08:00
Kaoru0822andClaude Opus 5.5 b7dcf782aa fix(native): keep property flags unknown for unfinished quoted types (#3825)
An Objective-C property attribute string whose quoted class name never
closes, such as T@"NSString, was treated as complete. The missing N and
R were then read as the Objective-C defaults, so the property was reported
atomic and read-write, and objc_properties_categories coverage stayed
complete. The documented semantics require null flags for incomplete
attribute metadata.

The attribute splitter now reports whether every quote closed. An
incomplete string leaves atomicity and any unestablished read-only value
null, while explicit N or R tokens remain established and the raw type is
preserved. Each affected property adds a property_attributes_incomplete
issue, which marks the facet partial.

Fixes #3345

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 20:42:44 -07:00
Kaoru0822andClaude Opus 5.5 4135c911d3 fix(javascript): keep recovered invalid templates dynamic in semantic effects (#3815)
dataEffectLiteralString asked the literal owner for an exact value, and
when a template recovered with an invalid escape had no cooked value it
fell back to the raw spelling. Child-process commands, request endpoints
and event names then became resolved values that are not JavaScript
strings.

Use only the owner's cooked value; a template without one stays dynamic.

Fixes #3361

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 20:22:16 -07:00
Kaoru0822andClaude Opus 5.5 0781716788 fix(cli): accept --flag=value for global output flags (#3811)
* fix(cli): accept --flag=value for global output flags

`rea <command> --format=json` failed with "Unknown flag: --format".
`--filter-output=...`, `--token-limit=...` and `--token-offset=...`
failed the same way. Incur reads these global flags only in spaced form,
so the equals form fell through to the command parser. Command options
already accept both forms, and `rea mcp doctor` parses `--format=` itself.

A valued output flag with no value (`--format` last, or `--format=`)
also reported the flag as unknown with code UNKNOWN.

normalizeCliOutputArguments, in the module that owns output-argument
interpretation, now splits the equals forms of the four valued output
flags. The entry point passes the normalized argv to rea's output
validation and to Incur, so both read the same effective values. A
missing value now fails with MISSING_OUTPUT_VALUE and names the flag.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(cli): detect missing output values while parsing output flags

The missing-value check scanned argv again after parsing, so it also
checked tokens the parser had already consumed as values. For example,
`--filter-output --format` selects the field `--format`, yet the second
scan reported `--format` as missing a value. parseCliOutputArguments
now records the first valued flag it skips for lack of a value, in the
same pass that consumes values.

Cover normalizeCliOutputArguments directly: values containing `=`, empty
values, and command options that only share a prefix. Export it in the
dispatcher test's runtime stub as well.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 20:22:02 -07:00
Kaoru0822andClaude Opus 5.5 847b7328cf fix(comparison): keep unnamed reference endpoints distinct or unknown (#3809)
* fix(comparison): keep unnamed reference endpoints distinct or unknown

compare_functions projected reference endpoints to procedure names only,
so two edges whose targets had no named procedure both became null and
compared equal, even when their recorded addresses differed.

Identify an unnamed endpoint by its address when both dossiers describe
the same artifact, where that address names one location. Across
artifacts the endpoint has no stable identity, so equal-looking
projections that contain one leave the references dimension unknown
with an explicit limitation. Named endpoints still survive relocation.

Fixes #3388

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(comparison): report known reference-kind changes before unnamed-endpoint uncertainty

Compare known reference kinds first and keep a differing kind as a
change; the unnamed-endpoint limitation now applies only when equality
remains unproven. The kind projection uses the same endpoint identity
as the endpoint projection.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 20:21:49 -07:00
Kaoru0822andClaude Opus 5.5 baad9eebe4 fix(javascript): reject FIFO recovery inputs without blocking (#3788)
* fix(javascript): reject FIFO recovery inputs without blocking

scanRecoveryFile opened the selected path with a blocking O_RDONLY open
before checking that it is a regular file. A FIFO with no writer blocks
that open in the libuv threadpool, so cancellation and the recovery
deadline could not settle the read, and the process could not exit.

Admit the input through the shared openRegularFile owner, which opens
with O_NONBLOCK and verifies a regular file before any read. A FIFO,
directory or socket now fails admission immediately with the existing
regular-file error; symlink rejection and cancellation are unchanged.

Fixes #3332

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(javascript): keep a failed admission close owned on cancellation

Rethrow RegularFileAdmissionFailure before mapping cancellation, so a
descriptor whose close failed stays owned by its error.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 20:21:34 -07:00
Kaoru0822andClaude Opus 5.5 4692bb3330 fix(reference): stop attributing overwritten require members to Node (#3801)
* fix(reference): stop attributing overwritten require members to Node

import-reference-source treated require.resolve(...) and require.main(...)
as Node's resolver even after the file replaced that member, so a literal
argument became a parsed internal dependency that the source never loads.

Attribute member calls only while the loader's resolve and main members
keep Node's identity: no write to either member or a computed member
(assignment, update, delete, destructuring) and no use of require as a
value where a mutator or alias could replace them. Bare require(...) calls
are unaffected.

Fixes #3393

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(reference): keep optional and wrapped loader uses intact

An optional call require?.(...) or a TypeScript or parenthesis wrapper
around require was read as require escaping as a value, which disabled
every member-call link in the file. Look through inert wrappers and
accept optional calls before deciding the loader escaped, and add
controls for bare calls after a member overwrite, a local loader's
member write, and optional calls.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 19:23:06 -07:00
Kaoru0822andClaude Opus 5.5 af6a998066 fix(javascript): keep string Worker paths unresolved without a document base (#3797)
A plain new Worker("./w.js") or navigator.serviceWorker.register("./sw.js")
resolves against the document base URL in a browser, and a Node
worker_threads string resolves against the working directory. Static
analysis was resolving browser strings against the calling script, which
recorded a resolved mapping to the wrong file.

Only new Worker(new URL(path, import.meta.url)) now produces a specifier.
Other worker and service-worker strings keep their expression and stay
unresolved, matching the existing worker_threads string handling.

Fixes #3309

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 19:13:17 -07:00
Kaoru0822andClaude Opus 5.5 7a66470385 fix(apple): report plist dates the decoder cannot represent as unknown (#3793)
plutil writes years past 9999 and negative years in a form the pinned
XML decoder cannot parse, and binary plists can hold dates outside the
JavaScript Date range. The decoder returns an Invalid Date. Its
projection's toISOString() call then threw, so inspect_plist failed the
whole plist as "unreadable_output" with a `rea doctor` remediation.
Keyed archive inspection and Interface Builder decoding rejected the
archive in the same way.

projectPlistValue, the shared projection, now reports such a date as
{ "$plist_type": "date", "iso8601": null } and counts it. inspect_plist,
keyed archive inspection and Interface Builder decoding each add a
counted limitation, as they already do for non-finite reals.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 19:13:01 -07:00
Kaoru0822andClaude Opus 5.5 90c6922905 fix(javascript): keep empty URL path segments significant (#3792)
* fix(javascript): keep empty URL path segments significant

ESM imports and HTML script references were joined with POSIX path
rules, which collapse '//' before applying '..'. URL resolution removes
the empty segment instead, so './assets//../app.js' names assets/app.js,
not app.js, and the graph linked the importer and renderer to the wrong
file. URL references refused every '//' even when dot segments cancel it.

Resolve ESM, HTML (including document base) and URL references through
one URL dot-segment helper. A remaining empty segment stays unavailable,
references above the artifact root are still rejected, and CommonJS keeps
filesystem path semantics.

Fixes #3307

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(javascript): resolve HTML base and reference as one URL path

The base href was turned into a canonical path before the script
reference was applied. A base empty segment that the reference's '..'
would remove was rejected early, and an above-root '..' from the base
became a removable segment that the reference's '..' could cancel.

Keep one URL path state through base and reference resolution, with
above-root escapes held apart from removable segments, and reject empty
segments only in the final path. Remove the superseded htmlBaseDirectory.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 19:12:49 -07:00
Kaoru0822andClaude Opus 5.5 3ded799bc7 fix(reference): never resolve a directory to an extensionless index (#3789)
resolveInternalSpecifier offered posix.join(dir, "index") as the
second candidate, ahead of every extension. With both dep.js and an
extensionless dep/index present, require("./dep") therefore resolved to
dep/index, a file that neither Node nor TypeScript ever selects.

Directory candidates now carry an extension. File and index ordering is
otherwise unchanged, so Node-validated cases such as dep/index.js
winning over dep.cjs keep their meaning.

Fixes #3381

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 19:12:36 -07:00
morluto d69acc3455 fix(windows): retain runtime ownership and clear readonly attributes 2026-10-15 09:29:04 +08:00
Ysopwnable 7d1e4d0e2b fix(browser): release attached emulation overrides (#3778)
* fix(browser): release attached emulation overrides

* fix(browser): preserve unowned environment state

* fix(browser): coordinate attached environment cleanup
2026-10-15 09:21:56 +08:00
Kaoru0822andClaude Opus 5.5 949228b606 perf(javascript): check wrapper IPC duplicates by invocation set (#3783)
The endpoint index from #3779 still scanned every earlier finding at the
same endpoint and serialized each invocation location. One wrapper called
N times for one channel shares an endpoint, so duplicate checks stayed
quadratic in N.

Record per endpoint whether a directly observed send/invoke exists and the
set of serialized invocation locations. The duplicate rule is unchanged and
each check is constant time.

Refs #3670

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 18:20:34 -07:00
Kaoru0822andClaude Opus 5.5 5e8f7c536e fix(cli): stop quietly when the stdout reader closes early (#3781)
* fix(cli): stop quietly when the stdout reader closes early

`rea ... | head` crashed once head closed the pipe. TOON and YAML output
raised an unhandled EPIPE 'error' event with a Node stack trace. JSON
output reported {"code":"UNKNOWN","message":"write EPIPE"} on stderr.
Both exited 1, as if the analysis had failed.

The CLI entry point owns process.stdout, so it now treats EPIPE as the
reader declining the rest of the output. It stops writing, suppresses
the failure report and exits 141 like a SIGPIPE-terminated writer, also
when Incur exits through its own failure path. Other stdout errors are
handled as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(cli): guard mcp doctor output and cover midstream reader closure

The closed-reader guard was only installed for ordinary CLI commands. As a
result `rea mcp doctor | head` still crashed on EPIPE. The guard now lives
in one entry-point function shared by both CLI branches; MCP stdio keeps
its own lifecycle.

The tests now also close the reader after the first chunk of a 4 MiB
streamed JSON result, so later writes in writeJsonOutput are covered.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 18:20:19 -07:00
morluto 18591f78ae Merge remote-tracking branch 'origin/main' into codex/bug-sweep-20261015-final 2026-10-15 09:14:14 +08:00
Kaoru0822andClaude Opus 5.5 44b197f924 fix(javascript): parse JavaScript sources with JavaScript grammar (#3782)
parserPluginsForPath enabled Babel's typescript plugin for .js, .jsx,
.mjs and .cjs sources. TypeScript reads some valid JavaScript
differently: a conditional whose consequent is an arrow with a
parenthesized body (c?x=>(a):y=>(b)) throws as a return-type
annotation, and a<b>(c) becomes a generic call. VS Code's 20 MB
workbench bundles therefore returned no AST at all.

Select JavaScript grammar (JSX plus legacy decorators) for explicit
JavaScript extensions. TypeScript dialects and unnamed sources keep the
existing TypeScript-capable contract.

Fixes #3780
Refs #3670

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 18:12:30 -07:00
morluto 610f873e91 Merge remote-tracking branch 'origin/main' into codex/bug-sweep-20261015-final 2026-10-15 09:01:38 +08:00
Kaoru0822andClaude Opus 5.5 59f528f0e5 perf(javascript): share one definitions pass and index wrapper IPC dedupe (#3779)
Static analysis rebuilt the full lexical definitions index three times per
file (open receivers, RPC client roots, binding lookup) and the Electron
wrapper pass checked each substituted IPC registration against every prior
finding with JSON.stringify, which is quadratic in registrations.

Classify open receivers and RPC client roots in one traversal over a single
definitions-only index, then extend that same state with ambient writes for
binding lookup. Index IPC findings by side, operation, channel and location
so duplicate checks only consult same-endpoint candidates.

Static facts are byte-identical on the measured corpora.

Refs #3670

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 17:59:44 -07:00
morluto c553d1138e fix(windows): clear readonly when no other file attributes remain 2026-10-15 08:49:11 +08:00
morluto 930dd5357f Merge remote-tracking branch 'origin/main' into codex/bug-sweep-20261015-final 2026-10-15 08:44:08 +08:00
Kaoru0822andClaude Opus 5.5 084e540414 fix(native): resolve inspect_plist defaults from the selected target's bundle (#3774)
inspect_plist rooted every file target at the file's own directory, so a
program file under App.app/Contents/MacOS defaulted to
Contents/MacOS/Contents/Info.plist and --relative-path resolved from MacOS/
instead of the app root the CLI documents. A selected plist file likewise
looked for <dir>/Contents/Info.plist instead of reading itself.

A program file directly under a bundle's Contents/MacOS now roots at that
bundle, and a plist artifact target is its own default plist. The missing
default message names the default plist rather than Contents/Info.plist.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 17:40:24 -07:00
morluto db689f3c3c fix(windows): relax owned parent sharing before admission validation 2026-10-15 08:35:11 +08:00
morluto f15ddbea7a Merge remote-tracking branch 'origin/main' into codex/bug-sweep-20261015-final 2026-10-15 08:31:24 +08:00
morluto 68081ac414 fix(windows): grant retained roots the rights required for cleanup 2026-10-15 08:31:22 +08:00
Kaoru0822andClaude Opus 5.5 bddeae5108 fix(managed): charge admitted member facts once in the final projection (#3487)
* fix(managed): charge admitted member facts once in the final projection

inspect_managed_members admits method bodies, signatures, call edges,
field accesses and user strings as they are produced, then admits the
whole result again before returning it. The final pass re-counted every
already-admitted record, so the 64 MiB representation budget was spent
at roughly twice the result's JSON size. Ordinary assemblies such as
MathNet.Numerics, MongoDB.Driver, Npgsql, ClosedXML, System.Reactive and
Azure.Storage.Blobs returned resource-limit with no members.

The budget now remembers each admitted projection. A containing
projection spends that charge once instead of counting the subtree
again; a value that serializes more than once still pays for every
further occurrence. Results that already fit are byte-identical.

Refs #3449

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(managed): check a repeated occurrence pays with fresh credit

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 17:19:33 -07:00
morluto f78d137b42 fix(windows): retain unpublished runtimes after cleanup failure 2026-10-15 08:16:22 +08:00
morluto 1a3a96d36b fix(windows): honor directory readonly semantics during cleanup 2026-10-15 08:16:22 +08:00
Kaoru0822andClaude Opus 5.5 c9764084b8 fix(reconstruction): withhold readiness for manifest references to absent Evidence (#3526)
build_reconstruction_obligation_ledger accepted a manifest original case or
fixture whose Evidence ID was absent from the bundle whenever another entry
of the same case kind was valid. The obligation was reported verified, the
ledger ready, and the dangling ID appeared in evidence_links with no
diagnostic, although the documented contract requires every referenced
Evidence ID to exist in the bundle.

Report each absent original-case or fixture reference as missing-original-case
or missing-case, naming the ID. Any such diagnostic withholds verification and
readiness.

Fixes #3363

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 17:11:40 -07:00
Kaoru0822andClaude Opus 5.5 0f966e2b44 fix(reconstruction): merge repeated observations into one generated obligation (#3524)
Two capture-process records of the same executable and scenario generate
the same obligation ID. addCandidate added their definition counts, so the
ledger reported duplicate-definition, a closure blocker meant for competing
definitions, merely because the claim was observed twice. The merge also
kept only the first record's unavailable_authority and
required_next_evidence.

Generated IDs digest their derivation key, so merging two generated
candidates keeps one definition. A reviewed definition that reuses an ID is
still a duplicate. The merge now unions unavailable_authority and
required_next_evidence from every observation.

Fixes #3375

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 17:11:28 -07:00
Kaoru0822andClaude Opus 5.5 1851f1fcfd fix(managed): report ILONLY-clear assemblies as mixed in boundary summaries (#3516)
inspect_managed_native_boundaries derived summary.mixed_mode_or_native_header
only from the managed native header and the native entry-point flag. A C++/CLI
assembly with ILONLY clear and no ReadyToRun signature was classified as
mixed-clr-native by inspect_managed_artifact but summarized as not mixed.

Give the rule one owner, managedCliIsMixedClrNative in ManagedPeReader, and
use it for both the artifact runtime family and the boundary summary. The
summary now reads the CLI header directly instead of the cli_native projection.

Fixes #3366

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 17:11:12 -07:00
Kaoru0822andClaude Opus 5.5 2b2a5d0f26 fix(native): inspect arm64e.x1 slices by their exact name (#3500)
inspect_macho passed the CPU-family spelling (`arm64e`) for every dotted lipo
slice name to otool, nm, dwarfdump and vtool. That is right for the
pointer-auth ABI version suffix (`arm64e.v1`), but `arm64e.x1` is a distinct
subtype: on a universal x86_64 + arm64e.x1 executable otool failed with
"does not contain architecture: arm64e" and the whole inspection failed.

Strip only the `.v<N>` ABI version suffix and pass other variant names
exactly. vtool has no spelling for a variant subtype, so skip it for such
slices and report that build metadata comes from otool only.

Fixes #3389

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 17:10:58 -07:00
Kaoru0822andClaude Opus 5.5 2a9252e4e6 fix(managed): report metadata range failures at artifact file offsets (#3508)
ManagedMetadataLayout's range check reported offsets relative to the buffer
it was given: the metadata root for version and stream headers, and the
table stream for the tables header, row counts and table data. Other
diagnostics in the same reader already use artifact file offsets, so a
truncated table stream at file offset 888 was reported at offset 24, inside
the DOS header.

Pass each buffer's artifact file offset to the range check so every
invalid-metadata-root issue carries a file offset.

Fixes #3364

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 17:06:06 -07:00
Kaoru0822andClaude Opus 5.5 7666a482b6 fix(adb)!: keep the exact Android long version code (#3485)
inspect_adb_package converted the dumpsys versionCode, a signed 64-bit long
(versionCodeMajor << 32 | versionCode), to a JavaScript number. Values above
2^53 were rounded, so 9007199254740993 was reported as 9007199254740992 with
the same Evidence ID, and negative values became null.

Return version_code as the exact decimal string dumpsys prints, matching the
apktool and JADX version_code contracts.

BREAKING CHANGE: inspect_adb_package version_code is now a decimal string
("45") instead of a JSON number (45). Read it as a string, or parse it with
BigInt when numeric comparison is needed.

Fixes #3368

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 17:01:25 -07:00
morluto fa318995f5 fix(setup): include registered IDA budgets in client deadlines 2026-10-15 07:36:34 +08:00
morluto 9a44fea347 test(mcp): avoid unrelated validator compilation in availability checks 2026-10-15 07:12:53 +08:00
morluto 2819defeea test(cli): verify attributed caveats in composed analysis 2026-10-15 06:56:09 +08:00
morluto 937b4e69c8 fix(cache): invalidate snapshots that lack source caveat semantics 2026-10-15 06:45:53 +08:00
morluto 1b668a527d chore: integrate current main before bug qualification 2026-10-15 06:42:51 +08:00
morluto 1e377e6965 perf(evidence): group repeated caveats with their source operations 2026-10-15 06:39:48 +08:00
morluto fb6e2b07e0 fix(ida): bound readiness requests by the remaining deadline 2026-10-15 06:34:32 +08:00
morluto c4608a2962 fix(evidence): preserve source attribution across composed workflows 2026-10-15 06:33:48 +08:00
morluto 225f26a5f8 refactor(ghidra): give function boundary scans one owner 2026-10-15 06:25:02 +08:00
morluto a004061abc fix(native): preserve provider limitations through call traces 2026-10-15 06:20:20 +08:00
morluto 01a1b3fda6 fix(ghidra): retain function boundary limitations on references 2026-10-15 06:20:20 +08:00
Ysopwnable 3d2846f237 fix(ghidra): invalidate synthetic entry reference snapshots (#3728)
fix(ghidra): exclude synthetic entry references
2026-10-15 06:14:29 +08:00
morluto 709e99d781 fix(windows): admit volume roots with existing write handles 2026-10-15 06:12:23 +08:00
morluto bb49252523 test(ida): verify typed timeout and retained cleanup through the CLI 2026-10-15 05:38:36 +08:00
morluto 4946688c33 refactor(setup): distinguish provider configuration from registration settings 2026-10-15 05:23:04 +08:00
morluto 333e0ab0df fix(setup): propagate selected provider deadlines through CLI registrations 2026-10-15 05:16:08 +08:00
morluto c0cfccb45f fix(ida): preserve SDK request timeout identity and captured output 2026-10-15 05:14:57 +08:00
morluto fbdd040324 docs(mcp): explain absolute SDK deadlines and client timeout configuration 2026-10-15 05:05:30 +08:00
morluto dadf909dce fix(setup): derive MCP tool deadlines and preserve caller timeout choices 2026-10-15 05:05:24 +08:00
morluto 097a4bce6f fix(jeb): make upstream request deadlines configurable and preserve cancellation 2026-10-15 05:05:18 +08:00
morluto c831c23633 docs(mcp): clarify gateway schema recovery without inferring backend constraints 2026-10-15 04:49:27 +08:00
morluto 5a46fbacd5 fix(mcp): advertise explicit target cleanup when investigations finish 2026-10-15 04:49:27 +08:00
morluto fffb074193 chore: integrate current main verification fixes 2026-10-15 04:06:41 +08:00
morluto 09e7f07108 fix(setup): select compact input schemas for Kimi registrations 2026-10-15 04:06:41 +08:00
morluto 1bbf4bd16c fix(process): preserve caller overrides during Windows environment normalization 2026-10-15 04:06:41 +08:00
morluto 07a501e82c fix(mcp): share inspection admission for prompt completion 2026-10-15 04:06:41 +08:00
morluto ebecf06f6e fix(setup): integrate Windows runner and ConPTY corrections 2026-10-15 04:04:17 +08:00
morluto 7e0c7b1244 fix(cutter): resolve bridge paths from the selected caller home 2026-10-15 04:01:36 +08:00
Ysopwnable d9cd239c5e fix(test): honor Ghidra startup budgets in Windows verification (#3417)
* fix(test): honor Ghidra startup budgets in file-offset verification

* fix(test): preserve selected startup budgets in direct Ghidra verification

* test(ghidra): cover verifier timeout budgets

* fix(test): preserve Ghidra MCP request budgets
2026-10-15 03:50:50 +08:00
morluto 11f64f653e test(browser): verify typed worker refusals across navigations 2026-10-15 03:43:29 +08:00
morluto 9b7ec809e6 test(process): use owned host-aware settlement fixtures 2026-10-15 03:43:29 +08:00
morluto 170bfc7b3f fix(mcp): authenticate inventory facts before prompt completion 2026-10-15 03:43:29 +08:00
morluto 591c87c4b6 refactor(artifacts): share inspection validation and prune legacy fixtures 2026-10-15 03:43:29 +08:00
morluto bfd76c5d16 test: isolate host discovery and update current capture fixtures 2026-10-15 03:43:29 +08:00
morluto 9ac91e0c1e fix(artifacts): bind inspection provenance to every retained inventory 2026-10-15 03:43:29 +08:00
morluto 30a9139b87 fix(javascript): keep source-module semantics independent of sibling bytes 2026-10-15 03:43:29 +08:00
morluto a9588be4eb fix(evidence)!: reject missing recorded inventory and capture facts
BREAKING CHANGE: Inventory Evidence must explicitly report integrity_contradictions, and browser capture Evidence must report every resolved budget. Caller input defaults remain supported; stored observations are never filled with inferred defaults.
2026-10-15 03:43:29 +08:00
morluto fc789d19ff style(apple): normalize class parser module formatting 2026-10-15 03:43:29 +08:00
morluto 4ff2dadf66 refactor(apple): remove unused class identity re-export 2026-10-15 03:43:29 +08:00
morluto 1a06c82772 style(test): format coverage regression fixtures 2026-10-15 03:43:29 +08:00
morluto 7d885d632c docs: align workflow recovery and current Evidence contracts 2026-10-15 03:43:29 +08:00
morluto 57a23f0a2d fix(browser)!: expose capture budgets and queue admitted content reads
BREAKING CHANGE: Browser capture results require resolved limits, event families, and omission facts. Caller-selected budgets replace hidden retention ceilings; admitted content reads queue behind bounded concurrency.
2026-10-15 03:43:29 +08:00
morluto d2a53beb3d test(hopper): verify external exit recovery and retained ownership 2026-10-15 03:43:29 +08:00
morluto a8887a9a85 fix(jeb): probe live operations with owned discovery deadlines 2026-10-15 03:43:29 +08:00
morluto 45d5e80144 fix(cli): preserve Cutter choices and reject invalid runtime arguments 2026-10-15 03:43:29 +08:00
morluto 50dd9fcad1 fix(ghidra): preserve MIPS overrides and filter entry metadata 2026-10-15 03:43:29 +08:00
morluto 6add17a317 fix(windows): explain missing native artifacts and verify output drain 2026-10-15 03:43:29 +08:00
morluto 8c0e431895 fix(setup): own Hopper replacement staging and committed outcomes 2026-10-15 03:43:29 +08:00
morluto ceb77f6697 fix(apple): distinguish JVM resources and UIKit event connections 2026-10-15 03:43:29 +08:00
morluto fc7e41ff3b fix(process): attribute finite trace omissions and cleanup states 2026-10-15 03:43:29 +08:00
morluto cb4ff50202 fix(functions): preserve coverage diagnostics separately from blockers 2026-10-15 03:43:28 +08:00
morluto b47055edbe fix(javascript): preserve module identity and boundary fields 2026-10-15 03:43:28 +08:00
morluto 0405c9d361 fix(artifacts)!: authenticate complete inspection and summary inputs
BREAKING CHANGE: Artifact inventories require scoped coverage. Obsolete coverage_status and prose-based coverage inference are removed. Inspection carriers authenticate every retained inventory substep.
2026-10-15 03:43:28 +08:00
morluto db8128dc0d refactor(evidence)!: require current coverage commitments
BREAKING CHANGE: Evidence and analysis snapshots require explicit nullable relationship coverage and include it in their identity commitments. Historical records without the field are rejected; saved files are not migrated.
2026-10-15 03:43:28 +08:00
Swatantra Yadav 2abfe53dbd fix(setup): preserve overridden runner variables and test isolation on Windows
Support case-insensitive environment value lookup in ClientRegistrationIdentity while preserving JavaScript property order precedence with findLast(). Add unit regression coverage for ambient uppercase npm variables on Windows. Isolate USERPROFILE in cliSetup tests so child processes do not touch the host profile directory. Harden JSON decision extraction in runJourney against ConPTY trailing ANSI escape codes. Deterministically sequence multiselect prompt navigation before confirmation. Filter ambient runner variables case-insensitively before launching interactive setup completion tests.
2026-10-15 00:56:31 +05:30
qorvrandqorvr efbc486b9b fix(contracts): reject NUL bytes in keyed archive and extraction paths (#3606)
Co-authored-by: qorvr <330186599+qorvr@users.noreply.github.com>
2026-10-15 03:14:31 +08:00
11b747f29d fix(analysis-view): unify inert inline Evidence admission
Share inline Evidence admission before authentication across analysis views.
Reject executable object behavior before reflection, inspect arrays
incrementally, preserve inert JSON-omitted metadata and existing budgets.
Keep precise error locations with one bounded path stack.

Validated base failures, 48 source tests, 71 related public-boundary tests,
27 final CLI/MCP tests and eleven calls against a genuine Chromium HAR;
source identity, selected output and capacity accounting remain preserved.

Co-authored-by: Zhong-fan <205188959+Zhong-fan@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
2026-10-15 03:13:28 +08:00
鸡哥 75104040df fix(javascript): track public CommonJS export receiver lifetimes (#3567)
* fix(javascript): track public CommonJS export receiver lifetimes

* fix(javascript): retain exports across uncertain identity effects
2026-10-15 02:46:14 +08:00
357996cc89 test(perf): measure genuine SAZ selection and retained recovery (#3584)
* test(perf): pin genuine Fiddler captures and raw HTTP oracles

Pin two public Microsoft SAZ captures with 13 and 1311 sessions, immutable source/digest/member coverage and predeclared caller-ordered transaction questions. Independently preserve stored HTTP headers, binary/empty bodies and archive coordinates; admit only known corpus bytes before ZIP allocation. Five reader regressions and both genuine selected-record checks pass. No production or performance-gain claim.

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>

* test(perf): measure SAZ selection and retained recovery workflows

Add authenticated complete/ordinal/retained-page SDK experiments, fixed real-Codex prompts and independent cited-byte grading. Preserve actual failed RPCs and exact retained-reference recovery when complete MCP delivery exceeds its receive buffer. Verify selected CLI/MCP facts, parameters and semantic Evidence identity. Keep a genuine partial-message admission-negative probe. Focused 12 tests and 10 actual-Evidence grading cases pass; no formal model or gain claim yet.

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>

* test(perf): verify resolved SAZ Evidence selection parameters

Distinguish omitted request ordinals from the complete resolved ordinal scope recorded by WebNetworkCaptureService. Share the expected sanitized Evidence representation between SDK checks and model grading; preserve original caller arguments. A focused regression and both saved genuine baseline RPCs confirm the correction. Keep original collector failures and costs unchanged; freeze a new driver before repeated measurements.

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>

* docs(perf): retain mixed SAZ whole-task costs and failure boundaries

Record both SDK batches, twelve corrected workflows, all four actual Codex attempts, independent byte equivalence and verified payload-phase CPU/memory. Distinguish successful-analysis MCP transport limits from real provider timeouts and negative partial-message admission. Three model questions complete; the only completed pair saves 1.6 percent input but takes 2 percent longer, with no consistent benefit. Keep production APIs/defaults/cache unchanged and preserve all deviations, failed attempts and unknowns.

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>

---------

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
2026-10-15 02:45:44 +08:00
e79e014f23 fix(browser): preserve passive network inventory uncertainty
Preserve uncertainty in offline passive network comparisons when request
identity, metadata, or per-kind inventory coverage is incomplete. Retain
proven HTTP/WebSocket changes without using missing information to infer
membership, duplicate counts, or equality.

Compare redirect resource types only where both observations retain them.
Keep metadata completeness with its owner, stream HTTP content digests,
and require full coverage for multiplicity and WebSocket stream inventories.
Preserve independently known metadata changes and full-inventory counts.

Keep the original JavaScript configuration-escape, JSON, cancellation,
SAZ and schema-documentation fixes compatible with current main. Split
public CLI comparisons into isolated test cases without loosening timeouts.
Validate actual Chromium/HTTP captures through CLI and real stdio SDK,
with explicit limits and unchanged source bytes.

Co-authored-by: To3akaRin <131595336+To3akaRin@users.noreply.github.com>
Co-authored-by: N0zoM1z0 <161784452+N0zoM1z0@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
2026-10-15 02:28:46 +08:00
morluto b8510ee00b test(mcp): remove cache and event order dependence from fault probes
Inject IDA diagnostic capacity faults per connection so prior module imports cannot bypass them. Use the CDP fixture event barrier before asserting complete body shapes, and document profile and renderer verification.
2026-10-15 01:41:31 +08:00
morluto 36615d9165 fix(mcp): honor Frida partial evidence retention acknowledgments
Use shared recorded-partial delivery on start, load and instrument errors. Return a retained reference only after a successful write; preserve the original inline partial when retention fails or is unavailable.
2026-10-15 01:41:31 +08:00
morluto c5fb86d194 fix(mcp): bind catalog identity to output schema profiles
Include the selected output advertisement profile while retaining canonical output validation in the identity. Reuse the SDK output projection and select doctor expectations from both profiles. Correct the compact input error documentation.
2026-10-15 01:41:31 +08:00
morluto 0df0e4f53e fix(contracts): honor options outside the schema projection cache
Bypass target-only memoization when Standard JSON Schema library options are supplied. Verify shared and inline projections remain independent in either call order.
2026-10-15 01:41:31 +08:00
morluto 204b9f3ac1 fix(contracts): preserve array guidance across MCP schema profiles
Keep item schemas when compact inputs reduce nested structure, expose literal tuple elements to items-first renderers, and preserve SQLite table-selection guidance across compaction. Verify catalog-wide client constraints and canonical rejection before dispatch. Document the stock Pi renderer size limit and caller-selected recovery.
2026-10-15 01:41:31 +08:00
Dery Ferdika a732c859dd fix(contracts): reject NUL bytes in Frida, Rizin, and GDB local path inputs (#3575)
Route the caller-selected local path fields in load_frida_script,
instrument_with_frida, start_frida_session, start_rizin_debug_session,
and start_gdb_session through the shared localPathStringSchema so a NUL
byte is refused at the advertised contract boundary instead of reaching
the filesystem or a provider subprocess and surfacing as a raw error.

Extend the OS-invalid-local-input acceptance harness to the tools that
were missing from it.
2026-10-15 01:17:54 +08:00
morluto 10aa74dfed fix(mcp): preserve tuple constraints in advertised schemas
Declare fixed tuple tails and lengths at the canonical boundary so client array validation accepts every tool. Preserve nonempty candidate constraints in managed and application comparisons. Add catalog-wide client checks and parser/schema parity coverage for both dialects and projection directions.

Fixes #3576
2026-10-15 01:12:46 +08:00
93e75bc8fe fix(setup): retain Hermes YAML scalar source during updates
Retain source nodes for unchanged scalar values and string keys when
replacing a Hermes registration. Discarding quoted YAML 1.2 keys could
make Hermes' C reader reject duplicate boolean keys and switch to the
pure reader, changing the interpretation of unrelated settings.

Use linear mapping lookup, quote newly created YAML 1.2 strings for
YAML 1.1 compatibility, and preserve direct numeric edit formatting.
Extend actual CLI coverage for both existing reader-selection directions;
verify strings, exact unrelated numeric/timestamp values, backups and
idempotence with the actual Hermes Python loader.

Follow-up to #3102 and main's independent Hermes scalar fix.

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
2026-10-15 00:57:57 +08:00
1579b6865e test(electron): isolate active-hook fixture resolution (#3565)
Stage the real active hook and boundary helper next to a fixture-owned CommonJS Electron package so preload and exercise resolve the same module. Remove NODE_PATH reliance, retain capacity/preload/fatal-exit coverage, and prove isolation against a conflicting ancestor package.

Fixes #3473

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
2026-10-15 00:44:48 +08:00
569ef4159d feat(website): add contributors and complete the Greek edition (#3520)
Add an equal, searchable directory of 214 contributors and translate its shared
navigation and copy. Complete the Greek website edition (1728/1728 messages),
preserving source examples, protected commands and evidence.

Publish refreshed contributor snapshots atomically and cover partial writes,
denied replacement and staging cleanup with failure regressions.

Validation: complete website checks and 16 maintenance tests, strict Greek
coverage, desktop/mobile and no-JavaScript journeys, selected PR CI and Greptile
follow-up. Codex automated review was unavailable because its quota was exhausted.
No analysis-provider behavior or production website publication is included.

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Assisted-by: Suika (gensokyo-skills:suika-scatter-gather-planning)
Assisted-by: Kogasa (gensokyo-skills:kogasa-surprise-testing)
Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
2026-10-14 16:21:24 +00:00
0f623732bd fix(ci): require portable inventory reports on selected runs (#3549)
Run complete portable-inventory validation whenever code tests are selected and the job is not cancelled. Missing timing reports now fail rather than bypassing the existing validator. Preserve partial diagnostics and successful-history ownership.

Verified the baseline regression, missing/present report and cancellation/selection controls, actual Vitest blob/report failure paths, static checks, actionlint and test discovery.

Fixes #3542

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
2026-10-15 00:16:09 +08:00
044fb9127a fix(hopper): skip non-file launcher candidates
Require automatically discovered Hopper launchers to be executable regular
files so POSIX directory search permission cannot hide a later valid
application bundle. Preserve file symlinks, explicit caller configuration,
and the shared candidate ordering.

Cover canonical and renamed user bundles through the production filesystem
predicate; respect existing canonical or renamed system installations in
the isolated fixtures. Clarify launcher eligibility in the installation guide.

Follow-up to #3085 and #3054 after their shared path discovery landed on main.

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
2026-10-15 00:09:42 +08:00
1438422dfe fix(managed): preserve enclosing identity in member comparisons (#3079)
Keep same-named nested members paired by their source-owned enclosing identity, including selected inventories that omit the parent TypeDefs. Share the cached, bounded nominal type-name digest owner with signature resolution; preserve current unknown identity through matching, remapping and absence instead of repairing it with display names or historical compatibility.

Verification: 457 related tests passed after preserving TypeDef visibility/parent applicability and coherent fixtures; the complete static/architecture gate and real pinned-SDK MCP nested/official .NET DLL workflows passed after a sigrok-experiment-only main refresh. Documentation checks passed after the compatibility fix. Full CoreLib inspection retains the pre-existing explicit representation limit. No target assembly execution; local verification used two CPUs and an 8 GiB RSS guard.

Co-authored-by: 8rulerstar <190670431+8rulerstar@users.noreply.github.com>
Co-authored-by: N0zoM1z0 <161784452+N0zoM1z0@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-14 23:20:07 +08:00
YsopwnableandKaoru0822 eb44921c4c fix(ci): invalidate GitHub entrypoint and package-scope caches (#3522)
Track GitHub package scopes and Knip workflow/action entrypoints in static-check cache keys. Preserve the upstream action-code ownership fix and restrict TypeScript to its actual typed inventory.

Real cold/warm regression tests retain cache reuse for unrelated inputs while rejecting changed source, declarations, package scopes, manifests and reachable module dependencies. Existing lint inventory and report transport coverage remain unchanged.

Fixes #3510

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-14 23:15:04 +08:00
ca1b2f8608 test(perf): measure real sigrok annotation selection workflows
Pin two public sigrok recordings and independently verify raw waveform bytes/sample locations through real CLI/MCP selection workflows. Record all eight actual Codex task costs, including a fixed-plan deviation and mixed whole-task token/time outcomes. Existing annotation controls reduce reply bytes; the cohort does not justify a new production cache/default/API change.

Harden corpus identity, UART data-bit coverage, I2C interval ownership and preallocation ZIP/metadata admission. Focused tests and original Evidence/transcript replays retain observed facts, outcomes, provenance and resource limitations.

Fixes #3131.

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-14 22:56:25 +08:00
Ysopwnable 0f55f247cd fix(ghidra): preserve PE PDB coverage facts (#2933)
Preserve reported Program PDB identity, explicit omissions and nullable load state, retaining declared CodeView facts when equivalence is unknown. Bound JSON locator expansion and advance PE/workflow replay revisions without discarding historical Evidence.

Main already contains the shared canonical workflow collector. Integrate its unknown-PDB repair and fix the CLI fixture for the new artifact-session dependency. No unrelated pcap repair is included.

Validation: 309 focused tests across 15 files pass (2 Windows-only skips), runtime build and check:fast/pre-push pass. Real Windows Ghidra 12.1.4 PUBLIC/JDK 21.0.12.1 uses the public where.exe/PDB pair; 17 relevant production files match verified ca24903f. MCP cold/warm normalized results match and the backward graph has 3 successful nodes. Inputs are unchanged and no owned Java/Node processes remain. CLI inspection keeps its earlier exact-source scope. NB10 omission uses regression fixtures, without a real NB10 provider claim.

Tests keep 2-core affinity and 512 MiB Node/Java heaps. Retain the 600s analysis and 30s initialization timeout observations alongside the successful test-only extended-budget run; no product-default or cross-revision speedup claim.

Fixes #2259
2026-10-14 22:25:05 +08:00
morluto 6060de329d fix(release): rehearse qualified packages before publication 2026-10-14 21:51:19 +08:00
morluto c0aa2e32b3 fix(ci): include GitHub action code in static cache inputs
Invalidate typecheck, lint, built lint and Knip when action implementations or declarations change. This closes the real checker inventory gap detected by staticCheckReports on main.
2026-10-14 21:42:54 +08:00
eab789aed7 fix(managed): compare nominal signatures across metadata builds (#3059)
Compare managed member signatures using bounded, framed nominal type identities rather than build-local metadata row numbers. Preserve unresolved and historical evidence semantics, and share TypeRef, nesting and member-list facts at their producing boundaries.

Validation: current-head CI and Greptile pass; all 435 related managed tests pass. Genuine Microsoft .NET DLL CLI comparison and bounded SDK MCP paging pass without modifying targets. A local CLI filter deadline also fails on clean main; no unrelated timeout or product change is included.

Co-authored-by: JayOfTheKeyboard <140487034+JayOfTheKeyboard@users.noreply.github.com>
Co-authored-by: N0zoM1z0 <161784452+N0zoM1z0@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-14 21:16:48 +08:00
morluto 790657dc0d fix(release): recover verified partial registry publications
Validate original exact-source qualification, release tag, retained package provenance and npm integrity before the corrected public-package canary. Publish only the existing release's MCP metadata through a shared idempotent publisher; never recreate tags or npm versions.
2026-10-14 21:15:34 +08:00
morluto 0b4fd11c8c fix(release): install the npm canary before its MCP handshake
Complete the isolated published installation before starting the protocol deadline, disable dependency lifecycle scripts consistently with package verification, and reuse that installation and cache for update validation.
2026-10-14 21:12:22 +08:00
morluto c99c6a89d6 fix(release): restore qualified runtime before the npm canary
The published-package verifier imports contracts from dist in the release checkout. Restore the exact qualification run's runtime artifact before publication so those dependencies are available without rebuilding the package.
2026-10-14 21:05:20 +08:00
3b79af28e8 chore(main): release rea-agents 6.5.0 (#1693)
* chore(main): release rea-agents 6.5.0

* docs(release): finalize contributor-aware 6.5.0 notes

* docs(release): retain verifier maintenance only in provenance

* style(release): format the contribution inventory

* docs(release): credit Windows fixes

* fix(comparison): recheck historical calls-only changes (#2223)

* fix(comparison): preserve incomplete function call evidence

Keep missing, unknown, partial and unresolved call relationships unknown in function comparisons and reconstruction claims. Aggregate IDA caller and callee coverage without overwriting uncertainty.

Validate with 144 focused tests, typecheck/lint, docs checks and test discovery. Real providers were not verified; the full headless IDA session suite requires unavailable Windows native controls.

* fix(comparison): recheck historical calls-only changes

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>

* fix(ci): isolate Linux Hopper process fixture inventory

* docs(release): credit selected-null and client setup changes

* fix(release): refresh 6.5.0 notes and align installation examples

* docs(release): clarify 6.5.0 migrations and recent fixes

* docs(release): cover omitted capabilities and refresh 6.5.0 provenance

* docs(release): pin final CI corrections in 6.5.0 inventory

* docs(release): include mutation scheduling in 6.5.0 provenance

* docs(release): include cache policy correction in 6.5.0 provenance

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
Co-authored-by: Codex <codex@users.noreply.github.com>
Co-authored-by: Lucien <164857476+dakjdakd@users.noreply.github.com>
2026-10-14 20:31:45 +08:00
morluto cbc0e97c29 test(ci): bind npm cache IDs to output consumers
Remove the blanket caller ID requirement that rejected the standalone mutation workflow. Keep producer bindings where static cache authorization and package verification consume action outputs.
2026-10-14 20:10:17 +08:00
morluto 92a0edf187 perf(ci): move full mutation sweeps off the release path
Keep fresh coverage and both real mutation preflights required in every CI tier. Run complete canonical sweeps daily or manually in isolated jobs with distinct reports, retained failure diagnostics, and explicit report-presence checks.

Preserve regression inventories, release qualification and mutation settings; remove the superseded duplicate gate test. Related: #3281.
2026-10-14 19:49:08 +08:00
N0zoM1z0andKaoru0822 1b46398795 perf(ci): preflight mutation configurations during fast feedback
Keep full qualification and standalone mutation execution intact, retain distinct reports, and preserve fail-closed quality gates.

Related: #3281

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-14 19:48:30 +08:00
morluto f75b1a416b test(browser): retain bounded diagnostics for scenario policy failures 2026-10-14 19:24:14 +08:00
morluto 0686b07546 test(doctor): retain discovered Hopper paths despite version failures 2026-10-14 19:23:11 +08:00
morluto c2b79aa462 test(doctor): separate Hopper availability and version assertions 2026-10-14 19:10:22 +08:00
morluto efe0923914 fix(release): exclude Codex automation from contributor thanks 2026-10-14 19:06:36 +08:00
YsopwnableandKaoru0822 a32b4b6ac5 test(ci): correct Rizin readiness and Hopper launcher fixtures (#3299)
Correct two independently reproduced CI fixture preconditions: close the real Rizin child's stdin before its readiness frame, and provide scoped Linux loader evidence for the Hopper doctor shell fixture. Preserve original timeouts, rejection checks, path spelling, launch prevention and cleanup.

Current-head CI passed, including both complete reverse-tools mutation passes and 1,273 unique portable test files (14,521 passed, 126 skipped, zero failures). Each issue retains its own branch/commit; the validated integration tree includes the Hopper fix from #3410. That duplicate PR can close after the same code is verified on main.

Fixes #3295
Fixes #3406

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-14 18:42:33 +08:00
morluto 60c08802a7 test(native): validate demangling through owned contracts 2026-10-14 18:14:01 +08:00
morluto 7435e79d65 test(doctor): assert platform-specific Hopper readiness and exit status 2026-10-14 18:14:01 +08:00
morluto 44d98de236 test: align malformed XML and route seed assertions with owning contracts 2026-10-14 17:55:22 +08:00
morlutoandmsaimshafique ba33c706d2 fix(setup): inspect installed skill identity for unqualified doctor
Select installed skill destinations in the existing inspection pass, retaining malformed or foreign identity facts while ignoring empty marker-only roots.

Refs: #3208, #3296

Co-authored-by: msaimshafique <msaimshafique4664@gmail.com>
2026-10-14 17:49:20 +08:00
morlutoandhaoran20100322 aade9bbcb7 fix(setup): evaluate Gemini workspace trust before settings content
Treat only ENOENT as absence. Preserve unreadable metadata errors and ignore untrusted workspace content before parsing it.

Refs: #3206, #3319

Co-authored-by: haoran20100322 <haoran20100322@outlook.com>
2026-10-14 17:49:19 +08:00
morlutoandPaoloValletta 0930e94e07 fix(setup): validate registered MCP output schema profiles
Reuse the shared profile schema before setup writes and preserve supported values through registration and startup.

Refs: #3205, #3277

Co-authored-by: PaoloValletta <paovalletta@hotmail.it>
2026-10-14 17:49:19 +08:00
morluto 3f6b5755b3 fix(managed)!: share member list facts and retain verification uncertainty
Parse TypeDef member lists once at their owning boundary, retain malformed ranges as unknown, and admit representation storage before allocation. Reuse parsed facts across member ownership, inventory, attributes, and native boundaries. Preserve constant operands in structural matching.

Refs: #3202, #3201, #3199

BREAKING CHANGE: unresolved managed/native verification results may retain a non-null matched_native and candidate facts when module identity is unknown. Consumers must use status and reason rather than assume unresolved observations are empty.
2026-10-14 17:49:18 +08:00
morluto 45b6a7fe8f fix(native): distinguish proven pre-launch failure from unknown target ownership
Emit explicit target-creation facts only when known. Invalid post-launch process handles remain unknown and retain cleanup ownership.

Refs: #3185

Validation uses bridge fakes and provider-boundary tests; no real LLDB target launch was performed.
2026-10-14 17:49:18 +08:00
morlutoandSII-YDD 2daeff4b24 fix(application): normalize owned target admission cancellation
Normalize only the exact aborted signal reason, retaining unrelated failures and descriptor ownership after cleanup failures.

Refs: #3197, #3265

Co-authored-by: SII-YDD <38822679+SII-YDD@users.noreply.github.com>
2026-10-14 17:49:18 +08:00
morlutoandhhang e67c3642c8 fix(process): retain deletions after observed roots become obstructed
Carry initial root observations into final capture. Treat ENOTDIR as absence only for a previously observed root, preserving initial obstruction and cancellation failures.

Refs: #3238, #3263

Co-authored-by: hhang <169027046+xu91102@users.noreply.github.com>
2026-10-14 17:49:17 +08:00
morluto 030c477328 fix(evidence): derive bundle comparison status from classified changes
Keep digests as provenance while status follows examined deltas and unresolved coverage.

Refs: #3216
2026-10-14 17:49:17 +08:00
morluto 3a34ad37bd fix(evidence): classify noncanonical bundles at the input boundary
Use a dedicated canonical-projection error so unrelated TypeErrors retain their protocol failure meaning.

Refs: #3219
2026-10-14 17:49:16 +08:00
morluto 1250aaedd8 fix(browser): decode SAZ XML through typed encoding validation
Reuse the format-aware XML text owner, preserve decoder causes, and reject contradictory declarations even in unselected records.

Refs: #3273
2026-10-14 17:49:16 +08:00
morluto 2b60e097f2 fix(browser): preserve uncertainty and compare redirect observations
Retain scenario selection limitations independently of known changes. Compare known status, MIME and byte observations for each redirect hop without inventing missing facts.

Refs: #3172, #3173
2026-10-14 17:49:15 +08:00
morluto 5e79d468e5 fix(javascript): retain runtime candidate ambiguity
Keep referenced ambiguous candidates unknown instead of classifying them as absent.

Refs: #3192
2026-10-14 17:49:15 +08:00
morlutoandJoão Pedro de Faria 44c9d2924d fix(javascript): match feature seeds against their owning source values
Apply exact, contains, and case options to node IDs. Route seeds consume retained route values rather than generated display labels.

Refs: #3189, #3190, #3298

Co-authored-by: João Pedro de Faria <jpdefo7@hotmail.com>
2026-10-14 17:49:14 +08:00
morluto 503109995f fix(javascript): retain early-return conditions in wrapped call paths
Share sequential fallthrough interpretation between block traversal and isolated seed context.

Refs: #3196
2026-10-14 17:49:14 +08:00
morluto 28f5761973 test(mcp): isolate target-free sessions from host provider discovery
Inject explicit unavailable provider ports into the fake-session harness, forbid accidental execution, and retain public availability coverage. This removes host-dependent timeouts without increasing deadlines.
2026-10-14 17:49:13 +08:00
morlutoandhhang dae1e55381 fix(javascript): preserve lexical binding and default export capture semantics
Resolve CommonJS wrapper bindings from the parser-owned source type. Capture default identifier export values at the export statement while preserving live named bindings.

Refs: #3261, #3297

Co-authored-by: hhang <169027046+xu91102@users.noreply.github.com>
2026-10-14 17:49:13 +08:00
morlutoandGeronimo 322506d0c0 fix(javascript): retain unavailable package metadata during resolution
Consume the same-container inventory lookup before treating a missing package.json projection as absent. Unknown metadata prevents index fallback and unsupported bare-package exports resolution; restored metadata selects its declared main or import/require export.

Validation: owner same-container/restored controls and actual ASAR CLI missing/restored package-manifest workflow. Completes the P1 review finding on #3278 that remained after the earlier missing-companion projection fix.

Fixes #3213
Related: #3278

Co-authored-by: Geronimo <varungore1@gmail.com>
2026-10-14 17:09:18 +08:00
7582f0dff2 fix(hopper): share launcher discovery across setup and runtime
Centralize canonical system/user and deterministic renamed-bundle path facts. Reuse the installer destination and honor the same local candidates in doctor and runtime; scan renamed bundles only after canonical candidates fail. Preserve explicit path spelling and exclusive selection. Homebrew command probing remains doctor-only with explicit runtime configuration guidance.

Validation: owner, config, setup and doctor tests cover canonical priority and renamed paths; public CLI verifies explicit path preservation without launching Hopper. No real Hopper analysis claimed.

Fixes #3054
Related: #3085

Co-authored-by: Vinícius DS Andrade <95101505+viniciusdsandrade@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-14 17:09:17 +08:00
morluto 864314ac85 fix(adb)!: distinguish reinstall intent from observed replacement
Preserve requested adb reinstall mode as replace_requested and report replaced as null when no prior package-state observation exists. Keep install Success/Failure, raw output and host digest, with an explicit Evidence limitation; add no device queries.

Validation: stateful CLI fake distinguishes fresh/existing installs and checks exact version/install commands, plus provider and MCP schema controls. No real-device verification claimed.

Fixes #3166

BREAKING CHANGE: install_adb_package now requires replace_requested and exposes replaced as nullable rather than claiming the selected mode was an observed replacement.
2026-10-14 17:09:17 +08:00
morluto 0351328e54 fix(electron): retain nested bridge facts through wrapper resolution
Merge direct same-call nested member facts into symbolic wrapper results using canonical deduplication and ordering. Preserve unknown spread coverage rather than losing known member paths.

Validation: bounded source cases and public CLI/stdio MCP spread/no-spread controls. The existing 125,000-member stress case exceeded the 384 MiB local cap and was stopped without increasing the limit; that capacity case remains unverified locally.

Fixes #3212
2026-10-14 16:54:08 +08:00
morluto 81f7638203 fix(javascript): keep exports out of write relations
Project writes only from write-role references. Export references remain distinct facts and genuine assignments still traverse writes-only traces.

Validation: source semantic controls and public CLI/MCP writes-only traces.

Fixes #3207
2026-10-14 16:54:08 +08:00
7e5cffa2ae fix(javascript): preserve loader ownership and unavailable artifact facts
Preserve missing ASAR companion occurrence identity, declared size and unknown content in graph nodes and resolution limitations. Apply URL text semantics once for ESM lookup while preserving literal CommonJS paths and original specifiers. Keep CJS wrapper var bindings and assignment history, recognizing only untouched wrapper redeclarations.

Validation: real ASAR producer workflow and CLI/MCP path/coverage cases; semantic reassignment controls. Context7 Node documentation and direct Node wrapper execution confirm wrapper behavior.

Fixes #3213
Fixes #3214
Fixes #3223
Related: #3278, #3254

Co-authored-by: Geronimo <varungore1@gmail.com>
Co-authored-by: Paolo Valletta <paovalletta@hotmail.it>
2026-10-14 16:54:08 +08:00
morluto 1d826f304f fix(process): bound normalized terminal retention before allocation
Pre-count ordered normalization stages before materializing output. Charge original and normalized terminal bytes independently, omit whole over-budget chunks and preserve exact raw counts plus explicit unknown final sizes. Bound rendered normalization, retain legacy capture compatibility, and remove temporary per-match output arrays.

Validation: UTF-8 surrogate boundaries, shrinking/no-op/literal rules, capture validation and public MCP expansion/equal-size controls with verified cleanup. CPU, process-family memory and token changes are unmeasured; no performance speedup claimed. Nonterminal derived payload bounds remain separate work.

Fixes #3168
2026-10-14 16:54:08 +08:00
morlutoandCRYPTONIKAV 7fb0a0cc57 fix(mcp): bound SDK validation errors before transport delivery
Measure actual wire result JSON, including its provenance marker, without allocating the full serialization. Retain complete SDK diagnostics through the existing Evidence writer and return a bounded reference or explicit retention failure. Keep fitting escaped errors and subsequent valid calls usable.

Validation: actual SDK client/transport retention readback and fit/one-byte-over delivery boundaries.

Fixes #3221
Related: #3267

Co-authored-by: CRYPTONIKAV <29835619+CRYPTONIKAV@users.noreply.github.com>
2026-10-14 16:54:08 +08:00
morlutoandTaksh e5deb832cb fix(android): report validated ADB and Apktool host availability
Probe the selected clients through their owning providers and expose current host availability through binary_session. Keep device/serial and target applicability separate. Reject unrecognized successful version output; retain the bounded producer output and original version spelling. Apply the documented Apktool floor, including the verified Debian dirty build.

Validation: provider/version tests and public MCP grouped availability; no real Android device or APK provider verification claimed.

Fixes #3067
Fixes #3068
Related: #3077

Co-authored-by: Taksh <takshkothari09@gmail.com>
2026-10-14 16:54:08 +08:00
morlutoandakushonkamen cf806ea02a fix(hopper): prepare deterministic FAT32 analysis slices
Use the validated thin-image preparation path for FAT32 and FAT64. Prefer ordinary arm64 then arm64e, masking capability bits only for ranking while preserving raw slice identity; reject unresolved ambiguity. Bump the analysis profile epoch.

Validation: slice/profile fixtures cover raw subtypes, ambiguity, malformed tables and source integrity. Real Hopper execution was not run.

Related: #3072, #3092

Co-authored-by: akushonkamen <akushonkamen@163.com>
2026-10-14 16:54:08 +08:00
morluto ee5cfbe05c fix(dotnet): accept legacy small exception section sizes
Apply CoreCLR-compatible clause counting while validating and advancing by the physical section extent. Preserve malformed, reserved, chained and truncation checks.

Validation: managed decoder suite passes; no native compiler or runtime verification claimed.

Fixes #3279
2026-10-14 16:54:08 +08:00
morluto 459c0951f2 fix(analysis): propagate typed cancellation across batch stages
Stop dispatching later resolution, identity and pseudocode stages after typed cancellation; settle already dispatched work. Preserve ordinary partial failures and stop queued graph traversal.

Validation: public MCP cancellation and partial-failure controls.

Fixes #3198
2026-10-14 16:54:07 +08:00
morluto a154b9752c test(setup): use verified invocations in uninstall fixtures
Update byte-preservation fixtures for the fail-closed command ownership contract. Unknown path retention remains covered.

Validation: both filesystem boundary files pass, including BOM restoration and idempotent removal.
2026-10-14 16:54:07 +08:00
Wade ffeb2d4367 fix(apple): keep unrooted extensionless Mach-O in IPA executables (#3300)
IPA projections keep components outside every application root, but
executables required root membership and native_libraries matched only
.dylib/.so names, so an unrooted extensionless Mach-O such as
SwiftSupport/HelperTool was in neither list and formed no bridge
candidate. List unrooted Mach-O images under executables unless they
are already native libraries.

Fixes #3174
2026-10-14 16:52:32 +08:00
morluto 5f0065c542 ci: validate initial PR readiness declarations (#3302) 2026-10-14 16:51:59 +08:00
7c962cc70b feat(website): simplify the reader journey and beginner setup (#3274)
Lead new readers from compact setup through the preserved dinosaur and
Calculator examples, with code, comparisons, and verification in disclosures.
Expand Windows, macOS, and Linux onboarding, client/provider choices,
on-site curl instructions, and the supplied first investigation.
Complete Get started translations while retaining executable literals,
historical evidence, and main's explicitly partial Greek fallback.

Verified the integrated website, its 12 maintenance regressions, all tracked
formatting, typecheck/lint, 38 locale/base-path browser combinations, and
the public Funnel preview. Native installers and providers were not run.
The separate manual production publication workflow is not triggered here.

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Assisted-by: Suika (gensokyo-skills:suika-scatter-gather-planning)
2026-10-14 08:36:00 +00:00
Wade 57290a01ac fix(apple): order projection paths by Unicode code point (#3294)
The Apple projection sorted application roots, bundle paths and symlinks
with relational string operators, which compare UTF-16 code units, so a
name with U+10000 sorted before one with U+FFFD. Use the shared
compareUnicodeCodePoints instead and remove compareProjectionStrings,
whose documented code point contract it did not meet.

Fixes #3177
2026-10-14 16:35:00 +08:00
min eunyoung 535c8ef303 docs(readme): add the Linux ps prerequisite to the Korean README (#3270) 2026-10-14 16:27:54 +08:00
Kaoru0822andClaude Opus 5.5 e5828596c5 fix(artifacts): inventory universal slices REA cannot structurally index (#3283)
* fix(artifacts): inventory universal slices REA cannot structurally index

inspect_artifact and extract_artifact rejected a valid universal Mach-O
when one slice was outside the structural index, such as XLD's
big-endian PowerPC slice. The whole inventory failed with "Artifact is
invalid or has changed. Get a fresh copy and try again."

When lipo reports every slice's range, the slices are now inventoried
from those ranges with a limitation naming the unsupported structure,
as the reader already does for files without structural facts. When
lipo omits a range, the failure is reported as unavailable support
rather than a corrupt artifact.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(artifacts): validate every FAT slice past an unsupported one

readFatSlices returned at the first unsupported slice, so a later
malformed slice went unchecked: with lipo's ranges now inventoried for
unsupported structure, a malformed little-endian slice after a
big-endian one was accepted while the reverse order was rejected.
Unsupported slices are now remembered and the remaining slices are
still validated, so malformed structure wins in either order.

Adds portable fake-lipo coverage for complete ranges, missing ranges
(unavailable) and malformed slices on both sides of a big-endian slice.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 01:22:45 -07:00
Kaoru0822andClaude Opus 5.5 04345767a8 perf(artifacts): run the asset catalog and Interface Builder bundle walks together (#3285)
* perf(artifacts): run the asset catalog and Interface Builder bundle walks together

inspect_asset_catalog walked the app bundle for Assets.car files and then
walked it again for the Interface Builder graph that supplies resource
keys. The analyses are independent, so start the Interface Builder one
first and let a catalog failure cancel it and win, as the sequential
order did. Keynote 4.20 s -> 2.89 s with identical output.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(artifacts): hold asset catalog and Interface Builder work open to pin overlap

Keep the Interface Builder stub pending until catalog inspection starts,
and keep catalog inspection pending while asserting an early Interface
Builder failure has not escaped.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 01:22:27 -07:00
ca7450beff test(perf): evaluate larger and non-HTTP packet workflows (#3232)
Add reproducible larger/non-HTTP packet cost measurements, an independent header oracle, exact page validation and separate factual/plan grading. Document caller-selected controls and retain negative/mixed model results. Production defaults and APIs remain unchanged.

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-14 16:18:22 +08:00
morluto cd88163e42 fix(cli): show refusals for blocked setup dry runs
Render a terminal dry-run plan only after setup admission succeeds. Blocked skill or registration plans use the existing remediation output. Reuse one bounded PTY helper for success and refusal workflows; verify no linked target or client configuration changes.

Follow-up-to: #3108
Validation: public CLI PTY tests and RPC owner controls pass (77 tests); build and static checks pass.
2026-10-14 16:10:47 +08:00
morluto ccd748635c test(mcp): retain uncertainty for empty capped IDA reference pages
Exercise the empty-but-truncated reference page through trace_feature, preserving unknown coverage without fabricating a reference row.

Fixes #2558.
2026-10-14 16:10:47 +08:00
morlutoandVaishnavi220506 12b3cdb804 fix(javascript): preserve final RPC property and source-body ownership
Normalize exact template and bigint keys at the shared AST boundary. Keep overwritten GraphQL body literals owned by their source object so they cannot resurface as standalone operations.

Reviewed-PR: #3244
Co-authored-by: Vaishnavi220506 <vaishnaviharish2006@gmail.com>
2026-10-14 16:10:46 +08:00
morlutoandHussnain Ahmad eef4a0bfb4 fix(javascript): derive loader references from lexical binding ownership
Use shared lexical bindings and recovered bundler identities for require and chunk references. Preserve uncertain writes as unresolved evidence; remove name-only loader aliases and repeated definition scans.

Reviewed-PR: #2989
Co-authored-by: Hussnain Ahmad <ha7886899@gmail.com>
2026-10-14 16:10:46 +08:00
morlutoandbalkan16 f2f83934d2 fix(javascript): preserve resource error reasons across worker frames
Serialize the typed resource reason once and let the application own path formatting, retaining partial static observations and reported limits.

Reviewed-PR: #3039
Co-authored-by: balkan16 <57933796+balkan16@users.noreply.github.com>
2026-10-14 16:10:46 +08:00
morlutoandwunianze666-netizen f14c2d4df3 fix(browser): retain Playwright IPv6 capture representations
Adapt observed Playwright 1.58.2 bracketed IPv6 addresses only in the disposable HAR validation copy. Preserve the producer spelling and per-entry limitation through public CLI and MCP workflows, retaining existing safe-number admission policy.

Reviewed-PR: #3161
Co-authored-by: wunianze666-netizen <wunianze666@gmail.com>
2026-10-14 16:10:46 +08:00
morlutoandLcokHcak 56128a8424 fix(frida): preserve attach targets and reconcile early detachment
Treat process lookup as optional display metadata, attach to the caller-selected PID, and reconcile already-detached sessions after subscribing to events. Tests observe the actual attach target and the detach race.

Reviewed-PR: #3099
Co-authored-by: LcokHcak <268763621+LcokHcak@users.noreply.github.com>
2026-10-14 16:10:46 +08:00
morluto 5b3400d5d9 fix(pwntools): preserve actionable provider admission errors
Carry the concrete configuration and admission reason through the typed provider-selection error to CLI callers.

Fixes #3055.
Reviewed-PR: #3087
Co-authored-by: 1052326311 <65798732+1052326311@users.noreply.github.com>
2026-10-14 16:10:46 +08:00
morluto 57bb7ec5ca fix(adb): parse successful daemon notices from both output streams
Recognize exact current and legacy ADB startup notices without treating stderr as device inventory or accepting error lookalikes.

Fixes #3051.
Reviewed-PR: #3073
Co-authored-by: Vinícius DS Andrade <95101505+viniciusdsandrade@users.noreply.github.com>
2026-10-14 16:10:46 +08:00
187f113f79 fix(identity)!: preserve invocation uncertainty and advertised catalogs
Capture the current entrypoint once without inventing a path. Preserve unknown registration ownership through setup and doctor, refuse unsafe registration plans, and retain exact pinned-launcher readiness checks. Hash the same full or compact schema projection advertised by each MCP server and use that identity in doctor comparisons.

BREAKING CHANGE: server.command_path may be null when the invocation path is unavailable. Registration and setup statuses, and doctor classifications, may report unknown.

Reviewed-PR: #3230, #3015
Co-authored-by: qorvr <330186599+qorvr@users.noreply.github.com>
Co-authored-by: Hemang Varshney <218273197+hemv-857@users.noreply.github.com>
2026-10-14 16:10:46 +08:00
morlutoandqorvr dd1c879580 fix(windows): end output streams before releasing owned handles
Settle local streams even when native handle release fails, retaining handle ownership and retry semantics until release succeeds.

Reviewed-PR: #3230
Co-authored-by: qorvr <330186599+qorvr@users.noreply.github.com>
2026-10-14 16:10:46 +08:00
morlutoandqorvr c482ddd6ad fix(mcp): release optional providers when transport startup fails
Close the browser scenario owner alongside session, Android and servers when transport startup throws. Preserve cleanup failures in the existing diagnostics.

Reviewed-PR: #3230
Co-authored-by: qorvr <330186599+qorvr@users.noreply.github.com>
2026-10-14 16:10:46 +08:00
morluto 69b8509602 docs(contributing): strengthen issue evidence guidance (#3287) 2026-10-14 16:07:32 +08:00
cf6ea09711 fix(setup): refuse linked skill installation directories (#3108)
Validate final selected skill roots and references before preview, approval,
and client/Hopper changes, with a repeated installation-boundary check.
Preserve interactive unselected-client choices and registration-only setup.

Share typed directory/read observations across setup, doctor, and update;
report unmanaged paths and unknown reads with actionable paths/reasons.
Retain uninstall child outcomes and ownership manifests when cleanup remains.

Shared single-pass inspection retains all unreadable paths after stale or
missing files and keeps observed metadata, without duplicate manifest reads.
Absent installed files do not depend on generated bundled bytes.

Validation: 217 related tests, 41 final CLI/MCP and filesystem checks, and all
7,446 source tests in 508 files pass (eight platform/condition skips).
Static/docs checks pass. Normal four-call
install/rerun/repair/uninstall tasks preserve results and output token counts.
No native Windows follow-up or arbitrary concurrent swap containment claim.

Fixes #3057.

Co-authored-by: geekwolf <3125615+geekwolf@users.noreply.github.com>
Co-authored-by: N0zoM1z0 <161784452+N0zoM1z0@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: akushonkamen <74654766+akushonkamen@users.noreply.github.com>
2026-10-14 16:00:00 +08:00
morluto 90d4098a01 docs(contributing): require human ownership and review readiness (#3286)
* docs(contributing): require human ownership and review readiness

* docs(contributing): strengthen the human declaration instruction
2026-10-14 15:59:58 +08:00
Kaoru0822andClaude Opus 5.5 bdacd4140e fix(sqlite): keep a retained worker owner after a failed worker exit (#3242)
* fix: abort invalid JSON parses and keep retained worker owners

Main (7375ab6e3) fails four boundary tests from its latest batch.

- f79fd59e9 moved JSON value validation into a non-aborting superRefine, so
  authenticated Evidence still computed its recursive identity digest over
  a 10,000-level `parameters` value and overflowed the stack. The process
  comparison CLI then reported "Maximum call stack size exceeded" instead of
  the depth limit. Abort the parse on a JSON validation issue so enclosing
  refinements never walk invalid data.
- runOwnedCommand retains an incomplete cleanup through retainCleanup and
  then rethrows the command failure without a cleanupOwner. The EVM and SQLite
  providers replaced their retained owner with that undefined value, so an
  output-limit failure with unresolved cleanup lost cleanupIncomplete and its
  owner. Keep the retained owner unless the failure carries one.
- The EVM malformed-reply test expected a nonexistent `output_failure` code;
  AnalysisOutputError projects to `unreadable_output`. The pcap capinfos stub
  prints its report with a trailing newline, which retained output preserves.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(sqlite): keep a retained worker owner after a failed worker exit

Cover the path the existing worker-cleanup test bypasses: a real owned worker
exits nonzero, runOwnedCommand retains its incomplete cleanup and rethrows the
failure without a cleanupOwner. The provider must still report
cleanupIncomplete and block a new worker until the retained cleanup succeeds.
Fails on the previous owner assignment.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(evm): retain the compact worker failure beside retained cleanup

When the worker command fails and its cleanup is retained, the cleanup error
now reports the primary failure in the same compact form as an unretained
command cleanup failure: failure kind, message, exit code and signal. Its
output is already retained once as captured_output, so the previous error no
longer repeats up to a megabyte of diagnostics, and failure_kind stays at the
top level that the provider test reads. Both paths share
ownedCommandPreviousError.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 00:56:22 -07:00
Kaoru0822andClaude Opus 5.5 307af044bc perf(artifacts): overlap bundle magic reads while enumerating dylib roots (#3248)
* perf(artifacts): overlap bundle magic reads while enumerating dylib roots

trace_dylib_resolution opened every file of an app bundle one at a time
to read its Mach-O magic (Keynote: 35,971 files, 23 Mach-O images).
Keep up to eight reads in flight while the directory walk continues,
settling them in walk order so classification and failure precedence
match the sequential scan. Keynote 4.20 s -> 2.78 s.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(artifacts): pin ordered-read failure precedence and cleanup

Extract the overlapped magic-read queue into consumeWithOrderedReads and
test its ordering, concurrency bound, failure precedence, enumeration
close and read settlement with controlled promises.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 00:40:59 -07:00
min eunyoung 9385060f9c fix(website): correct the Korean skip-link label (#3280) 2026-10-14 15:37:59 +08:00
min eunyoung 1c296f11a1 fix(website): correct Korean FAQ translations (#3276) 2026-10-14 15:37:49 +08:00
YsopwnableandKaoru0822 fb752d2293 test(domain): keep rejected Evidence payloads inert (#3260)
Exercise actual Evidence authentication with rejected accessors and proxies in parameters, raw_result and normalized_result. Require zero caller-code reads and preserve the original rejection path without a semantic-ID error.

Closes #3251.

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-14 15:34:35 +08:00
morlutoandYog D 8dde6370d0 fix(deps): update the development-only qs serializer
Scope qs 6.16.0 to typed-rest-client under mutation tooling, addressing the affected serializer advisories without changing runtime dependencies. Regenerate the locked dependency inventory; installed dependency tree and unchanged runtime fingerprint verified.

Fixes #3114

Co-authored-by: Yog D <yogd7657@gmail.com>
2026-10-14 15:20:10 +08:00
morlutoandwxggzz 102c5a3ae5 test(mcp): verify availability after cancelled session restoration
Exercise the production session and MCP SDK across provider failure, cancelled target switching, restored run identity, cleared stale availability and successful retry.

Co-authored-by: wxggzz <weixiegen0506@gmail.com>
2026-10-14 15:20:10 +08:00
morlutoandAliciawque 11d38cbc00 test(mcp): verify retained Interface Builder limits
Verify retained hierarchy limits and summary identity through the production artifact-only composition and real MCP SDK, alongside compiled CLI controls. Deep-provider selection is a separate lane.

Co-authored-by: Aliciawque <166712943+Aliciawque@users.noreply.github.com>
2026-10-14 15:20:09 +08:00
7777abf473 fix(artifacts): preserve ZIP member identity and filename semantics
Retain duplicate archive occurrences with validated ordinal identities across pages; qualify ambiguous comparison, parent and source-map relationships instead of overwriting entries. Preserve exact integrity-contradiction ownership. Decode unflagged names as CP437 while honoring UTF-8 and valid Unicode Path overrides.

Fixes #3152

Fixes #3237

Co-authored-by: Charan Rathore <180254320+charan-rathore@users.noreply.github.com>

Co-authored-by: hhang <169027046+xu91102@users.noreply.github.com>

Co-authored-by: gongfpp <1360187968@qq.com>
2026-10-14 15:20:09 +08:00
morlutoandShuaixinHuang 27311f88d4 fix(browser): preserve capture uncertainty and frame ancestry
Parse required resource rows and retain valid siblings. Missing collections and malformed child subtrees keep unknown omission counts. Compare complete frame forests without capture-local IDs, and never infer aggregate DOM/metadata changes from omissions.

Co-authored-by: ShuaixinHuang <1836656514@qq.com>
2026-10-14 15:20:08 +08:00
morlutoandclaudetrail2026-droid eb00112a3f test(javascript): exercise candidate endpoints through the worker
Verify the existing exact candidate-endpoint schema through a completed compiled CLI workflow instead of adding a duplicate worker-only assertion.

Fixes #3128

Co-authored-by: claudetrail2026-droid <313336388+claudetrail2026-droid@users.noreply.github.com>
2026-10-14 15:20:08 +08:00
morlutoandAlex Desir 76fa5304a8 refactor(javascript): share computed-key callable ownership
Give calls, captures, object operations, promises and effects one callable-owner tracker. Suspend a method while its computed key is evaluated in the enclosing callable; remove the capture-only downstream repair.

Co-authored-by: Alex Desir <adesir089@gmail.com>
2026-10-14 15:20:07 +08:00
morlutoandTo3akaRin 6396851958 fix(javascript): retain directive values in function fingerprints
Retain directive literal values, including escaped strict-mode lookalikes, in semantic function fingerprints. Verify normalized duplicates and distinct directives through CLI/MCP.

Co-authored-by: To3akaRin <131595336+To3akaRin@users.noreply.github.com>
2026-10-14 15:20:05 +08:00
morlutoandVaishnavi220506 b8eea783da fix(javascript): honor final RPC object properties
Resolve final object writes once at the RPC endpoint producer. Unknown computed writes and spreads remain barriers; overridden properties do not invent endpoints. Source, CLI and stdio MCP regressions cover the behavior.

Co-authored-by: Vaishnavi220506 <vaishnaviharish2006@gmail.com>
2026-10-14 15:20:05 +08:00
morlutoand吴佳浩 9d00d7d7fa fix(native): retain unclassified accepted Swift manglings
Use the shared Swift prefix owner for admission while preserving legacy classified spellings. Keep unresolved symbols and aliases without letting them override decoded category votes.

Co-authored-by: 吴佳浩 <wujiahao@supremind.com>
2026-10-14 15:20:04 +08:00
morlutoandarlandev 74afb04f32 fix(native): decode the Objective-C word-alignment sentinel
Interpret the 64-bit ivar word-alignment sentinel as eight bytes. Preserve ordinary exponents and unknown invalid encodings; source fixtures only.

Fixes #3186

Co-authored-by: arlandev <arlanpower69@gmail.com>
2026-10-14 15:20:04 +08:00
7df85f16a0 fix(cli): align advertised analysis contracts with runtime
Advertise the nonnegative outline-depth bound after safe-range conversion, verify it against the runtime parser and Ajv 2020, and describe every retained view family and both supported recorded-core architectures. Reuse the existing CLI schema fixture instead of three duplicate capture helpers.

Fixes #3211

Fixes #3194

Fixes #3167

Co-authored-by: Anankequ <2983852214@qq.com>

Co-authored-by: gongfpp <1360187968@qq.com>
2026-10-14 15:20:03 +08:00
morlutoandN0zoM1z0 4c401458da fix(ci): track imported compiler inputs and package scopes
Include imported declarations, nested Node package scopes and authored documentation helpers in compiler task ownership. Schedule static checks for documentation package scopes. Three real cold/warm compiler invalidation cases passed under bounded Flameox captures; the complete matrix remains CI verification.

Fixes #3164

Co-authored-by: N0zoM1z0 <161784452+N0zoM1z0@users.noreply.github.com>
2026-10-14 15:20:03 +08:00
morlutoandSaim Shafique ada133c256 fix(reference): retain unknown kind for unsupported source entries
Keep FIFO, socket and device entries unknown instead of projecting unsupported reader entries as ordinary files. Preserve real file read-failure identity.

Fixes #3218

Co-authored-by: Saim Shafique <msaimshafique4664@gmail.com>
2026-10-14 15:20:02 +08:00
morlutoandSaim Shafique b57a054d5e fix(mcp): cover prompt objectives in catalog identity
Digest the delivered guided-prompt objective alongside metadata and steps. Objective-only changes update prompt and combined identity without changing tools.

Fixes #3222

Co-authored-by: Saim Shafique <msaimshafique4664@gmail.com>
2026-10-14 15:20:02 +08:00
morluto 29d3dcb7ea test(managed): verify complete fixture resources and references
Exercise the actual JavaScript fixture producer with the reported 2045-byte embedded resource and all 60 AssemblyRef names. Verify declared complete extents and clean production-inspector coverage.

Fixes #2585
Fixes #2583
2026-10-14 14:47:26 +08:00
morlutoandyasshmittal dc6872c66f test(mcp): cover navigation errors and source-map cancellation
Exercise real MCP navigation error delivery and preserve a custom caller abort cause through source-map cancellation. Document doctor stderr retention.

Co-authored-by: yasshmittal <myash0091@gmail.com>
2026-10-14 14:47:26 +08:00
morluto 16e1cc65ed fix(analysis-view): remove contract initialization cycles
Move the shared input-error constructor and operation identity to a leaf contract. View leaves no longer import their runtime composition owner. Replace a duplicate graph assertion with compiled CLI summary/facet coverage for omitted roots and unresolved hierarchy endpoints.
2026-10-14 14:47:26 +08:00
morluto 3f11bd9655 fix(verify): decode Hopper dispatcher file URLs
Resolve the dispatcher using fileURLToPath so spaces and encoded path characters retain filesystem identity. Syntax checked; real Hopper verification was not rerun.

Fixes #2587
2026-10-14 14:47:26 +08:00
morluto bcc884556e fix(ghidra): preserve unknown PDB application state
Use parsed PDB loaded facts without collapsing omitted/null state to false. Only an explicit false reports unapplied symbols; operation and health Evidence retain the same uncertainty. Controlled bridge validation only.

Fixes #2534
2026-10-14 14:47:26 +08:00
morlutoandShitK 778114a1ab fix(browser): confirm late-opening cleanup independently of cancellation
Keep cancellation responsive while retained browser/profile cleanup owns its independent confirmation deadline and remains retryable.

Fixes #2597

Co-authored-by: ShitK <1724059440@qq.com>
2026-10-14 14:47:26 +08:00
morluto b98be5408b fix(browser): retain excluded scope through final URL capture
Latch target scope exclusion so a late frame-tree reply cannot restore a stale approved final URL or complete coverage. Preserve redirect observations and approved controls.

Fixes #2593
2026-10-14 14:47:26 +08:00
morluto 410537667e fix(javascript): retain uncertainty for conditionally skipped writes
Own skipped-write interpretation at definition collection, including conditional branches, loop iterations and optional-chain arguments. Remove the narrower initializer-only representation.

Fixes #2562
2026-10-14 14:47:26 +08:00
morlutoandDragolone 36c91428fc fix(rust): retain compatible cargo-auditable revisions
Admit documented compatible format revisions, preserve the original uint32 revision and disclose unverified fallback for unknown revisions. Keep malformed package and compressed-input rejection.

Co-authored-by: Dragolone <810170966qq@gmail.com>
2026-10-14 14:47:26 +08:00
morlutoandYann Roberto 66536d0d2e fix(setup): preserve Hermes YAML scalar semantics
Use one Hermes YAML owner for fresh writes and edits, preserving source scalars, exact Python key equality and ruamel timestamp resolution. Validate round trips, backups, idempotence and CLI doctor.

Co-authored-by: Yann Roberto <1922498827@qq.com>
2026-10-14 14:47:26 +08:00
morlutoandAurelio Bernal 9387282274 fix(ghidra): reconcile GNU MIPS ABI declarations
Parse bounded GNU file-scope FP/MSA attributes once, retain absent versus uninspected declarations, reject conflicts and unsupported argument ABIs, and include observed facts in profile identity. LE/BE filesystem and profile regressions pass; real Ghidra/MIPS behavior remains unverified.

Fixes #3096

Co-authored-by: Aurelio Bernal <aurelio@bernalr.com>
2026-10-14 14:47:26 +08:00
morluto 1341e8c831 test(pcap): retain exact output and release injected cleanup failures
Assert the capinfos newline as actually captured, and restore fixture cleanup even after an assertion failure.
2026-10-14 14:47:26 +08:00
morluto 07540cbcde fix(evm): preserve transferred cleanup ownership and bounded output
Retain a worker owner transferred through the callback when a failure has no replacement owner. Give captured streams one canonical owner while preserving unrelated diagnostics and partial Evidence. Linux retained-cleanup coverage requires CI.
2026-10-14 14:47:26 +08:00
morluto 713163e644 fix(domain): stop Evidence authentication after invalid JSON
Abort rejected JSON validation so Evidence refinements never recursively hash invalid deep data. Validated 10,000-level inputs through source parsing and compiled CLI.
2026-10-14 14:47:26 +08:00
qorvrandqorvr 877d40408b fix(electron): attach error listener to spawned node process in readiness fixture (#3165)
In environments where REA_ELECTRON_NODE_EXECUTABLE or node is unavailable,
unhandled error events on the spawned child process can crash the Electron
runtime before readiness checks complete.

This attaches an explicit error handler to absorb ENOENT and spawn failures.

Evaluated and error found by SubQ by Qorvr.

Co-authored-by: qorvr <330186599+qorvr@users.noreply.github.com>
2026-10-14 14:36:52 +08:00
pablodiazjorge e195b8ba91 fix(evm): decode shortest-form empty CBOR text in Solidity metadata (#3226) 2026-10-14 14:36:28 +08:00
YsopwnableandKaoru0822 4190630432 test(ghidra): preserve address alias restoration failures (#3060)
Cover shared DOS/COM alias restoration after uncertain writes, rejected edits, incorrect readback and simultaneous failures. Reuse the upstream producer restoration unchanged; preserve exact observed naming and immutable input protections.

Closes #2924

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-14 14:33:26 +08:00
Kaoru0822andClaude Opus 5.5 e5791229f3 perf(native): skip llvm-nm's dyld-info pass when the symbol table names every export (#3200)
* perf(native): skip llvm-nm's dyld-info pass when the symbol table names every export

inspect_macho ran `nm -gjU`, whose default dyld-info pass takes seconds on
large apps (Keynote arm64 5.8 s, x86_64 10.2 s) yet under -gU can only add
export-trie names that dyld_info -exports already reports. Run
`nm -gjU --no-dyldinfo` and rerun the default pass only when dyld_info
reports an export the symbol table lacks (e.g. strip -T), so its exact
names still disambiguate dyld_info rows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(native): fall back to nm's dyld-info pass for unreadable export rows

Account for every dyld_info export line instead of only the rows the
parser returns, so an unfamiliar row format reruns the default nm pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 23:21:03 -07:00
Kaoru0822andClaude Opus 5.5 7484401601 perf(managed): add retained member summary and pages (#3061)
* perf(managed): add retained member summary and pages

inspect_managed_members always returned the assembly-wide member_refs, so
one selected or unmatched type produced megabytes, and an unselected
ordinary assembly exceeded the MCP response budget. Add opt-in
detail: "summary" that retains the complete Evidence and returns identity,
selection with unmatched names, coverage, user-string coverage, limitations
and per-collection counts. Page original rows through inspect_analysis_view
with kind managed-members; each page names its collection so no row schema
is chosen by union order. Complete delivery stays the default.

Refs #3010

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* perf(managed): validate a sealed member parent once across pages

Each managed-members page reparsed the whole parent report, about 35 ms
for an 18 MB assembly result regardless of the page size. The analysis
view service now reports whether the parent Evidence is a sealed
snapshot, and the member view reuses the validated report for that
sealed result through a weak reference. Unsealed input is still
validated on every call.

The round-trip test fixture now carries an exported type, an ldstr
literal, a field access and a call edge, and requires every collection
to be nonempty so a row schema that drops facts cannot pass on empty
arrays.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(managed): keep delivery detail out of retained Evidence identity

Summary, omitted and complete delivery of the same managed inspection
now retain one parent record, as inspect_macho already does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 23:16:38 -07:00
morlutoandGRAB Racem 7375ab6e34 test(firmware): stream hashes of sparse oversize fixtures
Hash the complete oversize fixture through a read stream rather than allocating the entire 128 MiB file. Preserve the digest and oversize admission test semantics.

Validation: node --check and full static checks pass. The capacity fixture was not rerun locally to avoid heavy work.

Based on #1892.

Co-authored-by: GRAB Racem <racem.grab@etu.univ-lyon1.fr>
2026-10-14 13:58:18 +08:00
morlutoandYog D e62e92526c test(reference-source): avoid filesystem enumeration order assumptions
Assert exact inventory contents and cardinality without depending on directory enumeration order.

Validation: all nine reader boundary tests pass.

Based on #3117.

Co-authored-by: Yog D <yogd7657@gmail.com>
2026-10-14 13:58:18 +08:00
morlutoandShuaixinHuang b782064d54 fix(test): reject excluded symlink aliases in exact selection
A matching realpath does not make an excluded symlink spelling a discovered test. Compare differing path components, rejecting symlink aliases while preserving ordinary case and Unicode filesystem identity.

Validation: all 14 runner boundary tests pass, including excluded file/directory aliases, valid selections and no target execution.

Based on #2849.

Co-authored-by: ShuaixinHuang <1836656514@qq.com>
2026-10-14 13:58:17 +08:00
morlutoandqorvr ef153774f7 fix(mcp): tolerate synthetic entrypoint paths during import
Treat an unresolvable argv entry path as an imported module instead of throwing during entrypoint detection. Keep canonical direct-entry comparison private.

Validation: real child imports compiled runtime with a nonexistent argv path and no CLI side effects; all six runtime-error tests pass.

Based on #3162.

Co-authored-by: qorvr <330186599+qorvr@users.noreply.github.com>
2026-10-14 13:58:17 +08:00
morlutoand1052326311 ae433f0983 fix(mcp): bound retained doctor stderr while draining output
Retain a 64 KiB diagnostic prefix while counting and draining the complete stderr stream. Report truncation and avoid manufacturing a replacement character at a truncated UTF-8 boundary.

Validation: all nine doctor tests pass, including real noisy child, malformed bytes, canonical catalog and startup deadline controls.

Based on #3121.

Co-authored-by: 1052326311 <65798732+1052326311@users.noreply.github.com>
2026-10-14 13:58:17 +08:00
morluto ad9e22d05f fix(browser): honor cancellation during CDP cleanup
Complete owned teardown before deciding whether a successful observation can be published. Classify an exactly propagated source-map abort reason as cancellation while preserving unrelated failures.

Validation: all four cleanup/cancellation boundary tests pass, retaining detach, socket and target-liveness assertions. Based on #2893.
2026-10-14 13:58:16 +08:00
morlutoandAliciawque 7cfcd166e3 fix(apple): derive hierarchy completeness once per document
Include omitted roots, unresolved retained endpoints and serialized NIB omissions in the owning hierarchy status. Reuse that status for summaries and coverage; remove the adapter recomputation and preserve per-document isolation.

Validation: 18 graph and 12 real NIB filesystem/built CLI tests pass.

Based on #3156.

Co-authored-by: Aliciawque <166712943+Aliciawque@users.noreply.github.com>
2026-10-14 13:58:16 +08:00
morlutoandIvenKooLab 583166785f fix(setup): report the effective ZCode fallback registration
Resolve native-versus-fallback precedence once at the parsed configuration owner. Status and maintenance consume the selected dialect and path, including invalid and disabled fallback registrations.

Validation: 28 ZCode boundary and 26 maintenance tests pass. No configuration writes introduced.

Based on #3123.

Co-authored-by: IvenKooLab <276572622@qq.com>
2026-10-14 13:58:15 +08:00
morlutoandAurantium 03ffb2c13d fix(reference-source): recognize Kotlin files in imported inventories
Classify .kt and .kts at the source owner so imported entries and historical language summaries preserve Kotlin identity.

Validation: production inventory projection covers Kotlin, Gradle script, Java and backup-suffix controls.

Based on #3224.

Co-authored-by: Aurantium <280805072+Aurantium-47@users.noreply.github.com>
2026-10-14 13:58:15 +08:00
morlutoandDragolone 576b58cefe fix(native): wrap bind address advances to 64 bits
Apply dyld modulo-64 address arithmetic at the shared bind advance, retaining subsequent bindings after a negative ULEB skip.

Validation: all 37 Mach-O fixup tests pass, including continuation and false-failure controls.

Based on #3138.

Co-authored-by: Dragolone <810170966qq@gmail.com>
2026-10-14 13:58:14 +08:00
morlutoand吴佳浩 17e3cf9802 fix(native): share Swift mangling recognition across metadata
Use one upstream Swift mangling prefix owner for demangler status and metadata projection/counts. Keep visible legacy class names and nominal-category decoding semantics distinct.

Validation: metadata prefix, negative symbol and partial decode regressions pass; existing Mach-O controls pass.

Based on #3157.

Co-authored-by: 吴佳浩 <wujiahao@supremind.com>
2026-10-14 13:58:14 +08:00
morlutoandNikhil Jangid a9af4718ad fix(javascript): parse ignored tokens throughout GraphQL headers
Replace the whitespace-only header regex with bounded name/token scanning that consumes GraphQL comments and exact ignored characters at each boundary. Preserve anonymous, prose and incomplete input controls.

Validation: all 36 RPC endpoint tests pass. Fixes #2523.

Based on #2900.

Co-authored-by: Nikhil Jangid <161968345+nikhiljangid120@users.noreply.github.com>
2026-10-14 13:58:14 +08:00
morlutoandShuaixinHuang bdf865252c fix(electron): retain computed literal preference keys
Use the parsed semantic property key instead of treating all bracketed keys as dynamic. Preserve empty and numeric literal keys; unresolved computed expressions remain unknown.

Validation: exact named preference and dynamic coverage regressions pass. Fixes #2521.

Based on #3105.

Co-authored-by: ShuaixinHuang <1836656514@qq.com>
2026-10-14 13:58:13 +08:00
morlutoandJoão Pedro de Faria 7e1c560dd6 fix(javascript): decode literal file URLs at the path boundary
Decode known literal file URLs with explicit path semantics before assigning filesystem-expression context. Preserve unknown drive, UNC, dynamic and malformed inputs and existing source-relative projection.

Validation: 64 path-owner and 26 filesystem application tests pass. Fixes #2561.

Based on #3147.

Co-authored-by: João Pedro de Faria <jpdefo7@hotmail.com>
2026-10-14 13:58:13 +08:00
morluto 66dcd94d09 fix(cli): route static artifact analysis without native engines
Static .app decoding failed under auto selection when multiple deep engines were installed. Compose the existing artifact provider without native candidates and reuse the shared one-shot target, Evidence and cleanup lifecycle. Preserve the selected provider environment.

Validation: real NIB filesystem and built CLI cases, artifact direct workflow and direct-analysis lifecycle tests pass (57 tests with MCP controls).
2026-10-14 13:58:12 +08:00
morluto ee9bf58abc fix(mcp): materialize catalog identity at the JSON boundary
Keep lazy cached catalog calculation internal; publish catalog counts and digests as plain data. This repairs binary_session and doctor output after inert JSON validation.

Validation: production stdio MCP doctor and invalid-probe controls pass; build, static and docs checks pass.
2026-10-14 13:58:12 +08:00
96e7c22a9c fix(docs): render README source notices as GitHub alerts (#3228)
Move the official-source notices outside the centered HTML headers so GitHub renders IMPORTANT alerts in all 20 README editions. Preserve wording, links, placement and Persian RTL attributes.

Verified before/after rendering with the GitHub Markdown API, unchanged rendered link/image targets, exact source preservation, scoped formatting and a clean integration with latest main.

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
2026-10-14 13:49:21 +08:00
Kaoru0822andClaude Opus 5.5 3f5b703379 fix(native): derive Mach-O word size and byte order from the selected slice (#3163)
* fix(native): derive Mach-O word size and byte order from the selected slice

inspect_macho read endian and word_size from `file -b` for the whole
file. macOS `file` never prints a Mach-O byte order, so endian was null
for every target. For a universal binary, word_size matched "64-bit"
anywhere in the summary instead of describing the selected slice, and it
was null for a thin 32-bit slice.

Both now come from the selected slice's otool header: the magic gives
the word size and the CPU family gives the byte order (x86 and ARM
little, PowerPC big, other families unknown). The file capture is no
longer needed by inspect_macho, so it is no longer run there.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(native): select a 32-bit slice beside a 64-bit slice through inspect_macho

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 22:44:41 -07:00
morluto 131b2d85f3 fix(verify): report every owned cleanup failure before success
Use one cleanup owner across browser, Frida and JEB verifiers. Attempt all independent cleanup tasks, retain primary failures, and emit successful reports only after cleanup. Remove the superseded browser helper and duplicate tests.

Validation: cleanup aggregation and runtime fixture tests, build and static checks passed. No real Frida or JEB run.
2026-10-14 13:40:53 +08:00
morluto 23c70d674c fix(pcap): retain parsed observations when process cleanup fails
Separate command facts from unresolved cleanup, preserve captured stdout, and build partial Evidence through the domain owner. Retain process and snapshot ownership until cleanup succeeds.

Validation: command boundary tests, build and static checks passed. Linux provider regressions added; unavailable on this macOS host.
2026-10-14 13:40:53 +08:00
morluto 6cc6d22585 fix(evm): retain partial evidence and unresolved worker ownership
Preserve parsed worker facts and captured diagnostics when owned worker or snapshot cleanup fails, and retain the owner for retry. Keep primary malformed-reply errors in the cleanup chain.

Validation: build, typecheck and static checks passed. Linux x64 worker regressions added; unavailable on this macOS host.
2026-10-14 13:40:52 +08:00
morluto 02aea22f88 fix(electron): classify dropped preload configuration at the hook
Classify browser-role preload configuration while event kind and role remain paired. Consume canonical dropped families once and retain unknown attribution for legacy independent totals. Correct the stale dropped IPC assertion.

Validation: hook and projection regressions passed. Fixes #2649.
2026-10-14 13:40:52 +08:00
morluto 345c47ccf8 fix(binary): clear retired availability when restoring a session
A cancelled target switch restores a new client but retained the retired runtime availability cache. Clear that cache when installing the restored client. Preserve cancellation, target identity and later real provider failures.

Validation: all 9 BinarySession liveness tests passed. Fixes #2657.
2026-10-14 13:40:51 +08:00
morluto f79fd59e96 fix(domain): validate JSON data without invoking caller code
Reject proxies, accessors, sparse arrays and non-JSON object members at the owning boundary. Validate once before preserving JSON data; retain immutable snapshot reuse.

Validation: JSON, Evidence and immutable snapshot focused tests; cached build and static checks passed.
2026-10-14 13:40:51 +08:00
YsopwnableandKaoru0822 500e32399a fix(ci): check authored documentation code (#3119)
Select portable code checks for authored JavaScript and TypeScript documentation, including hidden directories, removed paths and line separators. Keep Markdown-only changes on their existing documentation route.

Closes #3116.

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-14 13:05:31 +08:00
ec67aebed0 fix(electron): preserve selected fuse inspection paths (#3104)
Preserve caller-selected POSIX symlink/parent paths during CLI anchoring and
Electron bundle traversal. Resolve terminal directory suffixes through checked
real directories, retaining their identities for before/after readback. Keep
the existing Windows CLI resolution, bounded scan, and output contracts.

Validation: six regressions reproduced on main 7ad75bdc8; 41 focused tests pass
with one Windows-only skip. Full static/docs checks and final main-sync fast
checks pass. Official Electron v44.7.1 release archives, extracted executable
hashes and independent fuse wires verify actual ELF/PE/Mach-O bytes. Real CLI
and stdio MCP reproduce main's wrong target and confirm corrected artifact
identity plus CLI/MCP parity; owned MCP servers exit. Equivalent direct-file
outputs remain 963-1,054 o200k tokens. Individual timing measurements and their
limits are recorded in the PR; no speedup is claimed. Native Windows/macOS REA
execution and target application execution are not claimed.

Co-authored-by: Yann Roberto <165009858+shoyann@users.noreply.github.com>
Co-authored-by: N0zoM1z0 <161784452+N0zoM1z0@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-14 12:49:17 +08:00
Kaoru0822andClaude Opus 5.5 f4df6ab280 fix(native): classify every Swift mangling prefix as invalid when undecodable (#3089)
* fix(native): classify every Swift mangling prefix as invalid when undecodable

demangle_swift reported "invalid" only for $s and _$s inputs that
swift-demangle left unchanged. Undecodable symbols carrying the other
prefixes the Swift demangler accepts (_T0, $S, _$S, @__swiftmacro_, $e,
_$e) were reported as "unchanged", i.e. as non-Swift names. Legacy _T
stays "unchanged" because it is also an ordinary C symbol spelling.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(native): expect only toolchain-portable Swift manglings to demangle

The macOS 15 runner's swift-demangle predates Embedded Swift, so valid
$e symbols stay unchanged there. Keep $e and _$e in the undecodable
half only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 21:46:25 -07:00
77be0f6666 fix(capture): end special-scheme URL authorities at a backslash (#2767)
* fix(capture): end special-scheme URL authorities at a backslash

The native mitmproxy adapter removed transport userinfo with
`^(...//)[^/?#]*@`, so the authority ran to the last "@" before "/", "?"
or "#". For `https://example.test\part@tail` it treated
`example.test\part` as userinfo, reported `https://tail` as the
Location, and withdrew the original bytes as a transport credential.
WHATWG URL parsing, used by the shared TypeScript owner
(recordedTransportUrl.ts) and by HAR captures, ends a special-scheme or
scheme-relative authority at a backslash, so that "@" is ordinary path
text.

Locate the authority with the same boundary rule before removing
userinfo. Bytes are handled as Latin-1 text so unrelated bytes stay
identical. Userinfo before the backslash is still removed and redacted.
Add source unit tests for the adapter projection to test:python.

Fixes #2671

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(capture): skip special-scheme leading slashes before userinfo

Ending the authority at a backslash exposed a credential: for
`https://\user:pass@host/path`, WHATWG parsing skips every leading "/"
or "\" after a special scheme (and in scheme-relative references), so
`user:pass` is userinfo, but the new boundary stopped at the first
backslash and found no "@". The same applied to `https:\\user:pass@host`
and `https:user:pass@host`.

Start the authority after that whole slash run for non-file special
schemes and scheme-relative references. File URLs keep their "//"
boundary because they carry no userinfo. Load the adapter in tests with
fresh mitmproxy stand-ins scoped to its import instead of leaving them
in sys.modules.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(capture): parse URL controls and mixed network authorities

* fix(capture): distinguish native paths and URL base authorities

* fix(capture): preserve file-relative path evidence after separators

* fix(comparison): recheck historical calls-only changes (#2223)

* fix(comparison): preserve incomplete function call evidence

Keep missing, unknown, partial and unresolved call relationships unknown in function comparisons and reconstruction claims. Aggregate IDA caller and callee coverage without overwriting uncertainty.

Validate with 144 focused tests, typecheck/lint, docs checks and test discovery. Real providers were not verified; the full headless IDA session suite requires unavailable Windows native controls.

* fix(comparison): recheck historical calls-only changes

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>

* fix(android): make DEX descriptor pattern portable

* fix(ci): isolate Linux Hopper process fixture inventory

* fix(capture): remove empty URL userinfo markers

* test(capture): cover empty URL userinfo markers

* test(ci): isolate Hopper process inventory fixtures

* fix(ci): declare process inventory callback

* test(ci): isolate alternate launcher process fixture

* fix(capture): keep URLs that WHATWG rejects instead of redacting userinfo

File URLs cannot carry userinfo (an "@" in a file host fails the parse),
and a special authority whose host or port WHATWG rejects fails the whole
URL. Neither has credentials, so the bridge now keeps their source text
and byte sidecar. Checked against Node's URL over 30,000 spellings: no
under-redaction, over-redaction 20,540 -> 5,865 (remaining: empty
userinfo, IPv4 range, IDNA, non-special and baseless references).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(capture): strip trailing URL whitespace and validate IP hosts and ports

The previous host check counted trailing C0 controls and spaces, which
WHATWG strips, so `https://user:pw@host ` kept its password. Trailing
C0/space is now removed from the parse view (raw offsets are unchanged).

Special hosts now also follow the WHATWG IPv4 parser (ends-in-a-number,
hex/octal parts, range) and IPv6 literal syntax (zone IDs rejected), and
ports are bounded lexically before int(), which raised ValueError past
4300 digits on Python 3.11+ and failed the whole capture.

Node URL differential over 126,000 spellings: under-redaction 8,084 -> 0,
ValueError 2,550 -> 0; remaining special-scheme over-redaction is IDNA
and the pinned empty-userinfo case. Cost: +0.6 us per URL.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(capture): redact userinfo before hosts that need Unicode mapping

A non-ASCII special host may map under UTS #46 to an accepted host
(fullwidth digits, ideographic full stops, ignored code points), so
treat it as accepted instead of running the ASCII IPv4 check on it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
Co-authored-by: Lucien <164857476+dakjdakd@users.noreply.github.com>
2026-10-13 21:46:15 -07:00
morluto 767fe0f0cc test(docs): isolate README facts from CLI catalog imports 2026-10-14 12:21:26 +08:00
morluto 5b5de1b527 test(javascript): synchronize worker cleanup and validate candidate facts 2026-10-14 12:21:22 +08:00
7ad75bdc80 fix: restore JavaScript analysis and current regression checks (#3080)
Restore source-position-aware ambient aliases, scalar env/argv reads, candidate
endpoint worker data, Chrome DOMSnapshot empty strings, and accumulated Ghidra
LaunchSupport diagnostics. Align regression fixtures with declared current
contracts, while preserving identity, provenance, cancellation, and cleanup
checks. Assert workflow caveats independently at both established locations.

Validation: original five source failures reproduced on main; 307 focused tests
passed with one platform skip, 22 fixture follow-up tests and 22 workflow/MCP
tests passed; full static and docs checks passed. Real public http-server CLI
analyses preserve identical artifact identity, statistics, and 581-token selected
output. Real Chromium confirms restored DOM form destinations; isolated real
service-worker verification passes on main and the change. The complete local
browser run retained an unclassified later marker timeout; current-head CI
provides complete real-browser and recovery lane results. Native IDA/Ghidra
execution and complete local suite execution are not claimed.

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: N0zoM1z0 <161784452+N0zoM1z0@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 12:17:54 +08:00
5830088520 docs(release): review compatibility for the current candidate (#3112)
Review breaking changes and migration notes for the release being prepared; retain the 6.1-to-6.2 guide as historical context.

Fixes #3058.

Co-authored-by: Geekwolf <3125615+geekwolf@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
2026-10-14 11:18:21 +08:00
f3534749e5 docs(process): clarify terminal progress message fields (#3086)
Clarify that final capture progress carries disposition and cleanup states as key=value text inside message, with an illustrative wire-format example.

Fixes #3052.

Co-authored-by: 1052326311 <65798732+1052326311@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
2026-10-14 10:54:50 +08:00
d901561640 docs(hopper): clarify lease directory recovery guidance (#1628)
Clarify that path-specific Hopper lease recovery guidance applies when opening the lease directory fails with a permission error. Earlier creation failures retain their separate diagnostic.

The reviewed security implementation and owning regressions from this PR are already present on main through 599a8ee11, which explicitly records #1628 among its integrated fixes; this merge does not duplicate that code.

Co-authored-by: Sunil <180283757+Sunil56224972@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
Co-authored-by: N0zoM1z0 <161784452+N0zoM1z0@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-14 10:37:10 +08:00
morluto b3c5672ce6 fix(setup): preserve validated caller-selected JEB registration settings 2026-10-14 10:06:46 +08:00
morluto 70771523c5 chore(licenses): preserve locked XML parser runtime notices 2026-10-14 09:55:16 +08:00
morluto fd8a2e98ff fix(verifiers): own browser fixture setup and cleanup in one scope 2026-10-14 09:55:09 +08:00
morluto 278d7fb284 fix(analysis-view): reject accessor-backed inline evidence before parsing 2026-10-14 09:55:08 +08:00
morluto e8d22ec9eb fix(browser): carry unknown XML ancestry through metadata contracts 2026-10-14 09:54:58 +08:00
morluto e84c58dabf chore: merge upstream main while preserving bug fixes
# Conflicts:
#	README.md
2026-10-14 09:50:13 +08:00
morluto bffdd91053 fix(browser): preserve malformed capture gaps and protocol session identity 2026-10-14 09:49:49 +08:00
morluto e572c38dcf docs: repair analysis guide links and document Linux process prerequisites 2026-10-14 09:47:58 +08:00
morluto eca18e08ac fix(verifiers): require positive ownership and restore cleanup coverage 2026-10-14 09:47:57 +08:00
morluto 443c7aff77 fix(evaluation): require explicit REA server attribution 2026-10-14 09:47:57 +08:00
morluto 55778f20ba fix(inspector): retain usable targets and cancel owned capture waits 2026-10-14 09:47:57 +08:00
morluto 29a2d13994 fix(captures): validate SAZ XML and classify expansion limits 2026-10-14 09:47:56 +08:00
morluto 88afcc738b fix(javascript): retain endpoint candidates through graph projection 2026-10-14 09:47:30 +08:00
morluto 6e344302cb fix(javascript): prove bundled CommonJS getter identity 2026-10-14 09:47:30 +08:00
morluto 896696b2e7 fix(javascript): preserve semantic uncertainty and lexical ownership 2026-10-14 09:47:29 +08:00
morluto 9fe54c4073 fix(dotnet): honor bounded method row selections 2026-10-14 09:47:29 +08:00
morluto 36db19c59c fix(providers)!: preserve unknown segment extents and initialized IDA readiness
BREAKING CHANGE: binary_overview segment length is null when Hopper coordinates do not establish a finite same-address-space extent. Consumers must preserve this unknown instead of treating it as zero or a saturated value.
2026-10-14 09:47:28 +08:00
morluto 5fa7a52d1f fix(frida): preserve script diagnostics and isolate capture truncation 2026-10-14 09:47:28 +08:00
morluto b940ad0d65 fix(sqlite): retain completed worker evidence and unresolved cleanup ownership 2026-10-14 09:47:27 +08:00
morluto 31b99306aa fix(providers): retain composite cleanup failures and probe child liveness 2026-10-14 09:47:26 +08:00
c7105d9723 test(perf): preserve negative small-PCAP workflow measurements (#2640)
Preserve reproducible complete versus summary/page PCAP measurements on two official HTTP recordings and a fixed metadata/URI question. Summary/page uses more than twice the SDK JSON and about 51% more total model input in both real-model pairs, so existing complete defaults and production APIs/cache remain unchanged. These results apply to these captures and question; no general speedup is claimed. Refs #2016.

Authenticate experiment SDK envelopes with the pinned runtime's existing Evidence parser and check coverage, target/view/operation ownership and retained-parent identity. Separate factual and exact-workflow grading, retaining diagnostics and partial facts for malformed replies. Register six comparator CLI scenarios in the discovered Vitest evaluation project and remove the manual suite. The final change is limited to nine experiment files and one discovered evaluation test; production behavior is unchanged.

Validation includes eight actual SDK workflows and four actual Codex CLI tasks, independent raw-frame and CLI/MCP checks, authentication of all 18 original receipts, 34 context/grading regressions and six comparator scenarios. All four stored model answers pass factual and workflow regrading without model reruns. Original measured runtime remains 3ae826ceba6f25b08bce50bcb51001209bc16b57; original measurements and private raw artifacts are unchanged. Serial local checks use one CPU with 3-GiB early-stop/4-GiB hard/no-swap limits. Unknown terminal counters remain explicitly unknown.

Integrate upstream shared fixture owners and Windows verification, removing both superseded local differences. All final experiment/comparator files remain byte-identical to the reviewed sources and the final PR scope is 10 files. Full bounded preflight passes in 73.953 seconds with ~2.23 GiB peak; payload cleanup is verified, zero phase OOM/limit events are observed, and terminal counters remain unknown. Current-head automatic CI 38492231165 passes all 35 selected jobs, including every compiled shard, source/static/build/docs, actual Windows/native/package and real application lanes, Portable reports/inventory, reverse-tools coverage/mutation and CI required. Greptile passes and all review threads are resolved. Actual CI checkout is 4eb2262359cb01f2fb1314db0bc18d3dc9092792, integrated with main 41a2e84647. Passing retries do not establish the root cause or a fix of the earlier main worker cleanup failure; its original evidence is preserved. No release.

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-14 09:34:02 +08:00
YsopwnableandKaoru0822 b8513401e1 docs: celebrate 200,000 GitHub stars 🎉 (#2897)
Celebrate the 200,000-star milestone across the English README and all 19 current translations, preserving locale-specific number formatting.

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-14 09:31:59 +08:00
b11fe63bf4 docs(installation): document Linux procps prerequisite
Document the Linux procps-compatible ps prerequisite before quick start and in the installation/provider guides. Scope it to shared process-supervision workflows and give recovery guidance for generic provider startup errors.

Actual JavaScript CLI analysis passed with ps and refused an empty PATH with capability_unavailable and retained partial evidence. Real Chrome scenario launch succeeded without ps, confirming the need for qualified wording. Docs checks, formatting, typecheck/lint and diff checks passed. Five English documentation files; no runtime changes. Native Ghidra/JADX/firmware execution was not performed.

Fixes #2763

Co-authored-by: Avocado <93173363+HeadmasterEggy@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
2026-10-14 09:21:11 +08:00
bcc141549a feat(electron): inspect packaged Electron fuse state without execution (#2399)
Add bounded, read-only inspection of packaged Electron fuse state for ELF, PE, and Mach-O applications through CLI and MCP workflows.

Preserve artifact provenance and unknown values, resolve app bundles without execution, reject malformed or overlapping containers before scanning, and keep output compact for agent use.

Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
Co-authored-by: N0zoM1z0 <161784452+N0zoM1z0@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-14 09:16:33 +08:00
morluto fab33a66c6 test(package): select the intended Hopper smoke-test provider
Keep provider discovery independent from Hopper-specific packaged workflows. Installed Ghidra launchers on PATH must not turn the fixture into an ambiguous auto-selection request; target-free inventory and unknown-provider checks remain explicit.
2026-10-14 09:09:02 +08:00
morluto 86afc040e5 fix(ghidra): discover installations from resolved PATH launchers
Reuse installation inspection in doctor and provider readiness. Keep explicit configuration authoritative and validate discovered roots without executing launchers. Clarify the existing per-operation availability contract.
2026-10-14 09:05:53 +08:00
morluto 127c621328 Merge remote-tracking branch 'origin/main' 2026-10-14 08:59:31 +08:00
morluto 4d4631ac94 test: harden verification fixtures and runtime coverage
Use exact discovered test identities, production MCP sessions and byte-exact setup backups. Add CLI signal cleanup coverage and independent Ghidra, Go and firmware verification assertions; prune redundant test configuration and preserve platform limits.
2026-10-14 08:59:27 +08:00
morluto 1b14870ace refactor(evidence): remove superseded ordering wrapper and document exclusions 2026-10-14 08:58:53 +08:00
morluto a688c79784 docs(ida): describe catalog-based lifecycle compatibility 2026-10-14 08:58:46 +08:00
morluto 537467ab9e fix(test-fixtures): keep managed metadata and method bodies disjoint
Lay out resources, ReadyToRun data and IL bodies from their actual extents. Preserve RVA zero for bodyless methods and verify adjacent bodies through the production inspector.
2026-10-14 08:58:34 +08:00
morluto a769915f6e refactor(javascript): reuse canonical AST traversal
Remove the separate Vite walker and raw visitor-key require scan. Keep subtree selection at the shared traversal owner. All 103 focused builtin/Vite tests and the complete static and architecture checks pass.
2026-10-14 08:51:32 +08:00
morluto b58adafa4f fix(javascript): align module lookup and source evidence
Preserve CommonJS literal paths and confined ESM URL semantics, including package main fallback, directory intent, Unicode identity, and present opaque modules. Parse extraction inputs at their AST/source owner, retain skipped HTML and source-map coverage, and distinguish endpoint candidates from proven receiver facts. Version structural fingerprints with computed literal contents and propagate them through worker contracts.

Reviewed fixes include #1810, #1840, #2642, #2700, #2702, #2714, #2803, #2960, #2968, #2986, #3035, and #3036.

Validation: 158 resolver tests passed; public CLI/MCP module lookup controls, package-main/Unicode workflows, and six builtin workflows passed in the current compiled lane. Worker schema/fingerprint regressions passed. Large public pdf.js completion and real Electron analysis remain unverified.
2026-10-14 08:46:50 +08:00
41a2e84647 docs(installation): limit REA to one project or agent profile
Document existing Claude Code profile and per-project installation workflows, including scoped maintenance, backups, caller metadata and Windows commands. Clarify authentication storage and the filesystem/network restrictions required for enforced separation.

Validated 68 boundary tests, docs checks, formatting, typecheck/lint and two real REA setup dry-runs with zero applied actions or profile writes. Native Claude sessions, PowerShell execution and enforcement policies were not tested. Setup automation remains separate in #2187.

Co-authored-by: William Blair <60581672+williamblair333@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
2026-10-14 08:45:50 +08:00
morluto 04672f2b91 fix(javascript): preserve builtin identity and share alias traversal
Track module writes and escapes at their owner, preserve capture and reassignment ordering, and share CommonJS/default namespace identity without erasing parent-slot changes. Carry write sites into ambient alias resolution and use parsed write targets across consumers. Deduplicate returned-reference work with cycle context; retain unchanged temporal selection identity so provenance memoization stays effective.

Reviewed fixes include #2041, #2465, #2830, #2881, and #2948. Remove superseded mutation representations and the ineffective per-markValue dedup layer.

Validation: 380 focused JavaScript tests passed under a 384 MiB heap cap, including the previously aborting 30-level/128-call alias regression. Six public CLI/MCP builtin tests passed. Exact pdf.js completed-workflow performance remains unmeasured.
2026-10-14 08:44:52 +08:00
morluto 0f4c484be5 fix(reference): preserve missing symlink target facts
Treat ENOTDIR while resolving a symlink target as missing target evidence, preserving its spelling and partial inventory. Keep graph-level excluded-ancestor rejection separate from import success. The compiled boundary regression passes.
2026-10-14 08:43:59 +08:00
YsopwnableandKaoru0822 6a7f70fd54 test(windows): verify retained inspection ownership after rename (#2902)
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-13 17:39:38 -07:00
morluto 3ea5fdd7cf chore: reconcile current main fixes and client support 2026-10-14 08:36:38 +08:00
ff56f78594 fix(uninstall): remove the REA-owned ZCode fallback registration (#2783)
* fix(uninstall): remove the REA-owned ZCode fallback registration

ZCode loads the user-scope `~/.agents/mcp.json` `mcpServers` fallback
whenever its native `mcp.servers` table is empty. Uninstall removed REA
only from the native table, so a REA-owned fallback entry became eligible
to load again once the last native server was gone, while uninstall
reported ZCode as removed and complete.

Also visit the ZCode fallback as a standard `mcpServers` JSON
configuration in the uninstall host. It goes through the same
ownership check, `.rea.backup`, readback and restore path as every other
client configuration, so other fallback servers and settings, and a
foreign `rea` entry, are preserved. Its result merges into the ZCode item.

Fixes #2664

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(uninstall): preserve the strict ZCode fallback dialect

Address review findings at their producing boundaries and preserve directly affected consumers. Source inspection and git diff --check completed. Tests and automated checks were not run at the user’s explicit request.

* fix(comparison): recheck historical calls-only changes (#2223)

* fix(comparison): preserve incomplete function call evidence

Keep missing, unknown, partial and unresolved call relationships unknown in function comparisons and reconstruction claims. Aggregate IDA caller and callee coverage without overwriting uncertainty.

Validate with 144 focused tests, typecheck/lint, docs checks and test discovery. Real providers were not verified; the full headless IDA session suite requires unavailable Windows native controls.

* fix(comparison): recheck historical calls-only changes

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>

* fix(ci): isolate Linux Hopper process fixture inventory

* fix(android): make DEX descriptor pattern portable

* fix: repair the static-check failures on the default branch (#2895)

* fix: repair the static-check failures on the default branch

The default branch fails its own required `Static checks` job three ways, so
every pull request reports that job as failed regardless of its own change.

- `npm run format:check` reports `src/domain/functionComparison.test.ts` and
  `src/ida/IdaOperationRunner.ts`.
- `npm run lint` reports a duplicate `import { join } from "node:path"` in
  `tests/boundary/mcp/ghidraInventoryMcp.test.ts` (lines 4 and 17).
- `npm run typecheck` reports `format: "devin"` missing from the client format
  union in `src/application/SupportedClients.ts`, and an unchecked index in
  `tests/boundary/cli/clientProfileLifecycle.test.ts`, whose pair array is
  inferred as `string[][]`.

Apply the pinned formatter, drop the duplicate import, list the `"devin"` format
that its own definition already uses, and mark the pair array `as const`.

Validation: `npm run check:fast` and `npm run format:check` pass.

* fix(setup): keep Devin on the JSON configuration dialect

---------

Co-authored-by: 吴佳浩 <wujiahao@supremind.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
Co-authored-by: Lucien <164857476+dakjdakd@users.noreply.github.com>
Co-authored-by: 吴佳浩Alben <35291372+XiaoBinGan@users.noreply.github.com>
Co-authored-by: 吴佳浩 <wujiahao@supremind.com>
2026-10-13 17:33:26 -07:00
679658b287 fix(reference): report entries beyond the path limit as I/O failures (#2716)
* fix(reference): report entries beyond the path limit as I/O failures

Reference-source traversal joins each child onto the root path. A valid
tree whose nested entries exceed the platform's absolute path limit
(PATH_MAX 1024 on macOS) makes lstat fail with ENAMETOOLONG. The
reader's filesystem error allow-list lacked that code, so
filesystemFailureDetail returned undefined and the native rejection
escaped both readReferenceSource and importReferenceSource instead of
becoming the existing failed `io` entry.

Add ENAMETOOLONG to the allow-list, matching the JavaScript application
reader's list. Readable siblings stay in the graph and the unreachable
entry is recorded with its OS message.

Fixes #2652

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(reference): assert the imported graph keeps readable and failed entries

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(process): report no remaining time after a provider deadline fires

Node timers can fire while performance.now() is still a fraction of a
millisecond short of the monotonic deadline. ProviderStartupDeadline then
reported interruption "timeout" and an aborted signal while remainingMs()
returned ceil(0.x) = 1, so polling loops guarded by remainingMs() > 0
(Hopper and Ghidra startup) could run past an elapsed deadline, and the
real-timer test "aborts a bounded wait when its real monotonic deadline
expires" failed intermittently in CI (expected 1 to be 0).

Once the deadline's own timer has classified a timeout, report zero
remaining time. A deterministic regression test fires the timer with the
monotonic clock 0.4 ms short of the deadline.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(reference): preserve path limit entry uncertainty

* fix(reference): preserve unknown entry failures and identity

* fix(reference): type unknown-kind exclusions and keep them out of symlink counts

The metadata-failure path offers "unknown" to exclusion predicates, but
the predicate type only admitted concrete kinds, which broke the build.
Directory-only secret and ignore rules now also match an unknown-kind
entry (it may be a directory), and the summary counts unknown entries
separately instead of as symlinks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
Co-authored-by: Codex <codex@users.noreply.github.com>
2026-10-13 17:33:09 -07:00
morluto 5b7fe94710 fix(analysis): preserve extraction gaps and database failure reasons
Derive firmware completeness from examined coverage, retain linked continuation evidence, and preserve reported extraction limits. Keep Android package attribution and Go image fixtures coherent, and classify SQLite busy failures without discarding descriptor cleanup facts.

Reviewed fixes include firmware, ADB, SQLite, and recorded-transport bug reports and their focused PRs.

Validation: firmware report source tests passed 21 cases; prior focused artifact/provider boundaries passed. Real Android devices and external firmware tools were not rerun.
2026-10-14 08:32:15 +08:00
morluto 80de3145e4 fix(mcp): align advertised contracts and retain workflow evidence
Use one named input-schema projection for clients and catalog identity, preserve dialect declarations, and bound compact schema depth. Keep canonical SDK output validation when output advertisements are disabled. Release request-owned state on cancellation and carry upstream limitations through composed workflows. Preserve doctor full-output envelopes and validate navigation facts at their owner.

Reviewed fixes: #2088, #2108, #2513, #2684, #2693, #2695, #2836, #2864, #2868, #2877, #2933, #2998, #3002, #3015.

Validation: focused schema, SDK output-validation, context, and workflow tests passed; no schema dialect or canonical output validation was relaxed.
2026-10-14 08:32:00 +08:00
morluto f6905719c3 fix(browser): bound capture work and preserve observation gaps
Own service-worker CDP registrations and retry failed teardown. Admit redaction and network metadata against remaining capture capacity, retain explicit omission reasons, and preserve source URL, redirect ancestry, caller-selected content, and historical capture values. Forward CLI cancellation through provider cleanup.

Reviewed fixes: #2263, #2637, #2767, #2845, #2859, #2863, #2872, #2874, #2876, #2879, #2955, #2957, #2961, #3033.

Validation: 25 current event/network source tests passed; earlier focused browser lanes passed 201 source tests and 176 boundary tests with two skips, plus 35 Python tests. Real browser/Windows lanes were not rerun. Playwright allocation before admission remains unbounded by these retention limits.
2026-10-14 08:31:29 +08:00
a6c3d3a365 docs(skill): route browser actions to scenario capture
Route user browser actions to passive observation and agent actions to declared scenarios. Clarify requested artifacts, step outcomes, connect navigation and cleanup, and the limits of listener and request-initiator evidence. Distinguish discarded raw headers from retained response metadata. Update the bundled skill to version 42 with matching package metadata.

Validation on the synchronized revision: docs (5 tests), typecheck/lint, formatting, 61 focused tests, and real Linux Chromium launch/connect and passive CLI probes. Verified declared steps, requested artifacts, digests, external page preservation, temporary form reset and cleanup. Native Windows/macOS and separate follow-up tools were not rerun. Package install/update and CLI/MCP verification passed earlier on 738d02a, before this main sync.

Co-authored-by: 0xRafie <155859586+0xRafie@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
2026-10-14 08:31:16 +08:00
morluto ec271121e4 fix(artifacts): preserve path identity and exclusion applicability
Normalize path facts once at the owning filesystem boundary, preserve caller spelling and unavailable inventory members, and retain typed read failures. Record exclusion entry kinds so only proven directories can establish descendant symlink targets; omitted legacy kinds stay unknown.

Reviewed fixes include #1969, #2642, #2759, and #2994.

Validation: 26 reference graph source tests passed; prior artifact checks passed 275 tests with one Windows-only skip. Updated compiled import workflows are included in final validation after regeneration.
2026-10-14 08:31:16 +08:00
morluto 55dd21a5e2 fix(native): retain partial format facts and explicit coverage
Preserve XML and Interface Builder identity at their parsing boundary, retain usable Objective-C slots with precise coverage, and keep native trace failures distinct from absence. Add caller-selected Interface Builder summaries backed by complete retained Evidence, and preserve comparison limitations.

Reviewed fixes: #1969, #2088, #2108, #2513, #2639, #2684, #2731, #2759, #2875, #2888, #2889, #2970, #2971, #3002.

Validation: relevant native/domain focused lane passed 142 tests; earlier Interface Builder/MCP checks passed 24 tests. Real native UI and Ghidra producer coverage was not repeated.
2026-10-14 08:29:16 +08:00
YsopwnableandKaoru0822 d82ad1faf0 test(android): preserve malformed DEX prototype rejection (#2917)
Preserve malformed DEX parameter/return rejection with tagged producer errors and exact issue paths.

The equivalent portable descriptor fix is already on main via #2910.

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-14 08:28:23 +08:00
morluto d4bd65c53c fix(providers)!: preserve readiness, coverage, and diagnostic ownership
Share tagged readiness observations, retain partial provider facts, authenticate bridge identity, bound diagnostics, and preserve cancellation and lost-session reasons. Fail closed for unparsed MIPS ABI attributes and macOS saved Hopper databases without a script-capable bridge route.

Reviewed fixes: #2010, #2108, #2513, #2695, #2731, #2771, #2828, #2867, #2868, #2870, #2880, #2919, #2945, #2961, #2981, #2998, #3000, #3002.

Validation: 285 focused source/boundary tests passed. Real Ghidra, IDA, Linux Hopper, and Windows provider runs were not repeated.

BREAKING CHANGE: MIPS targets with unresolved GNU ABI attributes and saved Hopper databases on macOS are refused rather than advertised as supported.
2026-10-14 08:28:14 +08:00
JANG BYUNGKUN 2dc89f81be docs(readme): sync Korean README with the English README (#3046) 2026-10-14 08:27:37 +08:00
morluto ec56c1da04 fix(setup): preserve registration identity and recovery ownership
Keep layered environment choices, current byte-exact recovery snapshots, and package-manager failure details. Report pinned package drift without claiming runner resolution. Retain failed Linux installer cleanup for host-owned retry while preserving the install outcome.

Reviewed fixes include #2632, #2769, #2783, #2959, #2979, #2983, #2995, #3009, and #2559.

Validation: 98 focused setup/registration tests passed; 45 Linux installer tests passed. Real Linux package installation remains unverified.
2026-10-14 08:25:53 +08:00
morluto 30128a03cf refactor(domain): centralize relationship coverage interpretation 2026-10-14 08:22:50 +08:00
morluto 599a8ee11d fix(process): preserve cleanup ownership and normalize captures once
Retain unresolved process identity and launch evidence through retryable cleanup. Share capture path normalization and preserve failed filesystem observations. Bound child output and drain Windows pipes without one-chunk throttling.

Reviewed fixes include #1628, #2632, #2644, #2784, #2928, #2931, #2965, #2992, and #3011.

Validation: 92 focused source tests passed; four platform-specific skips. Real Windows and Hopper lifecycle coverage was not rerun.
2026-10-14 08:19:07 +08:00
Xiangfang Chen e049ef70f7 feat(setup): add WorkBuddy client support (#3041)
* feat(setup): add WorkBuddy client support

Register rea in the global ~/.workbuddy/mcp.json through a new workbuddy
entry in SUPPORTED_CLIENT_DEFINITIONS (json dialect, ~/.workbuddy marker,
~/.workbuddy/skills skill directory; WorkBuddy AI shares the file).
Document the client, extend the unreleased-client note, and cover
registration and uninstall with boundary tests.

* docs(setup): record verified WorkBuddy tool calls

Real-client verification completed: WorkBuddy loads the registration and
real REA tool calls execute through the registered server.
2026-10-14 08:12:33 +08:00
xpeng5278-webandxpeng5278-web bbf0638e88 fix(javascript): report EventSource and sendBeacon URLs as network endpoints (#3044)
Co-authored-by: xpeng5278-web <330743750+xpeng5278-web@users.noreply.github.com>
2026-10-14 08:10:04 +08:00
242e2b0001 fix(ghidra): preserve function facet and call coverage (#3002)
* fix(ghidra): preserve observed function facet and call coverage

Declare successful query availability in the Java producer, including explicit unavailable pseudocode reasons. Carry observed static call-set coverage through the parsed provider result and Evidence without inventing facts for legacy omissions. Use Ghidra indexed comment addresses over the function body and entry, retaining cancellation checks and sparse collection.

Closes #2951

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>

* fix(ghidra): retain unresolved call coverage in observations

Share decoded unresolved-call facts between dossiers, callee coverage and reference observations. Combine terminal and unresolved observations in one instruction traversal and retain site records only when requested. Keep missing procedure identities incomplete and validate unresolved locations at the provider boundary.

Bind snapshots to the corrected call coverage profile so older complete claims cannot replay under the new semantics. Add real public MCP callback trace/comparison regressions.

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>

* fix(ghidra): separate reference targets from callee coverage

Reuse containing-function ownership for resolved reference identities and independently compare those identities with the actual returned callee set. Keep known interior routes out of unresolved site records while retaining partial inventory coverage.

Advance the coverage profile to v3 and reject interim v2 cache bindings. Add a real Ghidra producer regression with restored reference metadata; the previous bridge fails the known-route assertion.

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>

* fix(ghidra): measure incoming call coverage across function bodies

Compare incoming reference source identities across the owned function body and entry with the actual returned caller set. Stream caller-only checks and reuse dossier reference observations. Combine caller/callee coverage from observed counts without reinterpreting serialized metadata, and share the body scope owner with comments and references.

Bind snapshots to function-body coverage semantics v4 and preserve the real off-entry caller regression.

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>

* fix(ghidra): preserve whole-scope directional call coverage

Check body-and-entry call references independently of decoded instruction flow. Reuse collected dossier references and stream inventory-only coverage checks. Keep caller-only gaps distinct from incomplete outgoing coverage in offline path reconstruction, and invalidate prior snapshot profiles.

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>

* fix(mcp): retain compact Evidence field types

Share optional diagnostics across the three tagged relationship coverage states instead of advertising duplicate branches. Preserve coverage validation while keeping compact Evidence properties within the existing reference expansion budget. Verify invalid direction cardinalities against the actual SDK-advertised schema.

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>

---------

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-14 07:52:14 +08:00
Saim Shafiqueandmorluto 20e4f83097 fix(ghidra): fail doctor when the JVM os.arch selects an unvalidated decompiler directory (#2792)
* fix(ghidra): fail doctor when the JVM os.arch selects an unvalidated decompiler directory

probeJava now also reads the JVM os.arch property. When it maps to a
different native decompiler directory than the one validated from the
host architecture (for example an x86_64 JDK under Rosetta on Apple
silicon selecting os/mac_x86_64 while only mac_arm_64 was built), the
native_decompiler check fails with a remediation pointing at a
matching-arch JDK instead of reporting a healthy installation that
fails every analysis. Unknown os.arch values keep the previous
behavior.

* fix(ghidra): tighten JVM arch remediation and cover os.arch extraction

Address review findings on the JVM os.arch decompiler-directory check:
- The remediation no longer suggests building the decompiler for the
  JVM-selected directory, which could never clear the check; it only
  advises pointing JAVA_HOME at a matching-architecture JDK.
- Add a test running the real Java probe against captured
  -XshowSettings:properties output, asserting os.arch reaches the
  installation diagnostics.
- Document the JDK-architecture prerequisite in the Ghidra setup guide.

* fix(ghidra): refuse unrecognized JVM native architectures

* docs(ghidra): align doctor prerequisites with JVM architecture admission

* fix(ghidra): reject unreported JVM architecture

* test(ghidra): separate JVM and executable admission cases

* fix(ghidra): defer decompiler arch validation when Java is absent

* fix(ghidra): reject JVM native architecture override

* fix(ghidra): validate JVM platform overrides

* fix(ghidra): validate JVM os.name and helper-environment platform overrides

* test(ci): isolate report fixtures and declare PowerPC Java bounds

* test(ghidra): provide observed JVM platform in PowerPC fixture

* fix(ghidra): parse quoted JVM environment options before admission

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-14 07:31:11 +08:00
97aff28201 fix(electron): reject non-executable paths and contain launch rejections (#2705)
* fix(electron): reject non-executable paths and contain launch rejections

An executable_path that runs but never prints the inspector line made
Playwright's electron.launch() leave three waits unawaited. They rejected
unhandled and Node ended the CLI or the MCP server (#2269).

Check the resolved executable_path is a regular file, and on non-Windows
hosts that it passes X_OK, before any launch. Wrap only the launch call
in a scoped unhandledRejection listener that absorbs rejections whose
stack is in playwright-core/lib and re-throws anything else.

Tested on Linux with Node 22.23.1, 24.18.0 and 26.11.1.

Refs #2269

🤖 Generated with Claude Code (Sonnet 5.5, verified by Opus 5.5) 🦊

* fix(electron): absorb only this launch's orphaned rejections

The listener around the launch dropped every rejection with a
playwright-core stack, so an unrelated Playwright failure during the
launch window would vanish. Hold those rejections instead. When the
launch settles, drop only the ones whose message is part of the launch's
own error (the orphans of a failed launch). Raise every other one again
through the unhandledRejection path, so the process still fails. A launch
that succeeds raises all of them. Rejections without a Playwright stack
still fail at once.

One setImmediate turn before the decision lets rejections queued by the
settling tick arrive first. It is a single turn, not a timer. A shared
set of absorbed errors stops overlapping launches from raising each
other's orphans. The listener is removed on every path.

Add child-process tests that drive the built provider through its launch
seam, since Vitest's worker has its own unhandledRejection listener.

🤖 Generated with Claude Code (Sonnet 5.5, verified by Opus 5.5) 🦊

* test(electron): distinguish launch success from capture ownership

* test(electron): assert cleanup ownership failures around overlapping launches

* fix(comparison): recheck historical calls-only changes (#2223)

* fix(comparison): preserve incomplete function call evidence

Keep missing, unknown, partial and unresolved call relationships unknown in function comparisons and reconstruction claims. Aggregate IDA caller and callee coverage without overwriting uncertainty.

Validate with 144 focused tests, typecheck/lint, docs checks and test discovery. Real providers were not verified; the full headless IDA session suite requires unavailable Windows native controls.

* fix(comparison): recheck historical calls-only changes

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>

* fix(android): make DEX descriptor pattern portable

* fix(ci): isolate Linux Hopper process fixture inventory

* fix(browser): avoid replaying observed launch rejections

* style: format comparison and IDA sources

* test(electron): allow cold macOS ownership setup

* test(electron): allow cold macOS setup in the early-exit path test

The test that runs an executable which exits at once builds the Swift
ownership helper twice on macOS: in the CLI process, and again in the
MCP server process on its first tool call that prepares. A cold Swift
cache can spend up to 120 seconds on a build. The test had only the
30 second default.

Give the test 180 seconds, as e1579ec31 did for
electronLaunchRejections.test.ts and 65a809a0d did for
webSourceLocationCli.test.ts. cli.run has no deadline of its own, so
it needs no timeoutMs.

🤖 Generated with Claude Code (Sonnet 5.5, verified by Opus 5.5) 🦊

* test(ci): isolate report fixtures and declare PowerPC Java bounds

---------

Co-authored-by: Claude Opus 5.5 <opus@fox.invalid>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
Co-authored-by: Lucien <164857476+dakjdakd@users.noreply.github.com>
2026-10-14 07:30:14 +08:00
641a8395ee fix(cli): refuse a JavaScript integrity policy the binary analyze lane cannot honor (#2808)
* fix(cli): refuse an integrity policy the binary analyze lane cannot honor

`analyze` declares --integrity-policy for its JavaScript lane and forwards it
only there. On a binary target the flag was accepted and then dropped:
inspect_artifact-style integrity handling does not exist on this path, and
binary_overview is a strictObject with no input at all, so the request could
never take effect and the run proceeded as if the caller had not asked.

The same branch already refuses source_paths on a binary target with a typed
AnalysisInputError, and the sibling `inspect` command refuses the flag outright,
so the refusal is the established handling here. The policy option carries a
default, so the check keys off a value that differs from the owner schema's own
default rather than the flag's presence; otherwise every binary analyze would be
refused. The default is read from the schema instead of being restated.

Verified with 5 integrity policy CLI cases, check:fast and
verify:test-discovery (1023 files) on macOS arm64, and by running the built CLI
for the no-flag, explicit-default and record-and-continue cases. The regression
case failed with the command reaching the binary lane before the change.

* fix(cli): reject explicit default integrity policies for binaries

* fix(cli): keep an omitted integrity policy absent on the binary lane

`javascriptApplicationOptions.shape.integrityPolicy` fills an omission with its
default, and `.optional()` keeps reporting that parsed value, so
`options.integrityPolicy !== undefined` was true for every `analyze <target>`
call: ordinary binary analysis was rejected before the target was even read.

List `z.undefined()` first in a union so an omitted option stays absent while
the owner still validates a supplied value, and reapply the owner's metadata so
help and `--schema` keep describing the option.

Validation: tests/boundary/cli/javascriptIntegrityPolicyCli.test.ts reports
1 failed | 5 passed without this change (the case "still routes a binary
analyze target without an integrity policy" returned invalid_request for
integrity_policy instead of reaching the target) and 6 passed with it;
`npm run check:fast` passes on macOS 26.5.1 arm64 with Node.js 24.18.0.

* fix(cli): omit integrity defaults from binary option schema

* test(ci): isolate report fixtures and declare PowerPC Java bounds

---------

Co-authored-by: 吴佳浩 <wujiahao@supremind.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-14 07:28:00 +08:00
1ae9000587 fix(javascript): exclude TypeScript this annotations from runtime parameters (#2761)
* fix(javascript): exclude TypeScript this annotations from runtime parameters

* test(javascript): assert exact typed argument bindings

* test(apple): keep dyld slice regression portable

* fix(process): report no remaining time after a provider deadline fires

Node timers can fire while performance.now() is still a fraction of a
millisecond short of the monotonic deadline. ProviderStartupDeadline then
reported interruption "timeout" and an aborted signal while remainingMs()
returned ceil(0.x) = 1, so polling loops guarded by remainingMs() > 0
(Hopper and Ghidra startup) could run past an elapsed deadline, and the
real-timer test "aborts a bounded wait when its real monotonic deadline
expires" failed intermittently in CI (expected 1 to be 0).

Once the deadline's own timer has classified a timeout, report zero
remaining time. A deterministic regression test fires the timer with the
monotonic clock 0.4 ms short of the deadline.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(ci): restore descriptor and fixture validation

* test(ci): retain verification coverage within lint limits

* fix(test): share admitted Ghidra profile installation fixtures

* fix(ci): isolate timing report fixtures from workflow state

---------

Co-authored-by: hhang <169027046+xu91102@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-14 07:18:50 +08:00
morluto 02987d8e8c test(hopper): bind string assertions to observed source extents
Remove OS-based assumptions about long and non-ASCII string recognition. Require the dedicated fragmented fixture, independently verify full source bytes, and reject gaps, overlaps, incomplete reads and corrupted decoding. Report unrecognized native strings explicitly and retain the actual procedure count.

Verified through 14 verifier regressions and the complete macOS arm64 Hopper 6.6.0-demo CLI/MCP lane with cleanup.
2026-10-14 07:12:10 +08:00
morluto b1b0211c28 fix(native): preserve observed function dossier coverage
Declare observed Ghidra and Hopper facets, preserve unavailable pseudocode, and attach dossier call-set coverage. Version the analysis profiles to invalidate older snapshots and remove the retired include_assembly fixture branch.

Add MCP comparison and snapshot-replay regressions plus real-provider checks for empty and unavailable facets. Fixes #2980.
2026-10-14 07:11:55 +08:00
Shivekandmorluto 80592b82a6 fix(javascript): keep CommonJS percent bytes literal (#3001)
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-14 06:50:29 +08:00
b9b3a9098b fix(gemini): compare consolidated allowlists through one normalizer (#2495)
* fix(gemini): compare consolidated allowlists through one normalizer

geminiServerPolicyBlock consolidates allowlists by normalized intersection but
tested the consolidated list against the raw server key. Because the
intersection keeps the spelling of whichever policy came first, the same pair of
policies blocked the server in one arrangement and allowed it in the other, and
the excluded test had the mirror-image asymmetry.

Normalize the comparison key once and use the same normalizer for the exclusion
test, the intersection and the membership test. The messages keep the
caller-facing server key, so lookup and display stay separate.

Verified with 88 client profile, lifecycle and registration tests, check:fast
and verify:test-discovery on macOS arm64. The regression test drives setup twice
with the same policies in both arrangements; before the change one arrangement
failed and the other passed.

* test(gemini): cover normalized exclusions and padded allowlist entries

The regression case added with the fix drove the allowlist intersection in
both spellings but left the other two comparisons the change touches
uncovered: the exclusion membership test and the trimming applied to padded
names. Reverting either behaviour kept the suite green, so neither was
pinned. Greptile raised exactly this on the PR.

An exclusion is now exercised as " REA " against the selected server, and a
padded allowlist entry is exercised as the only policy, so a padded entry has
to permit the same server its exact spelling permits rather than fall to the
membership test.

Verified with `npm run test:focused -- tests/boundary/cli/clientProfiles.test.ts`:
14 passed with the change; reverting only ClientConfigurationDocument.ts to
main fails both cases, the arrangement case with `rea+REA: expected 1 to be +0`
and the new one on both the excluded and the padded-allowlist assertions.

* fix(process): report no remaining time after a provider deadline fires

Node timers can fire while performance.now() is still a fraction of a
millisecond short of the monotonic deadline. ProviderStartupDeadline then
reported interruption "timeout" and an aborted signal while remainingMs()
returned ceil(0.x) = 1, so polling loops guarded by remainingMs() > 0
(Hopper and Ghidra startup) could run past an elapsed deadline, and the
real-timer test "aborts a bounded wait when its real monotonic deadline
expires" failed intermittently in CI (expected 1 to be 0).

Once the deadline's own timer has classified a timeout, report zero
remaining time. A deterministic regression test fires the timer with the
monotonic clock 0.4 ms short of the deadline.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: 吴佳浩 <wujiahao@supremind.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 06:43:03 +08:00
Yann Roberto 1f122c02b7 fix(javascript): exclude HTML URL suffixes from path admission (#2833) 2026-10-14 06:41:22 +08:00
bcb60c179e fix(browser): read expression-free template literals as module specifiers (#2842)
Captured-bundle chunk edges and source-map original module edges only
accepted quoted string literals as import()/require() specifiers. Bundlers
such as rolldown emit plain constants as expression-free template literals
(import(`./chunk.js`)), so statically known dependencies were dropped while
completeness stayed complete. Read both sites through the shared exact-literal
reader (stringValue); template literals with substitutions stay unrecognized.

On OWASP Juice Shop v20.3.0 this recovers 8 lazy chunk edges (previously 0).

Fixes #2841
Fixes #2599

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: morluto <17176810+morluto@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-14 06:40:25 +08:00
c6166d4b56 fix(javascript): retain bridge conditions on IPC joins (#2747)
* fix(javascript): retain bridge conditions on IPC joins

* fix(javascript): retain IPC condition ownership

* style(javascript): format bridge path regression

* fix(process): report no remaining time after a provider deadline fires

Node timers can fire while performance.now() is still a fraction of a
millisecond short of the monotonic deadline. ProviderStartupDeadline then
reported interruption "timeout" and an aborted signal while remainingMs()
returned ceil(0.x) = 1, so polling loops guarded by remainingMs() > 0
(Hopper and Ghidra startup) could run past an elapsed deadline, and the
real-timer test "aborts a bounded wait when its real monotonic deadline
expires" failed intermittently in CI (expected 1 to be 0).

Once the deadline's own timer has classified a timeout, report zero
remaining time. A deterministic regression test fires the timer with the
monotonic clock 0.4 ms short of the deadline.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(javascript): preserve Electron join condition coverage

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Codex <codex@users.noreply.github.com>
2026-10-14 06:39:27 +08:00
3a4d49406a fix(javascript): preserve Electron runtime entrypoint paths (#2742)
* fix(javascript): preserve Electron runtime entrypoint paths

* fix(javascript): resolve runtime entrypoints before modules

* fix(javascript): preserve Electron runtime path semantics

* fix(javascript): resolve Electron renderers exactly

* fix(javascript): retain runtime package aliases

* fix(javascript): retain missing renderer URL paths

* fix(javascript): resolve Electron URL and utility entries correctly

* fix(javascript): reject suffix-only worker URLs

* fix(javascript): respect Electron URL and utility path semantics

* fix(javascript): separate Node runtime entrypoints from source imports

* fix(comparison): recheck historical calls-only changes (#2223)

* fix(comparison): preserve incomplete function call evidence

Keep missing, unknown, partial and unresolved call relationships unknown in function comparisons and reconstruction claims. Aggregate IDA caller and callee coverage without overwriting uncertainty.

Validate with 144 focused tests, typecheck/lint, docs checks and test discovery. Real providers were not verified; the full headless IDA session suite requires unavailable Windows native controls.

* fix(comparison): recheck historical calls-only changes

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
(cherry picked from commit 688defca0f)

* fix(ci): isolate Linux Hopper process fixture inventory

(cherry picked from commit 8cac51e121)

* fix(android): make DEX descriptor pattern portable

(cherry picked from commit 1335bcddc5)

* fix: repair the static-check failures on the default branch (#2895)

* fix: repair the static-check failures on the default branch

The default branch fails its own required `Static checks` job three ways, so
every pull request reports that job as failed regardless of its own change.

- `npm run format:check` reports `src/domain/functionComparison.test.ts` and
  `src/ida/IdaOperationRunner.ts`.
- `npm run lint` reports a duplicate `import { join } from "node:path"` in
  `tests/boundary/mcp/ghidraInventoryMcp.test.ts` (lines 4 and 17).
- `npm run typecheck` reports `format: "devin"` missing from the client format
  union in `src/application/SupportedClients.ts`, and an unchecked index in
  `tests/boundary/cli/clientProfileLifecycle.test.ts`, whose pair array is
  inferred as `string[][]`.

Apply the pinned formatter, drop the duplicate import, list the `"devin"` format
that its own definition already uses, and mark the pair array `as const`.

Validation: `npm run check:fast` and `npm run format:check` pass.

* fix(setup): keep Devin on the JSON configuration dialect

---------

Co-authored-by: 吴佳浩 <wujiahao@supremind.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
(cherry picked from commit df3d96da80)

---------

Co-authored-by: morluto <morluto@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
Co-authored-by: Lucien <164857476+dakjdakd@users.noreply.github.com>
Co-authored-by: 吴佳浩Alben <35291372+XiaoBinGan@users.noreply.github.com>
Co-authored-by: 吴佳浩 <wujiahao@supremind.com>
2026-10-14 06:39:17 +08:00
7072f87baf fix(artifacts): preserve Mach-O load-command string identity (#2110)
* fix(artifacts): preserve Mach-O load-command string identity

* fix(process): report no remaining time after a provider deadline fires

Node timers can fire while performance.now() is still a fraction of a
millisecond short of the monotonic deadline. ProviderStartupDeadline then
reported interruption "timeout" and an aborted signal while remainingMs()
returned ceil(0.x) = 1, so polling loops guarded by remainingMs() > 0
(Hopper and Ghidra startup) could run past an elapsed deadline, and the
real-timer test "aborts a bounded wait when its real monotonic deadline
expires" failed intermittently in CI (expected 1 to be 0).

Once the deadline's own timer has classified a timeout, report zero
remaining time. A deterministic regression test fires the timer with the
monotonic clock 0.4 ms short of the deadline.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit dfdaa0ca74)

* fix(ci): isolate Linux Hopper process fixture inventory

* fix(comparison): recheck historical calls-only changes (#2223)

* fix(comparison): preserve incomplete function call evidence

Keep missing, unknown, partial and unresolved call relationships unknown in function comparisons and reconstruction claims. Aggregate IDA caller and callee coverage without overwriting uncertainty.

Validate with 144 focused tests, typecheck/lint, docs checks and test discovery. Real providers were not verified; the full headless IDA session suite requires unavailable Windows native controls.

* fix(comparison): recheck historical calls-only changes

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>

* style: format identity comparison and IDA runner

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Lucien <164857476+dakjdakd@users.noreply.github.com>
2026-10-14 06:38:32 +08:00
Jeremy Levartovskyandmorluto 1ebbc3b371 fix(managed): pair members by declared identity before name-free remaps (#2116)
* fix(managed): pair members by declared identity before name-free remaps

The first matching round keyed methods on signature and normalized CIL,
and fields on signature, without the declared type or name. Identical
members of different types fell into one ambiguous group, so a file
compared with itself reported unknowns, and a lone match across types
was reported as an unchanged exact pair while the member's own
counterpart went unpaired.

Pair by declared type, name, and exact CIL/signature first, then by
declared type, name, and raw signature. The name-free CIL/signature,
field-signature, and structural rounds now run last, as remaps, and
skip a pair across declared identities while either member's own type
and name remain unpaired on the other side.

Nested types that share a bare name still share one declared identity,
so their identical members stay ambiguous in a self-comparison.

* fix(managed): keep members with kept identities out of remap rounds

The cross-identity check applied only to one-to-one groups. A member
whose own declared type and name were still unpaired on the other side
could fall into a larger remap group and be reported ambiguous, and a
blocked candidate could make an otherwise unique remap ambiguous.

A remap round now drops such members before grouping, along with
members whose declaring type is unresolved. Same-identity structural
pairs get their own round before the remaps. Each member's declared
identity is computed once.

Ambiguity limitations now name the evidence a group shares, which
stays true whether the round keyed on names or not.

* fix(managed): keep names in unresolved identities and hold ambiguous ones

A member of an unresolved declaring type had no declared identity, so
the declared-identity rounds keyed it by CIL or signature alone. Two
unresolved members with different names paired as an exact match, and
a file compared with itself reported unresolved members ambiguous.

The declared identity now always includes the name, with a null type
for an unresolved one. Remap rounds still leave such members out.

A remap round now also leaves out a member whose own identity sits in
an ambiguous group on the other side, not only one left one-sided.

The undecoded-counterpart check uses the same declared identity.

* fix(managed): keep remap groups with unresolved owners ambiguous

Remap rounds dropped members of an unresolved declaring type before
grouping. Such a member can be another member's own counterpart, so a
resolved member could remap to a copy in another type while its
unresolved counterpart was reported added.

Unresolved members now join remap grouping, and a group that contains
one stays ambiguous instead of pairing.

The undecoded-counterpart set reuses each member's computed identity.

* fix(managed): keep ownerless members out of declared rounds when shared

A member whose declaring type did not resolve has a declared identity
of a null type and its name. The declared rounds paired two such
same-named members even when the other side also held a resolved
member with the same CIL and signature, which could be the owner. The
old name-free first round reported that group as ambiguous.

An ownerless member now enters the declared rounds only when no other
member on either side shares its exact CIL/signature key, and never
the declared structural round. Otherwise it reaches the remap rounds,
which keep any group with an unresolved member ambiguous.

* fix(managed): keep competitors of an ownerless member out of the declared round

When an ownerless member shares its exact CIL/signature key with another
member on the same side, its owner may be that member. The resolved
members holding the key now stay out of the declared round too, so they
cannot pair and consume the candidate the ownerless member may own, and
the remap round keeps the whole group ambiguous as main does. A lone
ownerless member still pairs with its lone counterpart.

* test(managed): split the ownerless comparison tests under the function length limit

The ownerless describe block reached 181 lines, past oxlint's
max-lines-per-function limit of 150. Move the structural-competitor
case into its own block.

* fix(managed): contest signatures and remap only open candidates

* fix(managed): retain blocked raw-signature choices as unknown

* fix(managed): keep member names in signature contention

* fix(managed): scope residual signature contention by name once

ownerlessContested applies competitionIdentityKey itself, but
unresolvedSignatureAmbiguities passed it keys that were already
name-scoped, so the contested set never matched the groups and the
same-signature, different-body case reported additions and removals
instead of one unknown group. Pass the raw signature key.

* fix(managed): constrain matchObserved to declared members

matchObserved passes its items to matchByKeys, which needs Declared, so
its looser { declaring_type } bound failed typecheck (TS2344, TS2322).

* fix(managed): retain held ownership contention for residual matches

* fix(test): bind workspace fixture in Hopper launch cases

* fix(ci): prepare worker tests and preserve Hopper cleanup ordering

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-14 06:36:43 +08:00
Ariel Fikru Avicennaandmorluto c86b09a109 feat(ghidra): admit 32-bit big-endian PowerPC ELF (#1747)
* feat(ghidra): admit 32-bit big-endian PowerPC ELF

Recognize EM_PPC as a provider-neutral `powerpc` family that keeps its ELF
class, byte order, type and flags, and admit ELF32 big-endian ET_EXEC through
a bounded Ghidra lane. ELF64, little-endian, relocatable/shared objects and
EF_PPC_EMB are refused with a specific reason; Hopper and IDA do not gain
PowerPC support, and Windows Ghidra P0 is unchanged.

Ghidra's ELF loader selects PowerPC:BE:32:e500:default for a plain EM_PPC
header, which misreads 750-family code. The lane binds
PowerPC:BE:32:default/default in the profile (language_selection
"admitted-lane-v1"), imports with it and checks the reported language. A
language set through REA_GHIDRA_LANGUAGE_ID still replaces the lane default.

Add a source-owned fixture, fixed-encoding probe, GNU readelf cross-check and
the optional `verify:ghidra:ppc` real-provider lane, plus docs/ghidra-ppc.md.

Refs #1741

* style(ghidra): format shared PowerPC profile integration

* fix(ghidra): preserve caller-selected PowerPC languages

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-14 06:35:55 +08:00
morluto 7e9fb6f1bb test(javascript): run native type bindings with compiled boundary prerequisites 2026-10-14 06:32:39 +08:00
morluto cd740a6764 test(hopper): fix fixture context and cleanup assumptions 2026-10-14 06:23:22 +08:00
morluto 22552525d5 fix(ci): preserve per-job findings and unavailable blob evidence 2026-10-14 06:23:22 +08:00
morluto 0161a593e5 style(hopper): format startup diagnostic expectations 2026-10-14 06:20:35 +08:00
morluto 70c238e376 test(ci): verify resolved cache inputs instead of glob spelling 2026-10-14 06:20:22 +08:00
morluto f0e86997aa test(providers): correct executable and retained-owner expectations 2026-10-14 06:20:22 +08:00
morluto 3ae02f6a45 fix(verification): parse browser endpoints before provider calls 2026-10-14 06:20:22 +08:00
morluto 4acda94b76 fix(hopper): recover provenance-verified Spotlight exits
Verify the native helper's sealed identifier and pinned Apple signing team in BridgeLauncher, then carry that provenance to HopperClient. Only a verified native helper's complete not-found exit confirms no document handoff, allowing runtime and target-lease cleanup and retry; wrappers retain unknown document state.

Share publisher identity with setup through the domain contract. Correct fixture teardown ordering and parameterized MCP coverage. Validation: 54 focused tests passed across boundary, CLI, MCP acceptance, diagnostics and installer coverage (one Linux-only case skipped); check:fast passed. Real codesign verification accepted the installed official Hopper helper; Spotlight failures and bridge recovery used process fixtures.
2026-10-14 06:19:47 +08:00
4d98123279 fix(hopper): recover from a Spotlight not-found launch (#2795)
* fix(hopper): recover from a Spotlight not-found launch

Hopper's native launcher locates the application through a fixed Spotlight
query before any Apple event. With indexing disabled it exits 3 with a
not-found report, but REA treated the external document as unconfirmed,
retained the runtime and failed every later open_binary until the server
restarted. Recognize the complete report as a pre-handoff failure, confirm
no document exists, and tell the caller to enable Spotlight indexing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(hopper): require empty stdout for native launch signatures

* fix(hopper): retain ownership after unverified Spotlight reports

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-14 06:08:31 +08:00
morluto 6510d0d57f perf(ci): start source feedback early and diagnose stalled runs (#3013)
* perf(ci): start source feedback early and diagnose stalled runs

* fix(ci): verify workflow identities before main comparison limits

* fix(ci): preserve diagnostics across passing checks and install failures

* fix(ci): preserve ambiguous workflow run ordering

* fix(ci): preserve failure-report candidate and request budgets

* fix(ci): retain candidate errors while checking later main evidence

* fix(ci): retain partial reports and require complete health scans

* test(ci): prune stale fixtures and duplicated assumptions
2026-10-14 06:07:08 +08:00
Jeremy Levartovskyandmorluto aa6f74af04 fix(setup): configure a comment-only Hermes config.yaml (#2096)
* fix(setup): configure a comment-only Hermes config.yaml

comments, or only a `---` marker, also parses to a null YAML document.
Setup still rejected it as malformed ("expected record, received null")
and reported Hermes as invalid.

Treat a null Hermes document as holding no settings, as #1983 does for
an empty file. The serializer already edits a comment-only document in
place. A bare `---` document holds a null scalar that takes no keys, so
replace it with a map and keep the comments written after the marker.

* fix(setup): keep rejecting an explicit null Hermes root

Only a document with no root, or a bare --- marker whose null scalar has
no source text, holds no settings. An explicit null or ~ is a value, so
setup and uninstall reject it as before, the same as any other non-map
root.

* test(setup): split the Hermes empty-configuration tests under the function length limit

The Hermes YAML registration block reached 159 lines, past oxlint's
max-lines-per-function limit of 150. Move the empty-document cases and
the multiple-document rejection into their own block.

* fix(setup): type the Hermes edit document as a plain Document

parseDocument with options returns a Document.Parsed, whose contents
accept only parsed nodes, so assigning the new map for a bare --- root
failed typecheck (TS2322). Annotate the edited document as Document, as
the code did before it moved into HermesConfigurationYaml.ts.

* fix(ci): isolate Linux Hopper process fixture inventory

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-14 05:48:05 +08:00
morconsultoriadigital-afkandClaude Opus 5 1342eddd03 test(analysis-view): order the message-cutoff fixture lexicographically (#3018)
boundedViewMessages returns uniqueSorted output, so the 100-message fixture
came back in lexicographic order ("message 59", "message 6", "message 60")
while the assertion compared against generation order. Pad the index to a
fixed width so both orders coincide and the test checks what it means to:
that no count-based cutoff applies below the byte budget.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-10-14 05:45:41 +08:00
14fef73882 docs(readme): restore Greek guide and official-source links (#3021)
* docs(readme): restore Greek guide and official-source links

Copy the canonical English official-source notice, Rust analysis row, and GNU
objdump/Rizin guide paragraph into the newly added Greek README. Restore the
documentation target set required by docs:check.

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>

* docs(i18n): translate restored Greek README guidance

---------

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-14 05:45:07 +08:00
06739aeaf2 fix(javascript): ignore erased TypeScript names in native-addon requests (#2766)
* fix(javascript): ignore erased TypeScript names in native-addon requests

Skip import type / export type declarations and per-binding type
specifiers when collecting .node addon members so only runtime value
imports request native exports (fixes #2667).

Co-authored-by: Cursor <cursoragent@cursor.com>

* test(javascript): cover erased TypeScript re-export forms

* test(javascript): allow Inspector observation events to arrive

* test(inspector): emit events before enable responses

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-14 05:39:09 +08:00
morluto bee73fcb67 docs(readme): move setup command above official-source notice 2026-10-14 05:27:03 +08:00
3620074b31 docs(android): link the JADX provider download
Document the pinned JADX release download and digest verification for npm users and repository checkouts. Keep caller-selected local paths; identify the fixture helper as checkout-only. Include shell and PowerShell examples, with the POSIX path set only after successful download and verification.

Validation: actual pinned JAR/APK fetch verification; real curl download and SHA-256 checks using sha256sum and the shasum fallback; controlled corrupt-file and failed-download cases; POSIX syntax; docs:check (5 conformance tests), typecheck/lint, formatting and diff checks. Provider execution and native macOS/Windows command runs were not repeated.

Co-authored-by: Seelly <93262000+Seelly@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
2026-10-14 05:25:39 +08:00
Pirro Zani ddcdb3c378 docs(i18n): add Greek translations (#2985)
* docs(i18n): add Greek translations

Give Greek-speaking readers localized onboarding guidance and begin website coverage while keeping incomplete pages on the explicit English fallback.

- Add the reviewed Greek README and place Greek last in every language selector.
- Register Greek website localization and include 206 translated messages.
- Keep incomplete routes nonindexable and replace fixed translation counts with inventory-based coverage.

Refs #2984

* docs(website): improve Greek homepage wording
2026-10-14 05:24:04 +08:00
4aa51bfe19 fix(reverse): reconcile cleanup ownership and mutation diagnostics (#2799)
* feat(process): add owned debugger shutdown lifecycle

* feat(reverse): add local inspection commands

* feat(gdb): add persistent debugger integration

* feat(cutter): add Cutter bridge integration

* test(reverse): enforce complete integration coverage

* fix(process): preserve debugger shutdown ownership

* fix(reverse): retain evidence across interrupted commands

* fix(gdb): honor captured provider environment

* test(process): cover debugger shutdown retries

* fix(reverse): retain evidence for interrupted providers

* test(process): cover debugger shutdown retries

* fix(reverse): retain evidence for interrupted providers

* fix(server): snapshot provider environment at creation

* fix(reverse): close review gaps with complete coverage

* fix(reverse): address inspection review findings

* fix(gdb): bound sessions and retain cleanup ownership

* fix(ci): require reverse-tools quality lane

* fix(process): unref inherited debugger output pipes

* fix(reverse): address remaining PR 1960 findings

* fix(gdb): address remaining PR 1961 findings

* fix(reverse): account for unrestricted provider execution

* fix(process): retain debugger ownership until safe shutdown

* fix(process): preserve replacement debugger sessions

* fix(reverse): preserve admission and output provenance

* fix(process): retain exited debugger until output settles

* fix(mcp): bound provider admission and debugger cleanup

* fix(reverse): preserve failure provenance and classify E2BIG

* fix(gdb): preserve startup cleanup and detach safety

* fix(process): retain debugger ownership after detach failure

* fix(reverse): preserve mutation and failure provenance

* fix(gdb): address debugger review findings

* fix(reverse): rebind sessions after external mutation

* fix(process): cap debugger output after exit

* fix(gdb): protect one-shot detach verification

* feat(debugger): add reverse tool coverage gate

* test(debugger): cover integrated failure paths

* fix(gdb): bound and preserve session observations

* fix(gdb): address review regressions

* style: format merged integration files

* fix(reverse): preserve session and output evidence

* refactor(gdb): share repeated test and partial result setup

* fix(gdb): invalidate TTY proof for Python aliases

* fix(reverse): retain failure output byte counts

* test(process): synchronize late debugger output

* fix(gdb): invalidate TTY proof for wrapped commands

* fix(gdb): recognize wit command abbreviation

* test(reverse): wait for cancelled descendant readiness

* test(reverse): bound descendant cancellation cleanup

* style(reverse): format integration tests and adapters

* style: format reverse engineering integration files

* test(reverse): remove duplicate environment harness

* test(gdb): share session startup assertions

* fix(gdb): invalidate output proof for command abbreviations

* test(reverse): share composition edge case scenarios

* fix(ci): recognize verification and mutation tool entries

* test(rizin): cover successful fake inferior detach

* test(cli): align full output failure envelope

* test(cli): isolate reverse command lifecycle service

* test(process): share debugger output fixture setup

* fix(binary): preserve high-bit Windows signature bytes

* style(binary): format Windows signature parser

* test(mcp): preserve call coverage in unchanged comparison

* fix(ci): include reverse tools Vitest configs in lint cache

* fix(mcp): preserve target content on delivery errors

* ci: align duplication ceiling with expanded suite

* test(reverse): allow signal admission mutants

* fix(reverse): bound retained cleanup failures to process owners

* fix(test): replace drifting mutation ranges with owned selectors

* fix(reverse): admit persistent commands and retain cleanup ownership

* fix(cutter): recover timed-out commands from exact receipts

* fix(cutter): reject unsupported bridge request kinds

* fix(ci): repair client format and shared verification fixtures

* fix(native): preserve debugger cleanup ownership and bridge authentication

* test(native): retain diagnostics without perfect coverage requirements

* chore(licenses): refresh locked dependency inventory and notices

* fix(test): exclude transient command locks from mutation sandboxes

* fix(gdb): admit only platforms with safe detach verification

* fix(cutter): retain bounded completion receipts until acknowledgement

* fix(cutter): acknowledge commands rejected before UI dispatch

* test(reverse): fail cancellation checks before fixture teardown

* test(gdb): clean up the retained broken-stdin fixture owner

* docs(test): clarify focused provider verification scope

* fix(rizin): retain close cleanup failures

* fix(gdb): wait for inferior stop and bound MI buffering

* fix(cutter): retain failed receipt acknowledgements

* fix(rizin): return retained session identity on rebind failure

* fix(gdb): preserve startup cancellation and lossy status

* fix(rizin): retain launch provenance and assemble frames once

* fix(cutter): recover receipts across callers and bound discovery

* docs: describe debugger evidence and bridge recovery

* fix(process): keep cleanup incomplete while writers remain

Stopping a debugger no longer reports success when inherited output
streams stay open, so BinarySession cannot reopen a binary a child is
still changing. Concurrent Cutter cleanup retries share one receipt
acknowledgement instead of treating a consumed receipt as failure.

* fix(debuggers): recover cleanup with captured process identities

* fix(analysis): retire sessions after unrestricted commands

* fix(cli): preserve completed evidence after cleanup recovery

* fix(cutter): bound frame collection and coalesce cleanup

* fix(windows): resolve selected provider paths absolutely

* fix(gdb): report decoding loss in failed startup evidence

* fix(rizin): scope decoding loss to collected command frames

* test(mcp): cover snapshot retirement in reverse tools CI

---------

Co-authored-by: Daniel Gonçalves Araujo <47118053+dandgabr@users.noreply.github.com>
Co-authored-by: REA Maintainers <noreply@github.com>
Co-authored-by: OpenAI Codex <codex@users.noreply.github.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2026-10-14 04:47:34 +08:00
morluto 2ad2a044d4 chore(package): add analysis target and provider keywords 2026-10-14 04:45:49 +08:00
morluto 49e73fc03b chore(package): replace disassembler keyword with rea 2026-10-14 04:37:46 +08:00
morluto 0bf01dfa59 perf(ci): share trusted caches and fail fast on PR test failures (#3007)
* fix(ci): restore inherited validation failures

* perf(ci): share trusted caches and fail fast on PR test failures

* fix(ci): bound static cache and hash vitest config for knip

- Bound the rolling .turbo/cache snapshot with 7d/500MB eviction so each
  trusted main save cannot accumulate history toward the GitHub limit.
- Hash vitest.config.ts in knip inputs so removing the CiTestSequencer
  import invalidates the cached passing check.
- Cover both in cache-policy and task-hash regressions.
2026-10-14 04:25:57 +08:00
Quentin Limoandme 9630c8ff46 fix(adb): remove false exclusivity from the push-file description (#2990)
push_adb_file claimed to be the only device-mutating ADB tool while
install, uninstall, start-app, start-activity and stop-app are also
device-mutating. Drop the exclusivity claim from the contract
description and the push limitations, keeping the device-mutation fact.

Fixes #2602.

Co-authored-by: me <me@medz.org>
2026-10-14 04:00:39 +08:00
Nianze Wu dd36b4a8a0 feat(analysis-view): report observed query parameter names (#2982) 2026-10-14 04:00:21 +08:00
87a8c8c923 test(signals): verify native decoder discovery coverage (#2818)
* test(signals): verify native decoder discovery coverage

Compare catalog and help pages against independent native sigrok output,
including full coverage counts, empty matches and boundary offsets.

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>

* style(test): format the JVM class fixture

* fix(comparison): recheck historical calls-only changes (#2223)

* fix(comparison): preserve incomplete function call evidence

Keep missing, unknown, partial and unresolved call relationships unknown in function comparisons and reconstruction claims. Aggregate IDA caller and callee coverage without overwriting uncertainty.

Validate with 144 focused tests, typecheck/lint, docs checks and test discovery. Real providers were not verified; the full headless IDA session suite requires unavailable Windows native controls.

* fix(comparison): recheck historical calls-only changes

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>

* fix(ci): isolate Linux Hopper process fixture inventory

---------

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
Co-authored-by: Lucien <164857476+dakjdakd@users.noreply.github.com>
2026-10-14 03:54:14 +08:00
3cb9d61ec1 fix(artifacts): describe ZIP non-entry coverage accurately (#2059)
* fix(artifacts): describe ZIP non-entry coverage accurately

* fix(process): report no remaining time after a provider deadline fires

Node timers can fire while performance.now() is still a fraction of a
millisecond short of the monotonic deadline. ProviderStartupDeadline then
reported interruption "timeout" and an aborted signal while remainingMs()
returned ceil(0.x) = 1, so polling loops guarded by remainingMs() > 0
(Hopper and Ghidra startup) could run past an elapsed deadline, and the
real-timer test "aborts a bounded wait when its real monotonic deadline
expires" failed intermittently in CI (expected 1 to be 0).

Once the deadline's own timer has classified a timeout, report zero
remaining time. A deterministic regression test fires the timer with the
monotonic clock 0.4 ms short of the deadline.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* style: format comparison and IDA sources

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 03:37:35 +08:00
2320ac23ee test(process): preserve terminal deadline expiry regression (#2055)
* fix(cli): preserve import-map option descriptions

* fix(process): report no remaining time after a provider deadline fires

Node timers can fire while performance.now() is still a fraction of a
millisecond short of the monotonic deadline. ProviderStartupDeadline then
reported interruption "timeout" and an aborted signal while remainingMs()
returned ceil(0.x) = 1, so polling loops guarded by remainingMs() > 0
(Hopper and Ghidra startup) could run past an elapsed deadline, and the
real-timer test "aborts a bounded wait when its real monotonic deadline
expires" failed intermittently in CI (expected 1 to be 0).

Once the deadline's own timer has classified a timeout, report zero
remaining time. A deterministic regression test fires the timer with the
monotonic clock 0.4 ms short of the deadline.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(process): keep terminal deadline expiry sticky

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 03:29:34 +08:00
AurantiumandAurantium-47 4c83846947 docs: identify official REA installation sources (#2429)
* docs: identify official REA installation sources

Assisted-by: OpenAI GPT-6 <noreply@openai.com>

* docs: show official sources before setup shortcut

Move the unchanged official-source notice before the first setup command in all 19 README editions.

Assisted-by: OpenAI GPT-6 <noreply@openai.com>

---------

Co-authored-by: Aurantium-47 <280805072+Aurantium-47@users.noreply.github.com>
2026-10-14 03:23:30 +08:00
cddf31d55e fix(wasm): preserve original glue source locations (#2114)
* fix(wasm): preserve original glue source locations

* fix(wasm): retain BOM-prefixed hashbang glue references

* test(wasm): parse the artifact contract before reading glue evidence

* test(wasm): give glue fixtures and assertions a single owner

* fix(comparison): recheck historical calls-only changes (#2223)

* fix(comparison): preserve incomplete function call evidence

Keep missing, unknown, partial and unresolved call relationships unknown in function comparisons and reconstruction claims. Aggregate IDA caller and callee coverage without overwriting uncertainty.

Validate with 144 focused tests, typecheck/lint, docs checks and test discovery. Real providers were not verified; the full headless IDA session suite requires unavailable Windows native controls.

* fix(comparison): recheck historical calls-only changes

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
(cherry picked from commit 688defca0f)

* fix(ci): isolate Linux Hopper process fixture inventory

(cherry picked from commit 8cac51e121)

* fix(android): make DEX descriptor pattern portable

(cherry picked from commit 1335bcddc5)

* fix: repair the static-check failures on the default branch (#2895)

* fix: repair the static-check failures on the default branch

The default branch fails its own required `Static checks` job three ways, so
every pull request reports that job as failed regardless of its own change.

- `npm run format:check` reports `src/domain/functionComparison.test.ts` and
  `src/ida/IdaOperationRunner.ts`.
- `npm run lint` reports a duplicate `import { join } from "node:path"` in
  `tests/boundary/mcp/ghidraInventoryMcp.test.ts` (lines 4 and 17).
- `npm run typecheck` reports `format: "devin"` missing from the client format
  union in `src/application/SupportedClients.ts`, and an unchecked index in
  `tests/boundary/cli/clientProfileLifecycle.test.ts`, whose pair array is
  inferred as `string[][]`.

Apply the pinned formatter, drop the duplicate import, list the `"devin"` format
that its own definition already uses, and mark the pair array `as const`.

Validation: `npm run check:fast` and `npm run format:check` pass.

* fix(setup): keep Devin on the JSON configuration dialect

---------

Co-authored-by: 吴佳浩 <wujiahao@supremind.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
(cherry picked from commit df3d96da80)

---------

Co-authored-by: morluto <morluto@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
Co-authored-by: Lucien <164857476+dakjdakd@users.noreply.github.com>
Co-authored-by: 吴佳浩Alben <35291372+XiaoBinGan@users.noreply.github.com>
Co-authored-by: 吴佳浩 <wujiahao@supremind.com>
2026-10-14 03:20:48 +08:00
CindyGestandmorluto e1897c3c17 fix(verify-package): read Hopper installer applicability from its owner (#2973)
* fix(verify-package): read Hopper installer applicability from its owner

packageSetupSupport derived hopperSetupSupported from the general doctor
host check plus a non-Windows condition, so a Debian-family host REA
supports but the Hopper package never tested reported an automated
installer the packaged setup never offers. verifySetupApply then expected
needs_confirmation for the explicit --install-hopper request and failed
at verify-package-setup.mjs:403.

Applicability now comes from readLinuxDistribution() in the built
application, so the verifier follows the predicate the package ships, and
it stays unknown when no distribution can be read instead of collapsing
into a known-unsupported host. Generic agent configuration and idempotence
assertions stay independent of installer applicability, and the expected
status is still derived from the host rather than from the request being
asserted.

Verified on Kali 2026.3, a Debian-family host with an installer-untested
distribution and the simulated Hopper package environment: before the
change npm run verify:package fails at verify-package-setup.mjs:403 with
the packaged setup correctly returning needs_human; after it the same run
passes every stage. Both applicability branches are covered by
tests/evaluation/package/packageSetupSupport.test.ts, which runs in the
source lane without a build. No real Hopper installation was exercised,
so no engine compatibility claim follows.

Refs #2939

* fix(ci): preserve Hopper doctor readiness checks

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-14 03:19:45 +08:00
LouisChenandLouisChen ed24604ee1 fix(javascript): preserve empty ESM import names in provenance (#2797)
Co-authored-by: LouisChen <3361008+chqcjq@users.noreply.github.com>
2026-10-14 03:12:04 +08:00
20b802f165 fix(analysis-view): bound limitations and unknowns per view (#2794)
* fix(analysis-view): bound limitations and unknowns per view

Views of a JavaScript application or binary layout copied every parent
limitation into both limitations and unknowns, so a parent with tens of
thousands of limitations produced views as large as the complete record
and exceeded the MCP response budget even for a one-item page. Each view
now carries at most 64 distinct messages per field plus one line naming
the omitted count and the parent Evidence that retains the full list.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(analysis-view): assert exact bounded message notices

Source inspection and targeted formatting only. Tests, builds, lint, typechecks and provider workloads were not run at the user's explicit request.

* fix(analysis-view): preserve coverage facts in bounded messages

Reserve JavaScript completeness and unknown-count facts before bulk limitations. Direct omission recovery to the source parent without assuming session retention. Preserve exact threshold assertions and add unrun exhausted-page/item and unretained-inline MCP regressions. Source inspection and targeted formatting only; tests/checks/builds/provider workloads/CI reruns skipped by explicit user request.

* fix(analysis-view): bound repeated message serialization bytes

* fix(analysis-view): let byte budget bound messages

* test(analysis-view): exercise byte-based message omissions

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-10-14 03:11:48 +08:00
23dc8fbf83 fix(application): accept alternative document selection in binary_ove… (#2680)
* fix(application): accept alternative document selection in binary_overview (#2460)

Update binary_overview capability negotiation so that either current_document or list_documents satisfies the document identity requirement, matching resolveAnalysisDocument runtime semantics. When a provider declares neither document capability, resolveAnalysisDocument reports AnalysisCapabilityUnavailableError for current_document rather than leaking the internal list_documents fallback. Preserve list_segments as an independently mandatory requirement.

* fix(application): retain document fallback provider errors

Return the fallback producer error unchanged, retaining operation identity, retry advice, diagnostics and cleanup instead of fabricating missing capabilities.

Source inspection and git diff --check; tests/builds/typechecks/CI reruns skipped at explicit user request.

* test(application): retain document fallback error expectations

Composition and MCP controls now expect the original list_documents producer error and reason.

Source inspection and git diff --check only. Tests, builds and typechecks skipped by explicit user request. GitHub review writes paused globally.

* fix(ida): assert attached-mode routing failure

---------

Co-authored-by: yashas1624 <yashas1624@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-14 03:11:02 +08:00
gongfppandgongfpp 0c6c571a01 perf(javascript): reuse semantic member-mutation lookups (#2941)
The semantic member-mutation collector revisits the same nodes while it closes
alias chains, and each visit re-derived results that cannot change within the
phase:

- resolveSemanticBindingState re-walked the scope chain for the same identifier
  node. Definition collection completes before this phase, and addBinding is
  reachable only from it, so a scope's bindings no longer change.
- directInitializerPosition, directMutationPosition and
  referenceInitializerPosition are pure functions of a node and the analysis
  parent map, but were recomputed for every mutation and initializer.
- markValue re-encoded the same capture tuple with JSON.stringify and built a
  fresh long key for every Set/Map lookup, so each lookup re-hashed the whole
  key. The capture key is now cached per binding, origin and owner, and the
  lifetime key is derived from it.

On pdfjs-dist 5.3.93 wasm/openjpeg_nowasm_fallback.js (462316 bytes, sha256
53c228428a8861035a34738966af263fc9d4cee6df25a0ad942af947469a0bd9) the semantic
phase takes 177.8 s instead of 272.9 s in-process, with a byte-identical
semantic IR. With --max-heap-mb 3072 and the default heap the base revision
exhausts the worker; with the default 300 s per-source deadline raised, the
completed CLI run takes 240.6 s instead of 396.7 s and its output is identical
apart from volatile identifiers.

Refs #2498

Co-authored-by: gongfpp <22683417+gongfpp@users.noreply.github.com>
2026-10-14 03:09:34 +08:00
Devulapalli Naga Sri Vaishnavi a9195c3a98 fix(managed): retain raw CIL changes in member comparison (#2912) 2026-10-14 03:07:12 +08:00
CodeAlexandREA Maintenance 4758ba30b6 fix(package-verifier): reject overview responses without a procedure count (#2936)
* fix(package-verifier): reject overview responses without a procedure count

The analyze and inspect checks compared procedure_count with `< 1`, and an absent
or null normalized_result makes that comparison false, so a binary_overview
response without its required result passed the platform smoke check. Require an
integer procedure count before the positive-count comparison.

* test(package-verifier): cover malformed overview receipts

---------

Co-authored-by: REA Maintenance <76467478+morluto@users.noreply.github.com>
2026-10-14 03:06:53 +08:00
Aaditya 64041e3072 fix(session): reject an unknown configured provider before unbound routing (#2976)
When every registered deep provider rejected a target, select() returned
an intentionally unbound success for any concrete environment selector,
including one that names no registered provider. A misconfigured
REA_ANALYSIS_PROVIDER therefore opened the target unbound, and the later
deep-operation diagnostic blamed target support instead of the unknown
configuration. The same identifier requested explicitly already failed
with unknown_provider.

Take the unbound shortcut only for a registered environment preference.
An unknown configured identifier now reaches concrete selection and fails
with unknown_provider, matching the explicit request. Registered
preferences and auto still leave artifact-only targets unbound.

Fixes #2592
2026-10-14 03:05:57 +08:00
39f96d7188 fix(qlog): list a hidden stream's records without spreading call arguments (#2860)
* fix(qlog): list a hidden stream's records without spreading call arguments

When an explicit declaration matched a stream's derived total, the
decoder pushed every contribution ordinal with a spread call. A stream
with more than ~125k STREAM frames exceeded the engine's argument limit,
so a valid capture within the input budget failed with RangeError
instead of listing the records as incomplete. Push the ordinals in a
loop.

* fix(comparison): recheck historical calls-only changes (#2223)

* fix(comparison): preserve incomplete function call evidence

Keep missing, unknown, partial and unresolved call relationships unknown in function comparisons and reconstruction claims. Aggregate IDA caller and callee coverage without overwriting uncertainty.

Validate with 144 focused tests, typecheck/lint, docs checks and test discovery. Real providers were not verified; the full headless IDA session suite requires unavailable Windows native controls.

* fix(comparison): recheck historical calls-only changes

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>

* fix(ci): isolate Linux Hopper process fixture inventory

* fix(android): make DEX descriptor pattern portable

---------

Co-authored-by: Lucien <164857476+dakjdakd@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-14 03:05:34 +08:00
xingzhou 02095cd74a fix(browser): report complete storage key inventory changes (#2861)
* fix(browser): report complete storage key inventory changes

* fix(browser): preserve unavailable storage inventory coverage

Reuse the key-coverage propagation and independent fingerprint mapping proposed by chAng-L19 in morluto/rea#2491. Propagate failed or malformed inventories into the canonical completeness ledger before comparing membership; preserve independently proven value changes. Add producer-to-comparison regressions and document recapture requirements.
2026-10-14 03:02:57 +08:00
26525706fc feat(managed): decode ldstr user strings in member inspection (#2162)
* feat(managed): decode ldstr user strings in member inspection

inspect_managed_members reported every ldstr operand as a bare 0x70 token,
so an analyst could see that a method loads a literal but not which one.
Answering "where is this path or key used" required a second tool.

Read the optional #US stream and add a top-level user_strings inventory:
each distinct token loaded by an inspected method body, once, in token
order, with its #US file offset and decoded UTF-16 value. Instruction
anchors and normalized CIL digests keep their build-local tokens. A missing
#US stream or malformed item is reported against its token as partial
coverage. Evidence recorded before this field reads user_strings as null.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(managed): collect user strings only from ldstr

A malformed ldtoken can carry a 0x70 token, which the operand decoder
classifies as a string operand. Only ldstr loads a literal, so collect
user strings by opcode.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(managed): admit user-string tokens and failures before collecting them

Charge each distinct ldstr token to the decode budget before adding it to
the collection set, and admit each #US failure before retaining it. A
missing #US stream is now one stream-level issue rather than one per
token.

Document why the #US terminal flag is not validated: Mono and Roslyn
assemblies disagree with ECMA-335 II.24.2.4 and the runtime ignores it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(managed): describe missing #US reporting and the unvalidated flag byte

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(managed): cover #US items that run past the stream end

An odd length whose payload crosses the end of #US, and a two-byte
length prefix that starts on the last #US byte, are both rejected as
"User string leaves #US" beside a readable literal. The test fails if
that bounds check is removed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(managed): report reserved #US length prefixes as string failures

The compressed-length reader shared by #Blob and #US hard-coded
invalid-blob and "Metadata blob" for a reserved prefix, so a malformed
user string was published as a blob failure. The caller now names the
item it reads: #Blob keeps its code and message, and #US reports
invalid-string.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(managed): append member inspection issues without argument spreading

inspectManagedMembersBytes gathered every producer's issues with
issues.push(...list), passing one argument per issue. A large malformed
input, such as hundreds of thousands of distinct bad ldstr operands or
MemberRef rows, overflows the stack with an untyped RangeError instead of
returning partial coverage. All six sites now use
appendManagedParseIssues, which appends one issue at a time.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(managed): classify truncated #US and #Blob length prefixes by item

A two- or four-byte compressed-length prefix cut off by the end of the
artifact went through the generic range check and was reported as
invalid-row. The prefix reader now uses the item's own code for both
reserved and truncated prefixes: invalid-string for #US, invalid-blob for
#Blob. The unused generic range helper is removed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(managed): retain user-string terminal flag conformance issues

* test(managed): write ECMA-335 #US terminal flags and cover flag issues

d5b7b0704 reports a #US terminal flag that differs from ECMA-335
II.24.2.4. The fixture heap always wrote 0, so its non-Latin-1 literals
now read as nonconforming. The fixture writes the spec flag, and a new
test flips one flag each way ("ok" set to 1, "it's" set to 0) and checks
that both literals remain beside one issue each.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(managed): classify ldstr operands before collapsing a missing #US

With no #US stream, every collected ldstr operand was counted as a #US
reference and reported only as missing-stream, while the same non-0x70
operand got invalid-heap-index when the stream existed. parseUserStrings
now owns which operands are user-string tokens: any other operand is
invalid-heap-index either way, and only 0x70 tokens count toward the
missing-stream issue. The heap reader drops its duplicate kind check.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(managed): state user-string limits in member comparisons

compare_managed_members ignored user_strings, so a method whose ldstr
literal changed at the same token, or legacy Evidence that never examined
literals, could read as unchanged with no caveat. The comparison now adds
an unknown limitation when either side predates user strings and a
not-compared limitation when literals were observed. ExportedType and
user-string inventories share one uncomparedInventory rule.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(managed): advertise ldstr literals in member inspection help

The MCP tool description, the type_names input, and the CLI command and
--type help now say that inspect_managed_members returns the #US string
literals that ldstr loads, and that a type selection limits them to the
literals the selected types' method bodies load.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(managed): read the empty #US entry at offset 0

ECMA-335 II.24.2.4 makes the first #US entry the empty blob: a zero
length with no terminal flag byte. ldstr 0x70000000 loads it, but the
odd-length check rejected it as invalid-string, dropping the literal and
making coverage partial. Offset 0 with length 0 now reads as "".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(managed): reserve #US offset 0 and admit literals before scanning

Offset 0 is the reserved empty #US entry, so a nonempty item there is now
invalid-string instead of a published literal. The literal's projection
is reserved before the terminal-flag scan, so an inadmissible item fails
at the budget without reading every code unit. The scan's loop index no
longer shadows the token's heap index.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(managed): report #US length prefixes wider than ECMA-335 assigns

compressedLength accepted an overlong encoding, such as 0x80 0x05 for a
5-byte item, without comment. It now reports whether the width is the
one ECMA-335 II.23.2 assigns, and the #US reader reports a non-minimal
prefix as an invalid-string conformance issue beside the readable
literal, like the terminal flag. readMetadataUserString returns every
issue it observes. #Blob behavior is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(managed): check the #US length width before the empty entry

The offset-0 empty entry returned "" before the length-width check, so an
overlong empty entry such as 0x80 0x00 read as complete. The width issue
is now collected first and returned with the empty literal.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(managed): admit #US width issues only for returned literals

The overlong-prefix issue was reserved before the fatal shape checks, so
an item that then failed, such as 0x80 0x00 away from offset 0, leaked
512 bytes of budget per token. The width issue is now built and admitted
only on paths that return the literal.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(managed): keep #US issues out of member metadata status

User-string issues, including terminal-flag and length-prefix
conformance differences, marked metadata.status partial, so
compare_managed_members turned one-sided methods and fields into unknown
even though every member row was examined. #US issues now make coverage
partial while metadata.status describes the member inventory.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(managed): give user strings their own coverage

#US issues entered the member coverage, so every consumer that reads it
as member coverage (application graph member facts, comparison status)
reported examined members as partial over a literal conformance
difference. user_strings is now { state, literals, issues }: #US issues
live there and never change the member coverage or metadata.status.
parseUserStrings returns that inventory directly.

compare_managed_members inspects with user strings skipped, since it
never reads literals, so a large literal cannot exhaust the decode budget
of a comparison. Its limitations state once that literals are not
compared, and the ExportedType limitations return to their main form.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(managed): mark user strings partial when a body is not fully decoded

With user strings owning their coverage, an inspected method body that
was partial, malformed, or truncated no longer reached their state, so a
literal after the decode stopped was missing from a "complete" inventory.
parseUserStrings now marks the inventory partial unless every inspected
body was fully decoded, keeping the literals it did observe.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(managed): mark user strings partial when a method row is omitted

A MethodDef row that failed before its body was read was recorded as an
issue and omitted, so its ldstr instructions were never scanned, yet the
user-string inventory could still read as complete. parseUserStrings now
takes the parseMethods result and stays partial whenever a method row
failed, as well as when an inspected body was not fully decoded.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(managed): count only method rows that may hide a selected body

Any parseMethods issue marked user strings partial, including an
unreadable name on a method outside the type selection, so a fully
scanned selection read as incomplete. parseMethods now reads each row's
owner first and reports unscannedRows: rows that produced no method while
their owner is selected or unknown. parseUserStrings uses that count.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(managed): treat unreadably named types as possibly selected

Under a type_names selection, a type whose name could not be read may be
a requested type, but its methods were skipped without counting toward
unscannedRows, so an unexamined body left user strings complete. A
method whose owner's name is unreadable now counts as possibly selected.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(managed): judge user-string scans by CIL decoding, not body status

body.status is malformed for an invalid exception section even when
every instruction decoded, so user strings read as partial although every
ldstr was examined. The body reader now reports whether its CIL was fully
scanned, independent of exception-region validity, and parseMethods folds
unscanned bodies and unproduced rows into one unscannedMethods count that
parseUserStrings uses instead of reinterpreting body.status.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(managed): stop deduplicating user-string issues

The inventory passed its issues through uniqueManagedParseIssues, which
serializes every issue into an unbudgeted map: about 42 MB of heap and
113 ms for 125,000 issues on Node 24. The issues are unique by
construction (one failure or per-offset conformance issues per distinct
token, one missing-stream issue), so the inventory now returns them, and
its literals, without copies.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(managed): preserve selected string coverage unknowns

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-14 03:01:23 +08:00
45a3509db3 fix(artifacts): compare inventories using declared coverage (#2214)
* fix(artifacts): preserve complete root inventory comparisons

* fix(artifacts): declare inventory coverage before comparison

* fix(artifacts): preserve traversal omissions and materialization limits

* fix(artifacts): preserve coverage declarations and projection scope

Remove the duplicate inventory-schema import. Compare original coverage declarations, including field omission, before adapting legacy pages. Require explicit scope for completeness and container-entry coverage for application projections.

Source inspection and git diff --check only. Tests, builds and typechecks skipped by explicit user request. GitHub review writes paused globally.

* fix(artifacts): require entry coverage for child changes

* fix(artifacts): accept source-valid inventory limitations

* style(artifacts): format mobile graph projection

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
Co-authored-by: OpenAI Codex <openai-codex@users.noreply.github.com>
2026-10-14 03:00:27 +08:00
e4186af31b fix(process): expose Darwin swiftc compile deadline via env (#2240)
* fix(process): expose Darwin swiftc compile deadline via env

Busy hosts can exceed the hard-coded 60s ownership-helper compile
timeout. Honor REA_SWIFTC_COMPILE_TIMEOUT_MS like the other provider
startup deadlines, keeping 60000 as the default.

Fixes #2184

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(process): keep the Swift compile deadline with its runtime owner

* fix(comparison): recheck historical calls-only changes (#2223)

* fix(comparison): preserve incomplete function call evidence

Keep missing, unknown, partial and unresolved call relationships unknown in function comparisons and reconstruction claims. Aggregate IDA caller and callee coverage without overwriting uncertainty.

Validate with 144 focused tests, typecheck/lint, docs checks and test discovery. Real providers were not verified; the full headless IDA session suite requires unavailable Windows native controls.

* fix(comparison): recheck historical calls-only changes

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
(cherry picked from commit 688defca0f)

* fix(ci): isolate Linux Hopper process fixture inventory

(cherry picked from commit 8cac51e121)

* fix(android): make DEX descriptor pattern portable

(cherry picked from commit 1335bcddc5)

* fix: repair the static-check failures on the default branch (#2895)

* fix: repair the static-check failures on the default branch

The default branch fails its own required `Static checks` job three ways, so
every pull request reports that job as failed regardless of its own change.

- `npm run format:check` reports `src/domain/functionComparison.test.ts` and
  `src/ida/IdaOperationRunner.ts`.
- `npm run lint` reports a duplicate `import { join } from "node:path"` in
  `tests/boundary/mcp/ghidraInventoryMcp.test.ts` (lines 4 and 17).
- `npm run typecheck` reports `format: "devin"` missing from the client format
  union in `src/application/SupportedClients.ts`, and an unchecked index in
  `tests/boundary/cli/clientProfileLifecycle.test.ts`, whose pair array is
  inferred as `string[][]`.

Apply the pinned formatter, drop the duplicate import, list the `"devin"` format
that its own definition already uses, and mark the pair array `as const`.

Validation: `npm run check:fast` and `npm run format:check` pass.

* fix(setup): keep Devin on the JSON configuration dialect

---------

Co-authored-by: 吴佳浩 <wujiahao@supremind.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
(cherry picked from commit df3d96da80)

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
Co-authored-by: morluto <17176810+morluto@users.noreply.github.com>
Co-authored-by: Lucien <164857476+dakjdakd@users.noreply.github.com>
Co-authored-by: 吴佳浩Alben <35291372+XiaoBinGan@users.noreply.github.com>
Co-authored-by: 吴佳浩 <wujiahao@supremind.com>
2026-10-14 02:59:54 +08:00
Jeremy Levartovskyandmorluto 42667fc1a0 fix(wasm): report WABT output past its budget as a resource constraint (#2117)
* fix(wasm): report WABT output past its budget as a resource constraint

A module whose WAT exceeds the 32 MiB output budget failed as
unreadable_output, advised `rea doctor`, and echoed the full 32 MiB
truncated WAT prefix in details.captured_output. Map the owned-command
output-limit outcome during inspection to AnalysisResourceConstraintError
naming the budget, as the apktool and native macOS providers do, with a
remediation that says the fixed budget will not change on retry.

Captured output for an output-limit failure keeps only the first 4,096
characters of each stream, including on the cleanup-failure path. An
oversized --version probe keeps its configuration-phase mapping.

* fix(wasm): keep observed sizes and whole code points in output-limit excerpts

Cut each stream's output-limit excerpt with boundedUtf8Text at 4,096
UTF-8 bytes, so an astral character at the cut no longer leaves a lone
surrogate. Mark the output truncated when either excerpt was cut, and
report each stream's observed byte count.

Word the remediation as a fact about the fixed budget. A flood of
diagnostics from any WABT tool reaches this path, so it no longer says
the module is too large.

Replace the 32 MiB end-to-end test with a launcher seam that raises a
small output-limit failure.

* fix(wasm): match sibling captured-output fields for WABT output limits

Report retained byte counts in stdout_bytes and stderr_bytes, as apktool
and the firmware command do, and record exit_code and signal. Mark the
excerpt truncated outright, since the command raises output-limit only
after retention was truncated.

When cleanup also fails, record the output limit and its budget as the
previous error instead of the generic command message. Document that
the two streams share the budget, so the excerpt holds up to 4,096 of
the bytes each stream retained.

* docs(wasm): scope the output-budget error to inspection commands

* docs(wasm): distinguish reported warm costs from unmeasured gaps

* docs(wasm): clarify the scope of reported warm measurements

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-14 02:58:41 +08:00
729ca2ab6a fix(ghidra): report unapplied PE CodeView PDB limitation (#2283)
* fix(ghidra): report unapplied PE CodeView PDB limitation (#2259)

When Ghidra analyzes a PE target that references companion debug symbols
in its CodeView (IMAGE_DEBUG_TYPE_CODEVIEW / RSDS or NB10) debug directory,
Ghidra's headless analyzer cannot automatically locate or apply the companion
PDB from isolated runtime directories without companion admission. Procedure
names remain analysis-generated (FUN_<address>).

Parse CodeView debug metadata (RSDS GUID/age and NB10 age) during PE target
resolution, and report an Evidence limitation informing callers and agents
that referenced debug symbols were not applied and procedure names are
analysis-generated.

Fixes #2259

* test(pe): group CodeView header coverage separately

* fix(ghidra): avoid contradictory PDB limitations

* fix(android): make DEX descriptor pattern portable

* fix(ci): isolate Linux Hopper process fixture inventory

* fix(comparison): recheck historical calls-only changes (#2223)

* fix(comparison): preserve incomplete function call evidence

Keep missing, unknown, partial and unresolved call relationships unknown in function comparisons and reconstruction claims. Aggregate IDA caller and callee coverage without overwriting uncertainty.

Validate with 144 focused tests, typecheck/lint, docs checks and test discovery. Real providers were not verified; the full headless IDA session suite requires unavailable Windows native controls.

* fix(comparison): recheck historical calls-only changes

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>

* style: format identity comparison and IDA runner

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
Co-authored-by: Lucien <164857476+dakjdakd@users.noreply.github.com>
2026-10-14 02:56:26 +08:00
4481f86188 fix(frida): drain admitted operations and honor queued cancellation (#2305)
* fix(frida): drain admitted operations and honor queued cancellation

* fix(frida): settle cancelled loads before queued resume

* test(frida): share lifecycle fixtures without duplicating assertions

* fix(frida): preserve operation admission before session close

* test(apple): bound cold archive fixture compilation

* fix(frida): retain cancelled script creation evidence

* test(release): scope preflight schema to selected jobs

* fix(process): report no remaining time after a provider deadline fires

Node timers can fire while performance.now() is still a fraction of a
millisecond short of the monotonic deadline. ProviderStartupDeadline then
reported interruption "timeout" and an aborted signal while remainingMs()
returned ceil(0.x) = 1, so polling loops guarded by remainingMs() > 0
(Hopper and Ghidra startup) could run past an elapsed deadline, and the
real-timer test "aborts a bounded wait when its real monotonic deadline
expires" failed intermittently in CI (expected 1 to be 0).

Once the deadline's own timer has classified a timeout, report zero
remaining time. A deterministic regression test fires the timer with the
monotonic clock 0.4 ms short of the deadline.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(comparison): recheck historical calls-only changes (#2223)

* fix(comparison): preserve incomplete function call evidence

Keep missing, unknown, partial and unresolved call relationships unknown in function comparisons and reconstruction claims. Aggregate IDA caller and callee coverage without overwriting uncertainty.

Validate with 144 focused tests, typecheck/lint, docs checks and test discovery. Real providers were not verified; the full headless IDA session suite requires unavailable Windows native controls.

* fix(comparison): recheck historical calls-only changes

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
(cherry picked from commit 688defca0f)

* fix(ci): isolate Linux Hopper process fixture inventory

(cherry picked from commit 8cac51e121)

* fix(android): make DEX descriptor pattern portable

(cherry picked from commit 1335bcddc5)

* fix: repair the static-check failures on the default branch (#2895)

* fix: repair the static-check failures on the default branch

The default branch fails its own required `Static checks` job three ways, so
every pull request reports that job as failed regardless of its own change.

- `npm run format:check` reports `src/domain/functionComparison.test.ts` and
  `src/ida/IdaOperationRunner.ts`.
- `npm run lint` reports a duplicate `import { join } from "node:path"` in
  `tests/boundary/mcp/ghidraInventoryMcp.test.ts` (lines 4 and 17).
- `npm run typecheck` reports `format: "devin"` missing from the client format
  union in `src/application/SupportedClients.ts`, and an unchecked index in
  `tests/boundary/cli/clientProfileLifecycle.test.ts`, whose pair array is
  inferred as `string[][]`.

Apply the pinned formatter, drop the duplicate import, list the `"devin"` format
that its own definition already uses, and mark the pair array `as const`.

Validation: `npm run check:fast` and `npm run format:check` pass.

* fix(setup): keep Devin on the JSON configuration dialect

---------

Co-authored-by: 吴佳浩 <wujiahao@supremind.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
(cherry picked from commit df3d96da80)

---------

Co-authored-by: morluto <morluto@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
Co-authored-by: Lucien <164857476+dakjdakd@users.noreply.github.com>
Co-authored-by: 吴佳浩Alben <35291372+XiaoBinGan@users.noreply.github.com>
Co-authored-by: 吴佳浩 <wujiahao@supremind.com>
Co-authored-by: OpenAI Codex <codex@openai.com>
2026-10-14 02:56:00 +08:00
Jose Camargoandmorluto 72d5bbc96d fix(process): keep provider deadlines independent of wall-clock changes (#2311)
* fix(process): keep provider deadlines independent of wall-clock changes

* fix(process): retain monotonic targets after early timers

Adapt the stronger timer/poll owner from @nosmile99 PR #2793 as integrated by 21aa877e5 and captured main 94072364c7. Retain all contributor reliability coverage. Tests and workloads skipped by explicit request.

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-14 02:55:46 +08:00
Yunzhou Liandmorluto 97268f40ea fix(dotnet): name the truncated instruction when an operand leaves the method body (#2704)
* fix(dotnet): name the truncated instruction when an operand leaves the method body

The extent guard ran after the operand read, so a truncated operand threw a raw Buffer RangeError that replaced the intended diagnostic with the runtime message and lost the instruction identity.

Bound the operand extent before reading its value and carry the width beside each fixed reader so the two facts cannot drift.

Fixes #2677

* test(dotnet): consolidate truncated operand coverage

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-14 02:52:47 +08:00
Shenrui Ma 388b907c92 perf(javascript): defer ambiguity expansion in version comparisons (#2962)
perf(javascript): avoid quadratic temporary matching state

Repeated source groups can allocate candidates that later identity matches
discard. Retain the original ambiguous groups until all tiers finish, expand
only unmatched survivors, and append to locally owned key buckets in place.

Constraint: Preserve matching priority, full ambiguity, and public output.
Directive: Do not re-match residual groups that become unique after later tiers.
Tested: 59 relevant tests, npm run check, and test discovery on current main.
Tested: CLI benchmarks and full-suite failure comparison on the recorded base.
Not-tested: Full suite on the updated main; native Linux and Windows workflows.
2026-10-14 02:52:13 +08:00
0e6eca4085 feat(native): read embedded cargo-auditable Rust dependency lists (#2456)
* feat(native): read embedded cargo-auditable Rust dependency lists

* fix(native): trim PE section padding and compute dependency cycles iteratively

* fix(native): ignore SHT_NOBITS sections and validate dependency edges linearly

* fix(native): bound compiler path scanning and retained hashes

* fix(rust): pass package arrays as named parameterized test cases

* fix(rust): admit graph expansion and preserve resource units

* fix(rust): reject malformed packages, false cycles, and hidden compiler paths

Fail the first malformed cargo-auditable package before Zod collects an
issue per field. Require cyclic dependency indices to match the cycles in
package dependencies. Keep read-only ELF ranges when a section name does
not fit the bounded name window, so a rustc path in that range is still
reported.

* fix(ci): repair integrated checks and Hopper test baseline

* fix(go): verify Mach-O sections against segments

---------

Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2026-10-14 02:50:30 +08:00
c7ceaadb70 fix(browser): preserve unknown storage inventory and fingerprint coverage (#2491)
* fix(browser): preserve unknown storage inventory and fingerprint coverage

* fix(browser): compare known storage edits with incomplete inventories

* test(browser): consolidate storage completeness regression workflows

* test(ida): admit aligned FAT thinning fixtures on every platform

---------

Co-authored-by: chAngE <172472630+chAng-L19@users.noreply.github.com>
Co-authored-by: morluto <morluto@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-14 02:50:03 +08:00
saurabh sharmaandClaude Fable 5.1 090b454350 fix(javascript-semantics): keep emitted data out of dispatch listener fields (#2952)
* fix(javascript-semantics): keep emitted data out of dispatch listener fields

The EventEmitter operation collector read argument position 1 as the
listener for every event method, so a dispatch such as
bus.emit("ready", data) reported the data binding and its range as the
listener. Only registration and removal name a listener there; emit
passes its remaining arguments to listeners that already exist. Read the
listener argument only for register and remove operations.

Fixes #2574

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix(javascript-semantics): read listener arguments only for listener-taking methods

removeAllListeners accepts only an optional event name, so gating the
listener argument on operation kind still reported an ignored second
argument as a listener. Gate on the methods whose argument position 1
names a listener instead: the registration methods, off, and
removeListener.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-14 02:48:54 +08:00
Liu Jieandmaxazure 0bc422d7be fix(wasm): attribute invalid paths to their selected fields (#2796)
* fix(wasm): attribute invalid paths to their selected fields

* test(wasm): assert the exact rejected selection issues

---------

Co-authored-by: maxazure <405711+maxazure@users.noreply.github.com>
2026-10-14 02:39:03 +08:00
morluto 2895a18ca9 fix(ci): skip portable report merge when shards are skipped (#2943)
* fix(ci): skip portable report merge when shards are skipped

The portable consumer can still run after an upstream build failure, even though the test-shard matrix is skipped and no Vitest blob artifacts exist. Guard artifact download and both report merge variants on successful shards; retain the required-shard gate so skipped or failed shards remain failures.

This applies the report-consumer condition from ee0256c740 to current pinned main without changing action pins or required gates.

* fix(ci): preserve failed shard diagnostics

* docs: clarify current revision CI policy
2026-10-14 02:30:44 +08:00
morluto 7a6defab18 test(reconstruction): declare complete comparison fixture facets (#2964) 2026-10-14 02:17:57 +08:00
morluto 8fef2aa64e fix(ci): enforce merge verification and unblock superseded runs (#2938)
* fix(ci): repair inherited formatting failures

* fix(ci): release superseded PR concurrency slots

* docs: require current-base checks before maintainer merges

* docs: preserve administrator merge override
2026-10-14 01:52:57 +08:00
ebaaa79ca0 test(http3): add real MITM capture verification lab (#1997)
* test(http3): add real MITM capture verification lab

* test(http3): register the optional verification command

* fix(http3): preserve the latest verification attempt owner

* test(cli): admit the dyld slice fixture on x64 hosts

* test(http3): verify exact producer TLS connection facts

* test(http3): verify readable capture fields against observed bytes

* fix(comparison): recheck historical calls-only changes (#2223)

* fix(comparison): preserve incomplete function call evidence

Keep missing, unknown, partial and unresolved call relationships unknown in function comparisons and reconstruction claims. Aggregate IDA caller and callee coverage without overwriting uncertainty.

Validate with 144 focused tests, typecheck/lint, docs checks and test discovery. Real providers were not verified; the full headless IDA session suite requires unavailable Windows native controls.

* fix(comparison): recheck historical calls-only changes

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>

* fix(android): make DEX descriptor pattern portable

* fix(ci): isolate Linux Hopper process fixture inventory

* fix(http3): retain MCP startup diagnostics

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
Co-authored-by: Lucien <164857476+dakjdakd@users.noreply.github.com>
2026-10-14 01:09:19 +08:00
1d5f95dc4a fix(native): parse renamed dyld_info re-exports (#2364)
* fix(native): parse renamed dyld_info re-exports

dyld_info prints a re-export whose name differs from the source library's
symbol as `[re-export] _foo_renamed (_foo from libfoo)`. The export parser
only accepted `(from libfoo)`, so inspect_macho reported a nonexistent
export named `_foo_renamed (_foo from libfoo)` with no source, and the nm
row for `_foo_renamed` survived the name-based merge as a second export
with reexport null.

Accept the optional source symbol, trying the plain `(from lib)` form
first so a library name containing ` from ` keeps its text. The row now
reports `_foo_renamed` re-exported from libfoo and replaces the nm
duplicate. The export schema has no field for the source symbol name, so
exports coverage records a limitation when a renamed re-export is present.
The golden fixture is a dyld_info capture of a synthetic dylib linked with
-Wl,-alias,_foo,_foo_renamed.

* fix(native): split dyld re-export suffix without nested quantifiers

Replace the (?:(.+?)\s+)??from lazy-optional group with a linear string
scan so malformed lines cannot backtrack and a library path containing
" from " still keeps its whole text via the plain-form-first check.

* test(native): cover nested re-export library names

* fix(native): retain reexports missing from nm inventory

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
Co-authored-by: morluto <17176810+morluto@users.noreply.github.com>
2026-10-14 01:06:55 +08:00
Shubham AhujaandClaude Opus 5.5 f652709cac fix(cli): keep file URLs out of operator-relative path resolution (#2882)
CLI path normalization resolved every nonblank selected string against
the working directory, so executable_path "file:///opt/electron" became
an invented local path and failed with ENOENT for that location instead
of the field's local-path requirement. Leave `file:` and `scheme://`
references unchanged so the shared schema rejects them as non-local
paths. One-letter prefixes stay paths, so Windows drive paths and
relative names containing a colon resolve as before.

Fixes #2577

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 01:05:14 +08:00
Burak Kalaycı c8ef1f916c docs(readme): sync Turkish translation with current README (#2777)
docs(readme): sync Turkish translation
2026-10-14 01:04:55 +08:00
R f1045001f0 fix(browser): retain optional source-map requires (#2886)
Carry the require callee from dependency extraction into lexical scope
validation so optional calls remain visible without admitting shadowed
loaders.

Fixes #2883
2026-10-14 00:57:42 +08:00
morluto 69bd81f7f2 fix: isolate Linux inventory and repair DEX schema regex (#2910)
* fix(ci): isolate Linux Hopper process fixture inventory

* fix(android): make DEX descriptor pattern portable
2026-10-14 00:56:43 +08:00
Lin Ganandmorluto 1b6c899b03 docs: update Chinese README guides (#2119)
Update Chinese README docs

Refresh the Chinese README guidance to mention IDA as a supported native analysis backend, clarify that static JavaScript analysis does not require native engines, and document global CLI installation/update steps for Bun (`bun add --global`, `bunx` usage, minimum version 1.2.17). The capability table also now includes Go binary and WASM artifact support.

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-14 00:42:03 +08:00
To3akaRin 93e29d242a feat(cli): summarize committed reference-source inventories (#2768) 2026-10-14 00:37:54 +08:00
Praveen Yadavandmorluto c6631814e0 docs(website): document Apple Silicon Cairo setup for local preview (#1930)
* docs(website): document Apple Silicon Cairo setup for local preview

* docs(website): scope Apple Silicon Cairo fix to preview process

* docs(website): avoid shared-prefix rewrite and clobbering backup

* docs(website): keep shared Intel libs intact with same-shell Cairo lookup

* docs(website): enter ARM shell before exporting Cairo lookup

* docs(website): use ARM brew path and unprotected Python for Cairo

* docs(website): reinstall requirements after ARM venv rebuild

* docs(website): document DejaVu font dependency

* docs(website): show ARM Python selection for venv rebuild

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-14 00:37:39 +08:00
all-black-493 62c8b3da1b feat(browser): attach passive tools to browsers without HTTP discovery (#2346)
* feat(browser): attach passive tools to browsers without HTTP discovery

Accept a literal loopback browser WebSocket,
ws://127.0.0.1:PORT/devtools/browser/ID, as the cdp_endpoint of the six
passive browser tools. Discovery then reads Browser.getVersion and
Target.getTargets over that socket, and the target session adopts the
same socket, so a browser that asks for consent per connection prompts
once per call.

The socket identifier gates access, so Evidence records the endpoint as
ws://127.0.0.1:PORT/devtools/browser/[REDACTED], and runtime
reconciliation accepts that recorded form. HTTP endpoints and their
Evidence are unchanged. Electron, web runtime, scenario and Inspector
endpoints stay HTTP-only.

Refs #2288

* test(browser): match browser-socket captures against HTTP discovery

Check in the real Chrome lane that DevToolsActivePort names the same
browser socket as /json/version, and that inspecting the selected page
through that socket leaves DOM structure and scripts unchanged compared
with an inspection through HTTP discovery.

Refs #2288

* fix(browser): bound browser-socket handshake and report discovery timeouts

Run the browser-socket WebSocket handshake and both discovery commands
under one discovery deadline, so a port that accepts the connection but
never completes the upgrade times out instead of waiting forever.

Report a reached deadline as a timeout without carrying the internal
abort as its cause, so CLI and MCP project provider_timeout rather than
cancelled. Caller cancellation is still reported as cancelled.

Refs #2288
2026-10-14 00:36:37 +08:00
ShitK 0c9ee56c2f fix(native): accept declared facts in dispatch name inventory (#2906) 2026-10-14 00:34:06 +08:00
ShitK 501c5d45fe fix(managed): share native verification input diagnostics (#2901) 2026-10-14 00:33:14 +08:00
df3d96da80 fix: repair the static-check failures on the default branch (#2895)
* fix: repair the static-check failures on the default branch

The default branch fails its own required `Static checks` job three ways, so
every pull request reports that job as failed regardless of its own change.

- `npm run format:check` reports `src/domain/functionComparison.test.ts` and
  `src/ida/IdaOperationRunner.ts`.
- `npm run lint` reports a duplicate `import { join } from "node:path"` in
  `tests/boundary/mcp/ghidraInventoryMcp.test.ts` (lines 4 and 17).
- `npm run typecheck` reports `format: "devin"` missing from the client format
  union in `src/application/SupportedClients.ts`, and an unchecked index in
  `tests/boundary/cli/clientProfileLifecycle.test.ts`, whose pair array is
  inferred as `string[][]`.

Apply the pinned formatter, drop the duplicate import, list the `"devin"` format
that its own definition already uses, and mark the pair array `as const`.

Validation: `npm run check:fast` and `npm run format:check` pass.

* fix(setup): keep Devin on the JSON configuration dialect

---------

Co-authored-by: 吴佳浩 <wujiahao@supremind.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-14 00:31:27 +08:00
7e3a198638 fix(javascript): keep logical assignment results uncertain for unknown values (#2788)
* fix(javascript): keep logical assignment results uncertain for unknown values

* test(javascript): cover exact uninitialized logical assignments

Source inspection and targeted formatting only. Tests, builds, lint, typechecks and provider workloads were not run at the user's explicit request.

---------

Co-authored-by: Ganesh2325 <184471511+Ganesh2325@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-14 00:06:45 +08:00
hhangandhhang 72b9ab4af1 fix(javascript): retain configuration reads before member access (#2756)
Squash merge of https://github.com/morluto/rea/pull/2756.

Original pull request submitted by @xu91102.

Co-authored-by: hhang <169027046+xu91102@users.noreply.github.com>
2026-10-13 23:59:49 +08:00
d4f0c5e664 fix(cli): start on Windows when USERPROFILE is empty (#2345)
* fix(cli): start on Windows when USERPROFILE is empty

Node's Windows home lookup throws ERR_SYSTEM_ERROR (uv_os_homedir ENOENT)
for an empty USERPROFILE instead of consulting the account profile. Incur
resolves the home while it is imported, so every command, including
`rea --version`, stopped with a raw stack trace before REA ran.

Treat the empty selection as unset in the launcher, before any runtime
import, and cover startup with a compiled-CLI regression.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(process): report no remaining time after a provider deadline fires

Node timers can fire while performance.now() is still a fraction of a
millisecond short of the monotonic deadline. ProviderStartupDeadline then
reported interruption "timeout" and an aborted signal while remainingMs()
returned ceil(0.x) = 1, so polling loops guarded by remainingMs() > 0
(Hopper and Ghidra startup) could run past an elapsed deadline, and the
real-timer test "aborts a bounded wait when its real monotonic deadline
expires" failed intermittently in CI (expected 1 to be 0).

Once the deadline's own timer has classified a timeout, report zero
remaining time. A deterministic regression test fires the timer with the
monotonic clock 0.4 ms short of the deadline.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-13 23:53:35 +08:00
Riley Gramlich 3f80b75708 fix(source-maps): keep sources when sourcesContent length differs (#2873)
ECMA-426 DecodeSourceMapSources reads a content slot only while its index
is within sourcesContent, so a shorter array leaves trailing sources
without content and a longer array adds no sources. Local application
analysis and browser collection rejected any length mismatch and dropped
every original from the map.

The content helper now checks only entry types. Browser collection drops
its separate check because the leaf schema already validates every
supplied entry, and the raw records are still budgeted before parsing.

Fixes #2821
2026-10-13 23:52:47 +08:00
吴佳浩Albenand吴佳浩 855ed9689e fix(application): preserve a cancellation that races a completed provider run (#2710)
Squash merge of https://github.com/morluto/rea/pull/2710.

Original pull request submitted by @XiaoBinGan.

Co-authored-by: 吴佳浩 <wujiahao@supremind.com>
2026-10-13 23:51:44 +08:00
吴佳浩Albenand吴佳浩 59298923a5 fix(apktool): read the resource locale grammar from its owner (#2738)
Squash merge of https://github.com/morluto/rea/pull/2738.

Original pull request submitted by @XiaoBinGan.

Co-authored-by: 吴佳浩 <wujiahao@supremind.com>
2026-10-13 23:51:30 +08:00
Royceandmorluto af782dd5f5 fix(setup): treat empty DSH_HOME as unset (#2686)
* fix(setup): treat empty DSH_HOME as unset (#2666)

The released `@deepseek-ai/dsh-home-paths` package explicitly treats an
empty or whitespace-only `DSH_HOME` as unset and selects the default
Harness home patch. `dshHomeDirectory` previously used nullish coalescing
(`env.DSH_HOME ?? join(home, ".dsh")`), so an empty override flowed
straight through `join(..., "cordis.patch.yml")` and produced a
cwd-relative `cordis.patch.yml` outside the client-selected home.

Treat empty and whitespace-only overrides as unset, matching the sibling
OpenCode override resolver and the released DSH resolver. The patch now
lands at `$DSH_HOME/cordis.patch.yml` for explicit overrides, the default
`~/.dsh/cordis.patch.yml` for empty/unset, and trimmed whitespace for
padded overrides.

Adds `src/application/SupportedClients.test.ts` covering the empty,
whitespace-only, unset, padded, and explicit cases via the public
`supportedClients(...)` entrypoint. Fixes #2666.

* test(setup): remove superseded DSH path unit coverage

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 23:51:28 +08:00
d86d29c411 fix(mcp): keep advertised inputs within client reference budgets (#1749)
Squash merge of https://github.com/morluto/rea/pull/1749.

Co-authored-by: akushonkamen <akushonkamen@163.com>
Co-authored-by: GLM <noreply@z.ai>
2026-10-13 23:49:53 +08:00
TonyWuandTonyWu 77e71d55ec fix(native): name numeric Mach-O build-version platforms and tools (#2065)
Squash merge of https://github.com/morluto/rea/pull/2065.

Co-authored-by: TonyWu <2038838159@qq.com>
2026-10-13 23:49:37 +08:00
SII-YDDandmorluto 168bfe5060 fix(target): probe WASM before inferred database routing (#2007)
* fix(target): probe WASM before inferred database routing

* test(wasm): execute recovery arguments from native refusal

* test(ci): repair fixture alignment and share MCP assertions

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 23:49:33 +08:00
JinandClaude Opus 5.5 554ea7611e fix(adb): keep the device's refusal when a package change exits nonzero (#2344)
Squash merge of https://github.com/morluto/rea/pull/2344.

Co-authored-by: Hui <a3239737781@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 23:49:20 +08:00
Thang LuuandThang Luu 911894d758 feat(wasm): compare retained core-module interfaces (#2464)
Squash merge of https://github.com/morluto/rea/pull/2464.

Co-authored-by: Thang Luu <76550342+thangldw@users.noreply.github.com>
2026-10-13 23:49:04 +08:00
pablodiazjorgeandpablodiazjorge 75e7965dc3 docs(nativeaot): document the verified Windows-host parser route (#2497)
Squash merge of https://github.com/morluto/rea/pull/2497.

Co-authored-by: pablodiazjorge <pablodiazjorgegarcia@gmail.com>
2026-10-13 23:48:41 +08:00
The one -NiuandNIU-123370 522b9927c8 feat(recorded-crash): decode AArch64 core register notes (#2505)
Squash merge of https://github.com/morluto/rea/pull/2505.

Co-authored-by: NIU-123370 <191000457+NIU-123370@users.noreply.github.com>
2026-10-13 23:48:24 +08:00
2b98fc5f51 fix(cli): reject empty analyze and artifact target paths (#2687)
Squash merge of https://github.com/morluto/rea/pull/2687.

Co-authored-by: LouisChen <3361008+chqcjq@users.noreply.github.com>
Co-authored-by: morluto <morluto@users.noreply.github.com>
2026-10-13 23:48:09 +08:00
The one -NiuandNIU-123370 8b1549a264 test: share repeated boundary preambles to restore the jscpd gate (#2691)
Squash merge of https://github.com/morluto/rea/pull/2691.

Co-authored-by: NIU-123370 <191000457+NIU-123370@users.noreply.github.com>
2026-10-13 23:47:55 +08:00
The one -NiuandNIU-123370 782cc3b060 refactor(process): give the prlimit producer budget one owner (#2698)
Squash merge of https://github.com/morluto/rea/pull/2698.

Co-authored-by: NIU-123370 <191000457+NIU-123370@users.noreply.github.com>
2026-10-13 23:47:41 +08:00
f825c869d6 fix(artifacts): bound expanded ASAR listing before allocation (#2724)
Squash merge of https://github.com/morluto/rea/pull/2724.

Co-authored-by: chAngE <172472630+chAng-L19@users.noreply.github.com>
Co-authored-by: morluto <morluto@users.noreply.github.com>
2026-10-13 23:47:18 +08:00
LouisChenandLouisChen 9240a4e9ac fix(javascript): preserve asterisks in block source-map URLs (#2728)
Squash merge of https://github.com/morluto/rea/pull/2728.

Co-authored-by: LouisChen <3361008+chqcjq@users.noreply.github.com>
2026-10-13 23:47:03 +08:00
Jeremy Levartovsky ba06d9ec52 fix(setup): keep a Cordis operation's own fields when uninstall empties it (#2730)
Squash merge of https://github.com/morluto/rea/pull/2730.

Co-authored-by: JayOfTheKeyboard <140487034+JayOfTheKeyboard@users.noreply.github.com>
2026-10-13 23:46:46 +08:00
Feng af7638fa9d fix(session): preserve investigation records after failed target opens (#2754)
Squash merge of https://github.com/morluto/rea/pull/2754.

Co-authored-by: Adkid-Zephyr <laufs0310@163.com>
2026-10-13 23:46:23 +08:00
Iven KooandIvenKooLab 48b90fbaa4 fix(native): preserve unresolved calls in untyped route fallback (#2757)
Squash merge of https://github.com/morluto/rea/pull/2757.

Co-authored-by: IvenKooLab <276572622@qq.com>
2026-10-13 23:46:02 +08:00
Kaoru0822andClaude Opus 5.5 c580ba940a fix(browser): treat an unusable cache list as incomplete storage coverage (#2762)
Squash merge of https://github.com/morluto/rea/pull/2762.

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 23:43:11 +08:00
2a78763e20 feat(native): search analyzed memory for masked byte patterns (#2341)
* feat(native): search analyzed memory for masked byte patterns

The diff adds search_bytes to the CLI, the shared MCP contract, Ghidra
inventory, and the headless bridge, and advertises the same operation on
Hopper as unavailable until that provider is verified. Patterns are
hexadecimal byte pairs with optional ?? wildcards and an optional
equal-length bit mask; the TypeScript boundary and the bridge both
require a nonempty pattern, equal lengths, and at least one constrained
bit. Ghidra walks initialized blocks in address order within inclusive
bounds, keeps overlapping matches inside a single block, and returns
each hit with its segment, unique source file offset, and containing
procedure, continuing through a cursor bound to the target, profile,
pattern, mask, and bounds. Each page reports the inclusive ranges
examined and the skipped uninitialized blocks; cancellation returns a
typed error and discards the active Ghidra database.
address_to_file_offset now uses the same unique-offset helper, which
rejects a mapping that is missing, ambiguous, or larger than a safe
integer.

* fix(native): limit unbounded byte search to loaded memory

Without start or end, search_bytes walked every Ghidra block, including
non-loaded file parts such as .symtab and overlay blocks, which the docs
call out of scope. Unbounded searches now skip those blocks and list them
in searched.skipped_not_loaded; explicit bounds in their address space
still search them. Document that a match must end at or before end.

* fix(native): preserve implicit address spaces in range consumers

* fix(native): reject present malformed byte search state

---------

Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 23:41:45 +08:00
70611713e1 fix(targets): compare the Info.plist bplist prefix without ASCII folding (#2100)
* fix(targets): compare the Info.plist bplist prefix without ASCII folding

The six-byte prefix check used an encoding that maps high-bit bytes onto bplist, so a non-binary property list could be sent to the binary decoder.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(targets): satisfy the strict index check in the plist prefix test

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 23:37:45 +08:00
43e33cd49e fix(artifacts): compare plist and NIB signatures without ASCII folding (#2098)
* fix(artifacts): compare plist and NIB signatures without ASCII folding

Buffer.toString("ascii") drops the high bit, so a non-plist prefix could be decoded as bplist00 or NIBArchive and sent to the binary decoder.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(artifacts): satisfy the strict index check in the signature test

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(artifacts): remove superseded signature regression causing CI failure

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 23:37:31 +08:00
Tony Yuandmorluto efc491ab45 feat(setup): add Kimi Code client support (#2744)
* feat(setup): add Kimi Code client support

Registers rea in ~/.kimi-code/mcp.json (or $KIMI_CODE_HOME/mcp.json), preserving unrelated settings with a .rea.backup; registration is command-based stdio with no type field, startupTimeoutMs 30000 and toolTimeoutMs 120000 from MCP_STARTUP_POLICY.
Strict JSON parsing: empty or whitespace-only files initialize; malformed JSON is rejected untouched with no backup; explicit transport other than stdio reports invalid.
Bundled skill uses the shared ~/.agents/skills destination that Kimi Code discovers, installed and verified through the shared-skill flow.
Validated by 19 Kimi-specific tests, 260 adjacent-client regressions passing, check:fast and docs:check, and real-client verification on Kimi Code 0.40.1 (173 tools discovered, real mcp__rea__analyze_javascript_application call succeeded).

* fix(setup): preserve Kimi tool filters and strict JSON coverage

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 23:36:54 +08:00
hhangandhhang b19f9e4d2d fix(javascript): keep spread child argv counts unknown (#2748)
* fix(javascript): keep spread child argv counts unknown

* test(javascript): own runtime controls and tighten evidence assertions

* test(apple): keep dyld slice regression portable

---------

Co-authored-by: hhang <169027046+xu91102@users.noreply.github.com>
2026-10-13 23:36:35 +08:00
af142f656c fix(mcp): report references a provider truncated in trace_feature (#2348)
* test(mcp): expect trace_feature to report references the provider truncated

A provider that reports a capped xrefs read makes the trace truncated with a residual unknown for that target. An explicit complete report and a provider that reports nothing leave the trace as before.

* fix(mcp): report references a provider truncated in trace_feature

Executions gain an optional flag for a capped xrefs read, set by the IDA adapter from the upstream cap. trace_feature marks the trace truncated with a residual unknown for that target only when the provider reports a truncation; a provider that reports nothing keeps its previous result. The trace_feature workflow profile is revised so earlier results are not replayed.

* refactor(ida): report reference truncation whenever the runner observed an xrefs cap

* test(mcp): cover session forwarding, per-target state, native reports and failures of the trace coverage

* test(mcp): format the session forwarding test

* test(process): format processCaptureLifecycle.test.ts after the merge

* test(ida): declare the alignment each synthetic universal slice offset satisfies

* fix(process): report no remaining time after a provider deadline fires

Node timers can fire while performance.now() is still a fraction of a
millisecond short of the monotonic deadline. ProviderStartupDeadline then
reported interruption "timeout" and an aborted signal while remainingMs()
returned ceil(0.x) = 1, so polling loops guarded by remainingMs() > 0
(Hopper and Ghidra startup) could run past an elapsed deadline, and the
real-timer test "aborts a bounded wait when its real monotonic deadline
expires" failed intermittently in CI (expected 1 to be 0).

Once the deadline's own timer has classified a timeout, report zero
remaining time. A deterministic regression test fires the timer with the
monotonic clock 0.4 ms short of the deadline.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit dfdaa0ca74)

* fix(process): keep monotonic deadline ownership after merge

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 23:35:21 +08:00
Huamnanxandmorluto ffd8c9c03d test(evaluation): grade managed agent fixture facts (#1924)
* test(evaluation): grade managed agent fixture facts

* test(evaluation): cover distinct managed artifact deliveries

* fix(evaluation): require unfiltered managed type inventories

* fix(evaluation): preserve selected entry point grounding

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 23:34:17 +08:00
CindyGestandmorluto 7dcd23c005 fix(setup): separate host support and stop skill checks after drift (#2073)
* fix(host): separate REA host support from Hopper installer targets

LinuxDistribution.supported carried two meanings at once: that the host runs
REA, and that the automated Hopper installer is tested on it. The doctor read
the first meaning from the second, so Debian-derived hosts such as Kali
reported unsupported_host while their providers worked normally.

Rename the field to hopperSetupSupported, which only the Hopper installer and
setup planning consume, and derive host support from the package family
through supportsReaHost(). Any distribution with a recognized apt, dnf, or
pacman family is now a REA host. The tested Hopper installer list is
unchanged, so Debian and Kali keep falling back to configuring an existing
provider instead of downloading an untested package.

Verified on Kali 2026.3 (Debian testing, x86_64): rea doctor reports a healthy
environment with Ghidra 12.1.4, and analyze_function decompiles an x86_64 ELF
through the MCP server. npm run check, npm run docs:check, and the application
and Ghidra suites pass. No Hopper or IDA host workflow was exercised.

* fix(setup): stop skill checks after first drift

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 23:33:54 +08:00
ed93abed02 fix(cli): label each uninstall skill root by its client (#2197)
* fix(cli): label each uninstall skill root by its client

rea uninstall reported every managed skill destination under the
shared "skill" label (with only Claude Code special-cased), so a
retained or failed directory could not be told apart from the others.

Use the destination's client identifier so the aggregated detail names
each root: "shared: retained (...)", "claude_code: removed (...)",
"antigravity: retained (...)". Retained and failed entries now point to
the specific directory that needs attention.

Fixes #2111

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* test(cli): align skill summary assertions with client and path labels

---------

Co-authored-by: wjl8636 <wjl8636@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 23:32:31 +08:00
Rohit Ghumareandmorluto d005bd5dcd fix(javascript): preserve CommonJS lookup and analysis provenance (#2306)
* fix(javascript): preserve CommonJS directory and ancestor lookup

* test(apple): bound cold archive fixture compilation

* test(release): scope preflight schema to selected jobs

* fix(javascript): retain requested analysis limits in Evidence

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 23:31:30 +08:00
Lucienandmorluto 688defca0f fix(comparison): recheck historical calls-only changes (#2223)
* fix(comparison): preserve incomplete function call evidence

Keep missing, unknown, partial and unresolved call relationships unknown in function comparisons and reconstruction claims. Aggregate IDA caller and callee coverage without overwriting uncertainty.

Validate with 144 focused tests, typecheck/lint, docs checks and test discovery. Real providers were not verified; the full headless IDA session suite requires unavailable Windows native controls.

* fix(comparison): recheck historical calls-only changes

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 23:30:31 +08:00
Lucienandmorluto 3c39a3f536 test(adb): isolate cross-platform activity launch regressions (#2226)
* fix(adb): reject timed-out activity launches and retain observations

* test(adb): isolate cross-platform activity launch regressions

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 23:30:18 +08:00
xingzhou e6c6fdd21a fix(browser): preserve script-specific parser uncertainty (#2858) 2026-10-13 23:22:59 +08:00
xingzhou 40e94aeb9d fix(browser): preserve distinct WebMCP declaration metadata (#2854)
* fix(browser): preserve distinct WebMCP declaration metadata

* test(browser): retain the first equivalent declaration location
2026-10-13 23:19:37 +08:00
Shubham AhujaandClaude Opus 5.5 3e7c630223 fix(mcp): count the error flag in error result budgets (#2848)
Error results reach the wire as text plus `isError: true`, but the
text-only envelope omitted the flag. An error up to 15 bytes above the
result budget passed both the delivery and transport checks and was sent
as the original diagnostic instead of taking the oversized-error recovery
path.

Rename the error representation to "error-text" and size its envelope
with the flag, so ToolResultDelivery and the error transport share the
complete delivered shape. Successful structured results are unchanged.

Fixes #2557

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 23:06:55 +08:00
204a6f3f20 fix(ghidra): import HashMap in the bridge script (#2844)
8823c55b0 added a HashMap use without its import, so Ghidra could not
compile ReaGhidraBridge.java and every Ghidra-backed operation exited
before bridge startup.

Co-authored-by: SeokMyungSeop <SeokMyungSeop@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 22:57:45 +08:00
yasshmittalandClaude Opus 5.5 e13da084e7 fix(apple): exclude AppleDouble sidecars from IPA components (#2838)
* fix(apple): exclude AppleDouble sidecars from IPA components

IPA projection returned every inventoried component before the AppleDouble
sidecar filter applied to directory, ZIP and DMG roots, so ._* and
__MACOSX/ metadata appeared as native libraries and frameworks while the
result reported that sidecars are excluded. Apply the sidecar exclusion
first for every root format; IPA still keeps non-sidecar components
outside Payload/.

Fixes #2546

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(apple): exercise the __MACOSX exclusion with a non-dotfile name

The IPA fixture's __MACOSX entry also started with "._", so the AppleDouble
filename rule excluded it and a broken __MACOSX check would go unnoticed.
Use a library name that only the __MACOSX rule excludes; the native
library assertion now fails if that rule regresses.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 22:55:21 +08:00
Dragolone 94072364c7 fix(sqlite): keep triggers whose declaration spells the table in another case (#2807)
* fix(sqlite): keep triggers whose declaration spells the table in another case

SQLite stores a trigger's target in sqlite_schema.tbl_name exactly as
written in CREATE TRIGGER and resolves it by ASCII case folding, while an
index stores the table's own name. The selected_table scope filtered
tbl_name with COLLATE BINARY, so a trigger declared ON users for table
Users was omitted while the schema still reported complete. Match
dependents with COLLATE NOCASE, and report each trigger's table as the
stored table or view name so the selected-table contract still holds.

* fix(sqlite): refuse an inexact table before the case-folded schema scan

With the NOCASE expansion, a selection such as users for table Users
expanded the stored table's declarations before readRows reported the
missing exact name, so large trigger definitions could surface as an
output-limit failure instead of the selection error. Check the exact
table name against PRAGMA table_list first and share the selection
failure with readRows.

* fix(sqlite): fold dependent names only for a row-selectable table

An exact view, virtual or possible shadow table passed the exact-name
guard, so the NOCASE scan could still expand a differently cased trigger
past the reply budget before readRows refused the selection. Fold case
only when the selection is an ordinary table that is not a possible
shadow table, and keep the exact filter otherwise so every refused
selection fails as it did before the case-folding change.

* fix(sqlite): resolve trigger tables without folding unrelated names

The stored-name lookup case-folded every PRAGMA table_list name up front,
so a selected-table inspection of a tiny table paid for large unrelated
identifiers. A selected-table scan only returns the selected table's
dependents, so report the selected name directly; other scopes use an
exact stored name when one matches and fold the stored names once, only
when a trigger spells its target differently.

* perf(sqlite): fold each table name once when matching shadow parents

The extracted shadow-parent helper folded the table name inside the find
callback, once per unresolved virtual table, and the selected table was
checked both before and during the schema scan. Fold the name once per
call as before, and reuse the selected table's result in the scan.
2026-10-13 22:37:47 +08:00
xin zhuangandmorluto e103a89889 fix(artifacts): preserve NIB key and class name identity (#2109)
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 22:36:06 +08:00
The one -Niuandmorluto edd9939879 fix(browser): give the DevToolsActivePort wait one deadline owner (#2265)
* Single owner for the Chrome DevToolsActivePort startup deadline

Cold Chrome startup tolerance was spelled three ways for one wait: the helper
fell back to 15,000 ms, the runtime fixture lifecycle passed a literal 60,000,
and the real-browser verifier kept a local 60,000 constant. A per-caller literal
quietly changes how long a lane tolerates a cold browser before it fails, and
the failure reported only elapsed_ms, so a reader could not see which ceiling
was hit.

Export BROWSER_DEVTOOLS_STARTUP_TIMEOUT_MS as the owner, use it for the helper
fallback and both call sites, and report startup_timeout_ms in
BrowserStartupError. A source guard keeps callers from restating the number.

* Pin the DevToolsActivePort deadline guard to the startup call argument

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 22:35:45 +08:00
Vinícius Andradeandmorluto 1e38a038c6 fix(process): release handles and close worker on incomplete cleanup (#2321)
* fix(process): unreference handles on incomplete provider process stop

Signed-off-by: Vinícius DS Andrade <95101505+viniciusdsandrade@users.noreply.github.com>

* fix(javascript): close worker and release handles on incomplete cleanup

Signed-off-by: Vinícius DS Andrade <95101505+viniciusdsandrade@users.noreply.github.com>

* fix(javascript): defer worker stdin close until incomplete cleanup

Signed-off-by: Vinícius DS Andrade <95101505+viniciusdsandrade@users.noreply.github.com>

* test(javascript): assert cleanup error instance on inspection uncertainty

Signed-off-by: Vinícius DS Andrade <95101505+viniciusdsandrade@users.noreply.github.com>

---------

Signed-off-by: Vinícius DS Andrade <95101505+viniciusdsandrade@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 22:34:54 +08:00
morluto 6e25cc3c76 fix(tests): clear lint and typecheck errors on main
Split two over-long test functions into module-level helpers
(max-lines-per-function): IdaSessionClient transport cases, IDA provider
composition, and function comparison identity cases keep their names and
assertions.

Also fix three typecheck errors: default omitted
direct_invokes_exhaustive to null so the result schema stays JsonValue
compatible, parse the ghidra dossier before spreading it, and delete the
optional beforeCall hook instead of assigning undefined under
exactOptionalPropertyTypes.
2026-10-13 22:32:01 +08:00
7369b5ee63 fix(ida): retain cleanup facts and let one-shot commands exit (#2814)
* fix(ida): let one-shot commands exit after retaining cleanup

When a headless IDA request outlives timeoutMs (for example idb_open on
a large binary still running auto-analysis), REA cannot prove the
worker stopped and correctly retains the upstream connection and
private workspace. The retained stdio child and its pipes stayed
referenced, so a one-shot CLI printed its cleanup_incomplete result and
then never exited, keeping the supervisor, worker and database alive.

Detach the retained connection's child process and pipes from the event
loop when cleanup fails. A long-lived MCP server can still retry
through the retained connection; a one-shot CLI exits with status 1,
and the supervisor observes end of input.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(ida): report an unverified upstream worker after failed cleanup

Upstream ida-pro-mcp spawns headless workers in their own session and
deliberately leaves them running when its supervisor exits, so neither
a retained connection nor REA exiting proves that a timed-out worker
stopped. When cleanup fails before the owned worker's release is
confirmed, the cleanup diagnostics now name its private session and
state that it may still be running until it finishes and reaches its
idle timeout. Failures after a confirmed release, or before any open
was attempted, do not report a worker.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(ida): name the inventory sessions that block worker release

When idb_close confirmed the requested worker gone but the follow-up
inventory still found a different peer on the private input, the
upstream_worker diagnostic named the released session and could
misdirect manual cleanup. Record the session ids of the inventory
entries that actually blocked release and report those; fall back to
the requested private session only when no inventory evidence exists
(an incomplete open or an unanswered request). The field becomes
session_ids, since more than one entry can block release.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(ida): keep blocker evidence and backend in cleanup diagnostics

The release diagnostic reset its observed blockers on every close, so a
retry whose inventory was unavailable fell back to the already-released
session; and it kept only session ids, describing a blocking GUI
session with detached-worker guidance.

Blocker evidence is now replaced only by a successful inventory, and
the diagnostic becomes upstream_sessions with each session's id and
backend, with guidance that distinguishes workers from GUI sessions.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(ida): stop naming a worker after its close was confirmed

When idb_close confirmed the requested worker's release but the
following inventory failed, no blocker evidence existed yet and the
diagnostic fell back to the confirmed-absent worker. After a confirmed
close the diagnostic now reports an empty session list, meaning any
remaining same-input sessions are unknown.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(ida): group cleanup diagnostic cases in their own suite

The added cases pushed the reaped-worker describe block past the
150-line limit enforced by lint:built.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(ida): keep ownership and pre-close peers in cleanup diagnostics

Blocking sessions dropped the parsed ownership fact, so an external
unowned worker looked the same as REA's own; and recovery inventory
recorded same-input peers only when the requested worker was missing,
so a peer seen beside it was lost if the following close failed.

Each reported session now carries owned (null when no inventory has
reported it), and recovery records every same-input session until a
later successful inventory replaces that evidence.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(ida): drop a confirmed worker from retained cleanup blockers

Recovery inventory now records the requested worker beside any peers,
so once idb_close confirmed its release a failing follow-up inventory
still reported it as unreleased. Remove the confirmed session from the
retained blockers at close confirmation and keep the remaining peers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(ida): retry cleanup through a detached stdio connection

The retry coverage used a recording connection whose detach() only
counts calls, so it could not show that detaching keeps a retained
connection usable. Run a real stdio supervisor in a long-lived host,
fail the first post-close inventory, and require the retry through the
same detached connection to confirm release and remove the workspace.
The test fails if detach() destroys the pipes instead of unref-ing them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(ida): close the stdio supervisor in failed retry tests

Merge main and give its new IdaNativeProtocol connection double the
required detach() method. The real-stdio retry test now closes its
connection in an unconditional teardown, so a failing assertion cannot
leave the supervisor waiting on stdin.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(ida): share the cleanup diagnostic expectation in session tests

Repeated upstream_sessions assertions and the unrelated live peer
literal pushed repository duplication over the jscpd threshold. Build
the expected diagnostic with one helper and reuse one peer constant.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(ida): distinguish observed ownership from unreported input bindings

* fix(ida): keep requested-ID conflicts in recovery cleanup

Recovery inventory kept only same-input entries, unlike the startup and
post-close filters. After an explicitly rejected open, an entry under
the requested session ID on another input was dropped, so cleanup could
close the connection, remove the private workspace and report success
while that session stayed registered. Match the requested ID as well so
the entry is retained as a blocker and cleanup stays incomplete.

Also split the cleanup diagnostics suite under the lint:built function
length limit, and make two startup-inventory fixtures pass a defined
session id so the test file typechecks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(ida): preserve retained session inventory facts

---------

Co-authored-by: Xingchuan Ma <xingchuan.ma@msn.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: morluto <morluto@users.noreply.github.com>
2026-10-13 22:27:26 +08:00
morluto c85abacdb2 fix(ida): mock legacy list_functions in attached transport tests
The attached fixture negotiates the legacy protocol, so list_procedures
calls list_functions, never list_funcs. The list_funcs mocks added in
#2449 never fired and both tests failed on arrival. No product change.
2026-10-13 22:26:36 +08:00
cbe769b504 docs: move translated READMEs under docs/readme (#2165)
Move all 18 translated READMEs into docs/readme/ and keep the English entry at the repository root. Update navigation, guide/image paths, website discovery and documentation checks together.

Preserve the contributor's work and current main content. Cover every translated command and HTML destination, and repair the verified Persian heading fragments.

Validation: 203 focused tests on the synchronized head, plus 119 tests and documentation checks on the integration with main fc0dbe7f9; 1,501 local references and 399 authored fragments verified. The complete CI workflow passes. The upstream x64 dylib fixture correction is included; an unchanged-checkout rerun cleared the earlier deadline timing assertion.

Closes #2141

Co-authored-by: Aimal Khan <94559553+aimalexe@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
2026-10-13 22:25:32 +08:00
865c35e25a fix(ghidra): disclose missing PDB symbol coverage (#2275)
* fix(ghidra): disclose missing PDB symbol coverage

* style: format reviewed source changes

* fix(ghidra): bound PDB handshake and retain coverage uncertainty

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
Co-authored-by: morluto <17176810+morluto@users.noreply.github.com>
2026-10-13 22:17:27 +08:00
Yann Roberto 1016c96e6b fix(source-maps): accept omitted names during browser collection (#2831) 2026-10-13 22:17:09 +08:00
Avocadoandmorluto f0b8ae1fc8 fix(javascript): trace nested Electron bridge members (#2745)
* fix(javascript): trace nested Electron bridge members

* fix(javascript): preserve shallow Electron bridge paths

* fix(javascript): preserve exact Electron bridge member paths

* fix(javascript): preserve exact Electron bridge paths

* fix(javascript): preserve bridge entry ownership

* fix(javascript): preserve bridge path identity

* fix(javascript): normalize typed feature seeds before tracing

* test(javascript): normalize direct selector seed inputs

* refactor(javascript): consume normalized feature seed defaults

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 22:16:44 +08:00
c9982ad8bf fix(browser): release scenario listeners and allow bounded Chrome close (#2441)
* fix(browser): release scenario event listeners on finish

* fix(browser): allow Chrome cleanup to complete within bounded deadline

* style(browser): format cleanup deadline regression

* test(browser): expect popup cleanup listener

---------

Co-authored-by: LcokHcak <268763621+LcokHcak@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 22:16:25 +08:00
9b08c2b1cb fix(native/windows): share FILE_SHARE_DELETE so owned objects can be marked delete-pending (#2445)
* fix(native/windows): share FILE_SHARE_DELETE so owned objects can be marked delete-pending

The Windows native authority self-test (`binding.call("inspect", [])`) fails
closed on stock Windows x64 with a non-admin user:

    Delete owned object by handle failed (Win32 5): Access is denied.

Reproduced deterministically (15/15 failures) on Windows 11 x64, Node 22.22.2,
non-elevated account. Because `inspect` is a hard gate, every provider-backed
operation is rejected and the whole native authority is unusable.

Root cause: Windows requires that every handle currently open on an object
grants FILE_SHARE_DELETE before that object can be marked delete-pending via
SetFileInformationByHandle(FileDispositionInfo).

`createRelative()` creates the runtime object with FILE_SHARE_READ only, and the
returned handle is retained for the lifetime of the runtime
(`Runtime::directory`). `closeRuntime()` later needs to mark that same object
delete-pending, but the retained handle never shared DELETE, so the transition
is denied (Win32 5).

The child handles opened by `closeRuntime()` for recursive cleanup have the same
defect: they request DELETE access while sharing only FILE_SHARE_READ, so the
subsequent `dispose()` call on those very handles is denied too.

Fix: add FILE_SHARE_DELETE to both share masks, keeping FILE_SHARE_READ and
deliberately not adding FILE_SHARE_WRITE, so the existing write-exclusion
behaviour of the retained leases is preserved while the delete-pending
transition the cleanup path depends on becomes possible.

Verified end-to-end after the change: `inspect` passes, the full runtime
lifecycle (`runtime_create` / `runtime_mkdir` / `runtime_write` / `runtime_open`
/ `runtime_close`) completes with no residual directories, and a real 925 MB
application analysis (4396 files) runs through all stages.

* fix(windows): anchor private runtime cleanup to directory handles

* test(windows): assert cleanup sharing failure by code

---------

Co-authored-by: maodns <311719211+maodns@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 22:16:05 +08:00
21aa877e5a fix(process): honor startup deadlines and add a CI audit skill (#2692)
* docs(skills): add CI and test correctness auditing

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>

* fix(process): report no remaining time after a provider deadline fires

Node timers can fire while performance.now() is still a fraction of a
millisecond short of the monotonic deadline. ProviderStartupDeadline then
reported interruption "timeout" and an aborted signal while remainingMs()
returned ceil(0.x) = 1, so polling loops guarded by remainingMs() > 0
(Hopper and Ghidra startup) could run past an elapsed deadline, and the
real-timer test "aborts a bounded wait when its real monotonic deadline
expires" failed intermittently in CI (expected 1 to be 0).

Once the deadline's own timer has classified a timeout, report zero
remaining time. A deterministic regression test fires the timer with the
monotonic clock 0.4 ms short of the deadline.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(process): retain the monotonic startup deadline budget

Recheck the absolute monotonic deadline when an early timer fires, and keep
polling waits pending until their own target or the shared deadline arrives.
Preserve caller cancellation, disposal and the unchanged real-timer assertion.
This replaces the earlier remainingMs workaround: an early callback must not
classify an unexpired deadline as a timeout.

Adapt the production fix and regression cases from nosmile99 PR #2793 at
5460016936, including the independently reported
both-timers-early reproduction. Preserve the prior contributor's commit.

Fixes #2760

Co-authored-by: nosmile99 <147114285+nosmile99@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>

* style(tests): format the Java class fixture signature

The captured main baseline has an unformatted helper signature. Apply the
repository formatter so the required style gate can run to completion.

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>

* test(ghidra): group expanded suites by their contracts

Split result-envelope and completeness checks, and Windows invocation and
runtime checks, into named describe groups within the existing lint limit.
Preserve every test, fixture, assertion and platform prerequisite.

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>

---------

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: nosmile99 <147114285+nosmile99@users.noreply.github.com>
2026-10-13 22:15:43 +08:00
GuoJun Tangandmorluto b17487f482 fix(comparison): keep unsupported function facets unknown (#2450)
* fix(comparison): keep unsupported function facets unknown

* docs(comparison): explain function facet availability

* fix(analysis): preserve unavailable facets across Evidence consumers

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 22:15:01 +08:00
GuoJun Tangandmorluto 30e9b83a68 fix(ida): retain bounded stderr on upstream transport failures (#2449)
* fix(ida): retain bounded stderr on upstream transport failures

* fix(ida): preserve stderr through operation and cleanup boundaries

Address review findings at their producing boundaries and preserve directly affected consumers. Source inspection and git diff --check completed. Tests and automated checks were not run at the user’s explicit request.

* fix(ida): preserve cancellation and single-owner stderr diagnostics

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 22:14:39 +08:00
Dragolone 5278835b53 fix(flutter): find snapshot headers that a read chunk boundary splits (#2804)
* fix(flutter): find snapshot headers that a read chunk boundary splits

Within one chunk the snapshot hash scanner resumes four bytes after each
magic, but across chunks it resumed after the whole 52-byte record and
dropped the carried bytes. A magic starting 49-51 bytes after a rejected
(non-hex) record was therefore lost when its four bytes straddled a read
boundary, so the result depended on chunking and a conflicting hash could
be omitted while coverage stayed complete. Resume after the magic in both
cases.

* refactor(flutter): drop the snapshot scanner cursor the fix superseded

After a complete record the carried tail is shorter than a magic, and a
pending magic has not been processed, so every push can rescan its carried
bytes from the start without counting a record twice. Remove the absolute
cursor and base offset, and cover sections 49-51 bytes after a rejected
record with two-chunk and byte-at-a-time feeds.
2026-10-13 22:14:07 +08:00
e3a012d6c9 fix(flutter): reject ambiguous ELF mappings and bound note reads (#2334)
* fix(flutter): reject ambiguous mappings and bound ELF notes

Resolve PT_LOAD interpretations once for symbols and dynamic tables, including partial overlaps. Preserve equivalent mappings and leave conflicting or invalid alternate definitions unresolved.

Bound distinct nonempty ELF note ranges before retention and reads, deduplicate shared ranges, preserve observed build-ID candidates and report partial coverage on exhaustion. Correct shared verifier workspace ownership and structured Windows path assertions.

Addresses the two post-merge review findings on PR #1582. Validation: 147 focused cases across Linux, macOS Sonoma 14.8.9 and non-administrator Windows 11 build 26300.9457; real Gallery CLI/MCP checks on all three hosts; six compiler-produced ELF32/ELF64 hash-layout cases; static, docs, discovery and duplication checks. Full string-pool projections remain explicitly bounded.

* test(flutter): parse complete facts through named result schemas

---------

Co-authored-by: me <me@medz.org>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 22:13:22 +08:00
fd65e278be fix(setup): resolve the configuration MSIX Claude Desktop reads (#2426)
* fix(setup): resolve the configuration MSIX Claude Desktop reads

On Windows, setup, doctor and uninstall always used
%APPDATA%\Claude\claude_desktop_config.json. The MSIX build of Claude
Desktop (package family Claude_pzs8sxrjxfjjc) virtualizes AppData writes
and reads its package-private copy under
%LOCALAPPDATA%\Packages\<family>\LocalCache\Roaming\Claude once it exists.
Setup from an ordinary process therefore wrote a file the app never reads,
and doctor reported that file healthy.

Resolve the Claude Desktop location once, in supportedClients(): the
private copy when it exists, else the AppData file the package still
reads, else a new private copy. An unrecognized Claude_* publisher or
unreadable package storage is reported instead of falling back to AppData.

Compare one of the package's private files with its AppData path through
realpath.native to learn whether the running package started this
process. From that redirected view, or when no file can be compared,
setup and uninstall refuse to change Claude Desktop's configuration,
because the running app rewrites it from memory. From a redirected view
they also refuse writes under %APPDATA% and %LOCALAPPDATA% for other
clients and skills, which would land in the package's private storage.

Setup refuses to register a launcher stored in any MSIX package's
private storage, discloses which file the package reads, and tells the
user to quit Claude Desktop fully on every platform. Configuration fails
with target_changed if the package switched to its private copy after
the plan was shown. Uninstall removes REA from both files.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(setup): record the verified MSIX Claude Desktop registration

On the Windows 11 host with the MSIX build 2.31226.1.0, setup from an
ordinary terminal with the app quit wrote the package-private copy. After
the app restarted, the entry persisted, doctor reported it aligned, and
Claude Desktop's MCP log showed REA starting and answering tools/list.
Uninstall against the real app remains unverified.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* style(setup): format MSIX review corrections

* fix(setup): carry the admitted legacy path through uninstall removal

clientRegistrationRemoval returned a removeLegacy boolean, and removeClient
recomputed the legacy path separately, so TypeScript could not prove the
path existed when removeLegacy was true and `tsc --noEmit` failed in
Uninstall.ts. The decision now returns the admitted legacy path itself;
preflight and removal consume that one value.

The alias regression matched new-destination children with a "/"
separator while building paths with path.join, so it failed on Windows.
It now uses path.sep.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(setup): split MSIX Claude Desktop lifecycle cases under the function length limit

The uninstall cases pushed the registration lifecycle describe past the 150-line lint limit. Group them separately, with the AppData registration case kept among the registration cases.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 22:04:22 +08:00
Kaoru0822andClaude Opus 5.5 5996d8160f fix(native): bound captured output carried by native command failures (#2764)
* fix(native): bound captured output carried by native command failures

The native command runner retains up to 64 MiB of output because
successful analysis needs it complete, and failureCapture copied all of
it into the failure. An inspect_plist whose plutil output exceeded the
budget therefore returned a 67 MB resource_constraint error whose
details.captured_output.stdout repeated the whole retained output.

Keep a 64 KiB UTF-8-safe excerpt per stream in failure diagnostics,
mark them truncated, and keep the observed stdout/stderr byte totals.
Successful captures are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(native): bound codesign failures and cover excerpt cuts

translateCodeSignExitFailure wrapped an accepted nonzero codesign capture
into a NativeCommandFailure by copying both complete streams, so
inspect_signature failures still bypassed the diagnostic bound. Route it
through the shared failureCapture builder; accepted output stays
complete until the provider decides it is a failure.

Add a focused runner test for an ordinary nonzero exit with stdout and
stderr just above the excerpt size and a two-byte character straddling
the cut, and a provider test for the codesign path.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(native): build excerpt test output inside the child process

The output strings embedded in one node -e argument totalled about 133
KB, above Linux's 128 KiB per-argument limit, so spawn failed with E2BIG
on the CI runner. Generate the repeated output in the child instead.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 07:03:19 -07:00
c938f97fb2 fix(android): clean Windows workspaces after owner termination (#2440)
* fix(android): clean Windows workspaces after owner termination

* fix(android): retain Windows workspace guardian on cleanup failure

* fix(android): preserve released guardian pipe state on cleanup retry

* fix(android): normalize extended local guardian parent paths

---------

Co-authored-by: LcokHcak <268763621+LcokHcak@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 22:03:16 +08:00
c45d912c91 feat(android): expose exact DEX descriptors and direct invoke sites (#2439)
* feat(android): expose precise method references and DEX invoke sites

* fix(android): bound invoke evidence without losing reference inventory

* perf(android): skip invoke admission for class references

---------

Co-authored-by: LcokHcak <268763621+LcokHcak@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 22:02:47 +08:00
Kaoru0822andClaude Opus 5.5 ce748be2f3 fix(process): order interaction differences by their dispatch time (#2739)
* fix(process): order interaction differences by their dispatch time

first_divergence ranks candidate differences by the earliest recorded
time of the differing observations, but eventTime read only `at_ms`.
Interaction events record their time as `dispatched_at_ms`
(and `scheduled_at_ms`), so a changed interaction always ranked as
untimed. An interaction outcome that differed at 6 ms therefore lost to
a process sample that differed at 500 ms.

Give each comparison dimension its recorded time field. Interactions use
the dispatch time, when their outcome was observed; the other
dimensions keep `at_ms`. An earlier process difference still wins.

Fixes #2645

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(process): require ordered interaction dispatch times in captures

Comparison ranks a changed interaction by its dispatch time, while
firstCollectionDivergence returns the first differing interaction by
sequence. The capture parser checked only that interaction sequences are
contiguous, so an imported capture whose dispatch times decrease could
make that first-by-sequence difference later than another differing
interaction.

Interactions are dispatched in sequence and their times come from the
monotonic capture clock, so require non-decreasing dispatch times at
parsing, as frames, rendered frames, process samples and filesystem
checkpoints already require for their timestamps.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 07:02:28 -07:00
Liu Jieandmaxazure 173da755cd fix(reconstruction): reject contradictory function comparison status (#2798)
* fix(reconstruction): reject contradictory function comparison status

* test(reconstruction): cover imported function match precedence

---------

Co-authored-by: maxazure <405711+maxazure@users.noreply.github.com>
2026-10-13 22:01:41 +08:00
morlutoandmorluto 1eca600515 fix(apktool): resolve Windows PATH launchers before invocation (#2802)
* fix(apktool): resolve Windows PATH launchers before invocation

Resolve the default launcher through the selected PATH/PATHEXT before choosing native process creation or the existing conservative cmd.exe wrapper. Reuse the resulting selection and one canonical environment for the version probe and decode.

Follow-up to the release #1693 review of #1777. Source inspected and formatted; tests, builds, check reruns and real Windows Apktool execution were skipped at maintainer request.

* fix(apktool): bound lookup and retain only selected environment

Source inspection and targeted formatting only. Tests, builds, lint, typechecks and provider workloads were not run at the user's explicit request.

* fix(apktool): use supported timeout diagnostics

Retain lookup phase through AnalysisErrorOptions diagnostics while preserving the operation and timeout identity. Source inspection and targeted formatting only; tests, checks, builds and provider workloads were skipped at the user request.

---------

Co-authored-by: morluto <morluto@users.noreply.github.com>
2026-10-13 22:00:47 +08:00
Kaoru0822andClaude Opus 5.5 ae9a7f34e1 fix(process): report no remaining time after a provider deadline fires (#2720)
Node timers can fire while performance.now() is still a fraction of a
millisecond short of the monotonic deadline. ProviderStartupDeadline then
reported interruption "timeout" and an aborted signal while remainingMs()
returned ceil(0.x) = 1, so polling loops guarded by remainingMs() > 0
(Hopper and Ghidra startup) could run past an elapsed deadline, and the
real-timer test "aborts a bounded wait when its real monotonic deadline
expires" failed intermittently in CI (expected 1 to be 0).

Once the deadline's own timer has classified a timeout, report zero
remaining time. A deterministic regression test fires the timer with the
monotonic clock 0.4 ms short of the deadline.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 06:59:57 -07:00
Kaoru0822andClaude Opus 5.5 96d1f0f3fb fix(cli): refuse scenario steps on passive native UI observation (#2712)
observe-native-ui and capture-native-ui-scenario were registered in one
loop with a shared option schema and description. The passive command
therefore advertised `--steps` ("Observe or run a scenario"), accepted
any value, and then dispatched observe_native_ui without it, so a
caller who selected the wrong command never learned that no scenario ran.

Register each command against its own contract. Observation declares
only the passive scope options, so `--steps` is rejected as an unknown
flag; the scenario command keeps its JSON step decoding and validation.
Each command now has its own description.

Fixes #2674

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 06:59:33 -07:00
vtxandmorluto beca34f522 fix(ida): report missing tools when a compatibility profile mismatches (#2307)
A profile mismatch reached the caller as a bare AnalysisProtocolError,
which projects to `unreadable_output` with a generic message. The error
already named every missing tool, but none of that detail survived, so
the only way to identify the cause was to log the JSON-RPC traffic and
notice the exchange stopping after `tools/list`.

Throw a ProviderAdapterError instead, carrying the mode, the expected
profile and the missing tool names through the diagnostics channel that
already surfaces in `details`. Document which upstream versions each
profile accepts, since upstream `main` now serves neither.

Refs #2301

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 21:58:36 +08:00
48a683ac8c feat(evm): inspect lossless Solidity terminal CBOR metadata (#2790)
feat(evm): retain lossless terminal CBOR metadata

Refs morluto/rea#2388

Co-authored-by: Matheus Breguêz <matbrgz@gmail.com>
Co-authored-by: Codex <noreply@openai.com>
2026-10-13 21:55:30 +08:00
a3daf8a07b fix(ci): run the Windows native lane with its process boundary fixture (#2336)
* fix(ci): run the Windows native lane with its process boundary fixture

`verify-windows-native.mjs` takes the boundary executable as its second
argument and skips every observation that needs it when the argument is
absent. CI passed no argument, so the breakaway denial, caller and child
token comparison, Windows environment semantics and in-place ancestor
reparse attempt have never run there; they only ran for an operator who
built the fixture by hand, which `native/windows/README.md` described as
optional.

The fixture is an ordinary Windows executable independent of the addon,
so the lane that already installs MinGW to cross-build the artifact
cross-builds it too and uploads it beside the existing inspection
fixture. The Windows job downloads it and passes its path, matching how
that inspection fixture already crosses the same job boundary.

No observation, assertion or threshold changes; this only stops four of
them from being silently skipped.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(ci): pass the process boundary fixture in the real Ghidra Windows lane

`real-ghidra-windows.yml` also runs the native lane without the boundary
executable, so its proof run skipped the same breakaway, caller and child
token, environment semantics and ancestor reparse observations. Its
artifact job already installs MinGW, so it cross-builds and uploads the
fixture the same way, and the self-hosted Windows job downloads it and
passes its path.

This makes the README's claim about both Windows workflows true rather
than narrowing it to one lane.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(process): report no remaining time after a provider deadline fires

Node timers can fire while performance.now() is still a fraction of a
millisecond short of the monotonic deadline. ProviderStartupDeadline then
reported interruption "timeout" and an aborted signal while remainingMs()
returned ceil(0.x) = 1, so polling loops guarded by remainingMs() > 0
(Hopper and Ghidra startup) could run past an elapsed deadline, and the
real-timer test "aborts a bounded wait when its real monotonic deadline
expires" failed intermittently in CI (expected 1 to be 0).

Once the deadline's own timer has classified a timeout, report zero
remaining time. A deterministic regression test fires the timer with the
monotonic clock 0.4 ms short of the deadline.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit dfdaa0ca74)

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: morluto <morluto@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-13 21:55:17 +08:00
9680da76c8 perf(evidence): reuse sealed bundle reads in compare and export (#2715)
* perf(evidence): reuse sealed bundle reads in compare and export

`rea compare`, the compare_bundles MCP tool and `rea evidence-export`
read bundles through the mutable reader, so each consumer
re-authenticated every record again: compareBundles parsed both bundles
a second time and bundleDigest's serializeEvidenceBundle a third time;
export's writer parsed the source once more before serializing.

These consumers only read the bundle, so read it through the existing
sealed reader (readImmutableEvidenceBundle). Later parses hit the
immutable bundle cache. Ordinary mutable reads, kept deliberately by
#1721, are unchanged. Output is byte-identical.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(process): report no remaining time after a provider deadline fires

Node timers can fire while performance.now() is still a fraction of a
millisecond short of the monotonic deadline. ProviderStartupDeadline then
reported interruption "timeout" and an aborted signal while remainingMs()
returned ceil(0.x) = 1, so polling loops guarded by remainingMs() > 0
(Hopper and Ghidra startup) could run past an elapsed deadline, and the
real-timer test "aborts a bounded wait when its real monotonic deadline
expires" failed intermittently in CI (expected 1 to be 0).

Once the deadline's own timer has classified a timeout, report zero
remaining time. A deterministic regression test fires the timer with the
monotonic clock 0.4 ms short of the deadline.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 21:50:25 +08:00
Kaoru0822andClaude Opus 5.5 46f51ee6cc fix(flutter): name a missing or non-file APK selection (#2727)
* fix(flutter): name a missing or non-file APK selection

identify_flutter_build and inspect_dart_aot reported a missing path or a
directory as "The APK could not be read as a package: <path>: ENOENT ...",
which blames the package bytes for a selection mistake. Report
"Selected APK does not exist" for ENOENT/ENOTDIR and "Selected APK is not
a regular file" for a non-regular selection, matching the SQLite
inspector's wording. The issue path and invalid_format reason are
unchanged; package format failures keep their existing message.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(filesystem): classify unopenable non-regular selections at admission

A Unix socket cannot be opened at all: open() fails with ENXIO on Linux
and EOPNOTSUPP on macOS (surfaced by libuv as "Unknown system error
-102") before openRegularFile can stat the descriptor, so such a
selection escaped NonRegularFileReadError and the Flutter provider still
blamed the package bytes.

When open fails, classify the path by its own metadata (lstat when
symlinks are rejected, stat otherwise): an existing non-regular file
becomes NonRegularFileReadError. Missing paths, permission failures,
regular files, and the O_NOFOLLOW ELOOP rejection keep their original
errors. The Flutter selection test now covers a real Unix socket.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(filesystem): reclassify only unopenable file-type errors at admission

Checking the path's metadata after any open failure turned EACCES on an
unreadable directory into NonRegularFileReadError, so callers reported
invalid input instead of access_denied. Reclassify only errors that mean
the file type cannot be opened (ENXIO, EOPNOTSUPP, ENOTSUP, matched by
errno because libuv has no name for macOS EOPNOTSUPP 102); permission,
symlink-rejection and resource failures keep their original errors.

Run the Unix socket selection check in its own test, skipped on Windows
where listen() takes named pipes, and keep the missing-path and
directory checks cross-platform.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(filesystem): keep cancellation that arrives during the non-regular probe

A caller abort while the post-open metadata probe ran was replaced by
NonRegularFileReadError, so callers such as parseBinaryTarget reported an
input-format error instead of cancellation. Recheck the signal before
substituting the non-regular classification.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 21:49:23 +08:00
yasshmittalandClaude Opus 5.5 c22d7cd756 fix(browser): keep malformed Content-Length declarations unknown (#2773)
* fix(browser): keep malformed Content-Length declarations unknown

Response metadata normalized Content-Length with Number(), truncation and
clamping, so "-5", "0x10", "1e3" and "+12" became observed lengths 0, 16,
1000 and 12. RFC 9110 section 8.6 defines the field as 1*DIGIT; any other
spelling declares no length. Parse only decimal digits within the safe
integer range and keep every other value null.

Fixes #2596

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(browser): cover the largest accepted Content-Length

Pin the safe-integer boundary beside the overflow case so a future change
cannot reject the largest representable declared length unnoticed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 21:48:55 +08:00
4c79127fcb fix(sqlite): classify a missing database path as an invalid value (#2775)
* fix(sqlite): classify a missing database path as an invalid value

The snapshot reported an absent selected database as an artifact path
failure, which the adapter projects as invalid_format alongside malformed
paths and content. The snapshot owns the absence observation, so it now
returns an invalid_value input issue on path directly, matching the
adapter's existing ENOENT/ENOTDIR classification.

Fixes #2614

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(sqlite): classify a missing database path as an invalid value

The snapshot reported an absent selected database as an artifact path
failure, which the adapter projects as invalid_format alongside malformed
paths and content. The snapshot owns the absence observation, so it now
returns an invalid_value input issue on path directly, matching the
adapter's existing ENOENT/ENOTDIR classification.

Fixes #2614

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 21:48:39 +08:00
Naimur Rezaandmorluto c8e85b962a fix(javascript): recover child-process options without args (#2781)
* docs: celebrate 180,000 GitHub stars 🎉

* fix(javascript): recover child-process options without args

* fix(javascript): preserve execFile callback overload options

Choose execFile options by the second argument object shape rather than total argument count, preserving callback overloads.

Source inspection and git diff --check; tests/builds/typechecks/CI reruns skipped at explicit user request.

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 21:46:27 +08:00
jbernous fc0dbe7f9d fix(server): mark retained partial-observation errors as target content (#2398)
Pass the tool contract's `returnsTargetContent` to
`toRecordedPartialErrorToolResult` in the shared Evidence handler, matching
the Electron call site.

The call site that records a retained partial observation was added without
the argument required by the untrusted target-content framing, so the
delivery neither marked the result nor counted the provenance marker in its
wire budget. The type gate failed on main with
`contractToolHandler.ts(69,38): error TS2554`, which also blocked every
compiled boundary test lane.
2026-10-13 21:46:14 +08:00
23f72141d5 fix(javascript): keep configuration setting reads out of endpoints (#2402)
* fix(javascript): keep configuration setting reads out of endpoints

Static endpoint extraction reads the first literal argument of any call
named get as an HTTP request path unless the receiver is a provable keyed
collection. VS Code extension bundles read settings through
`workspace.getConfiguration(section).get(key)`, so every setting key
became a network endpoint: the bundled Git extension's dist/main.js
reported 173 network endpoints, nearly all of them setting names such as
`autofetchPeriod` and `blame.statusBarItem.enabled`.

The lexical receiver classifier now records a `configuration` fact for a
`.get` call whose receiver is a `getConfiguration(...)` call, inline or
through a binding whose only visible write is one unconditional
initializer. Minifiers emit `let` for these bindings, so the proof relies
on the recorded writes rather than the declaration kind, as tRPC client
root resolution already does. Endpoint extraction skips non-URL literals
for that fact in both application and web bundle analysis. Reassigned,
conditional, and shadowed receivers, URL or path literals, and other
clients such as `api.get("users")` keep their endpoints.

The receiver fact union was duplicated in three modules; consumers now
import the classifier's type.

On the Git extension bundle, network endpoints drop from 173 to 7 with
unchanged analysis time (2.1 s).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(javascript): require the workspace host for configuration receivers

A bare or differently scoped `getConfiguration(...)` may be an
application's own HTTP client factory, so `.get("users")` on its result
lost its endpoint. Only `workspace.getConfiguration(...)` and
`<x>.workspace.getConfiguration(...)` now produce the configuration fact.
Minifiers keep property names, so the bundled VS Code Git extension still
reports 7 network endpoints instead of 173.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(javascript): recognize default VS Code namespace imports

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 21:45:07 +08:00
Yann Robertoandmorluto 5d12a0b4b6 test(web): cover nullable HTTP captures through CDP and CLI (#1834)
* fix(browser): preserve unknown HTTP response comparison fields

* fix(browser): retain proven request inventory changes

* fix(javascript): preserve Electron identity and avoid quadratic property scans

Share visible Electron mutations with wrapper expansion, index static object
literal properties once, and align MCP assertions with current contracts.

* fix(javascript): track writes through symbolic Electron aliases

Resolve mutation targets through the same factories, module tables, containers,
and conditional origins as call analysis. Share mutation coverage between
direct and wrapper scans while isolating origin caches and preserving exact
property paths, local rebinding semantics, and analysis resource limits.

* fix(javascript): preserve container slots and imported mutation origins

* perf(javascript): defer container mutation replay until property reads

* fix(javascript): preserve captured object identities and write order

* fix(javascript): preserve ordered mutation origins

* fix(javascript): honor property mutation constraints

* test(web): narrow PR to HTTP and CLI comparison regressions

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 21:44:08 +08:00
YsopwnableandKaoru0822 9958754f6c fix(process): preserve discovery ownership across cleanup retries (#2643)
Bind retained descriptors and runtime resources to their admitting operation until cleanup succeeds, then rebind newly admitted work. Verify failed retries, successful recovery, shutdown and CLI projection.

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-13 13:43:06 +00:00
a4b8e9ccee feat(ghidra): admit recordless legacy MIPS32 ELFs with a pinned 32-bit language (#1898)
* feat(ghidra): admit recordless legacy MIPS32 ELFs with a pinned 32-bit language

Firmware built with pre-ABI-flags GNU toolchains declares EF_MIPS_ARCH_32
with an unspecified ABI field and no PT_MIPS_ABIFLAGS record. The lane
refused them before provider selection, and Ghidra's own header
auto-selection picks a 64-bit R6 language for exactly these files.

Admit ELF32 ET_EXEC headers with EF_MIPS_ARCH_32 or EF_MIPS_ARCH_32R2,
an unspecified ABI field, EF_MIPS_ABI2 clear and no observed ABI flags
record. o32 is recorded as an inferred analysis assumption
(mips_abi_basis), floating-point ABI semantics stay unverified, and the
existing refusals (FP64, NAN2008, ASEs, machine variants, ELF64) are
unchanged. The profile pins -processor MIPS:<LE|BE>:32:default through a
provider-pinned-v1 selection the bridge-reported language_id is checked
against after import; a caller-selected REA_GHIDRA_LANGUAGE_ID still
wins. The lane identity is versioned
elf32-exec-arch32-legacy-pinned-v1.

verify:ghidra:mips gains a recordless legacy round for both byte orders
that strips the ABI record declarations from a real mips32 fixture,
checks REA's interpretation against GNU readelf, asserts the pinned
language after import and the retained assumption limitations through a
snapshot round-trip.

Fixes #1620

* fix(ghidra): require an inspected absence for the recordless MIPS lane

Address review: undefined ABI flags means the record was not inspected,
not that none exists. Only an inspected absence (null) establishes the
recordless legacy admission; a merely unobserved record keeps its
specific refusal. Also document the legacy admission and its pinned
language in the MIPS provider guide.

* fix(ghidra): tighten the recordless MIPS admission boundaries

Address review: a recordless-looking header whose ABI-record inspection
left declaration sources unexamined (carved ELF with a partial section
table and no program-header record) no longer establishes the recordless
admission; it is refused with the inspection limitation retained. An
EF_MIPS_ARCH_32 header that declares an ABI is refused rather than
relabeled through the 32R2 declared-ABI lane.

* fix(ghidra): reject EF_MIPS_ABI_ON32 from the recordless MIPS lane

Address review: bit 0x40 declares the old n32 ABI. A recordless header
carrying it was admitted with the inferred-o32 assumption even though
the header itself declares n32; the recordless shape now requires the
bit clear and refuses it with a specific constraint.

* fix(ghidra): keep recordless relocatable MIPS objects off the legacy lane

The recordless admission now requires ET_EXEC: the lane and its
verification cover executable firmware images, and a recordless ET_REL
object must stay on the declared-ABI path instead of inheriting the
inferred-o32 assumption under an executable lane id. The ABI-record
requirement paragraph is scoped to the declared-ABI lane so it no longer
contradicts the recordless admission documented above it.

* fix(ghidra): name the ET_EXEC scope for recordless objects and probe the legacy lane

A recordless relocatable object is not an incomplete inspection, so its
refusal now names the ET_EXEC scope of the legacy admission instead of
claiming unavailable ABI headers remain unknown. The recordless legacy
verifier round reads the probe pair and applies the full assertMipsProbe
oracle like the standard round, and its report keeps the independent
GNU readelf receipt beside the fixture facts.

* fix(ghidra): retain the concrete ELF32 legacy compiler pin

* fix(ghidra): bind legacy profiles to fully admitted targets

---------

Co-authored-by: BaaRai <baarai@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 21:42:38 +08:00
Lluís Ariñoandmorluto c6503edfc4 ci: pin GitHub Actions to full commit SHAs (#1901)
* ci: pin GitHub Actions to full commit SHAs

Replace every third-party `uses: owner/repo@vN` reference in
.github/workflows with the 40-character commit SHA that the tag
currently resolves to, keeping the exact release as a trailing comment.
Each pin was checked with `git ls-remote --tags`: the floating major tag
and the commented release dereference to the same commit. Local
reusable workflows are unchanged.

Re-pin reactivecircus/android-emulator-runner from the SHA of the
annotated `v2` tag object (4c44018e) to the commit it points to
(a421e438, v2.38.0); Actions resolves both, but only the commit SHA is
an immutable reference that tooling can compare with the release.

Update the Windows Ghidra workflow trust-boundary test to require
SHA-pinned setup-node and upload-artifact references instead of the
previous floating tags.

Validation: actionlint v1.7.7 (-shellcheck= -pyflakes=, as in CI), YAML
parse of all workflows, no remaining unpinned third-party `uses:`, and
the workflow boundary/conformance tests.

* style(ci): format action identity assertion

Retain formatting edits produced before the user stopped validation.

Source inspection and git diff --check only. Tests, builds and typechecks skipped by explicit user request. GitHub review writes paused globally.

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 21:38:46 +08:00
5dd2ce3125 fix(ida): let one-shot commands exit after retaining cleanup (#1841)
* fix(ida): let one-shot commands exit after retaining cleanup

When a headless IDA request outlives timeoutMs (for example idb_open on
a large binary still running auto-analysis), REA cannot prove the
worker stopped and correctly retains the upstream connection and
private workspace. The retained stdio child and its pipes stayed
referenced, so a one-shot CLI printed its cleanup_incomplete result and
then never exited, keeping the supervisor, worker and database alive.

Detach the retained connection's child process and pipes from the event
loop when cleanup fails. A long-lived MCP server can still retry
through the retained connection; a one-shot CLI exits with status 1,
and the supervisor observes end of input.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(ida): report an unverified upstream worker after failed cleanup

Upstream ida-pro-mcp spawns headless workers in their own session and
deliberately leaves them running when its supervisor exits, so neither
a retained connection nor REA exiting proves that a timed-out worker
stopped. When cleanup fails before the owned worker's release is
confirmed, the cleanup diagnostics now name its private session and
state that it may still be running until it finishes and reaches its
idle timeout. Failures after a confirmed release, or before any open
was attempted, do not report a worker.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(ida): name the inventory sessions that block worker release

When idb_close confirmed the requested worker gone but the follow-up
inventory still found a different peer on the private input, the
upstream_worker diagnostic named the released session and could
misdirect manual cleanup. Record the session ids of the inventory
entries that actually blocked release and report those; fall back to
the requested private session only when no inventory evidence exists
(an incomplete open or an unanswered request). The field becomes
session_ids, since more than one entry can block release.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(ida): keep blocker evidence and backend in cleanup diagnostics

The release diagnostic reset its observed blockers on every close, so a
retry whose inventory was unavailable fell back to the already-released
session; and it kept only session ids, describing a blocking GUI
session with detached-worker guidance.

Blocker evidence is now replaced only by a successful inventory, and
the diagnostic becomes upstream_sessions with each session's id and
backend, with guidance that distinguishes workers from GUI sessions.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(ida): stop naming a worker after its close was confirmed

When idb_close confirmed the requested worker's release but the
following inventory failed, no blocker evidence existed yet and the
diagnostic fell back to the confirmed-absent worker. After a confirmed
close the diagnostic now reports an empty session list, meaning any
remaining same-input sessions are unknown.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(ida): group cleanup diagnostic cases in their own suite

The added cases pushed the reaped-worker describe block past the
150-line limit enforced by lint:built.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(ida): keep ownership and pre-close peers in cleanup diagnostics

Blocking sessions dropped the parsed ownership fact, so an external
unowned worker looked the same as REA's own; and recovery inventory
recorded same-input peers only when the requested worker was missing,
so a peer seen beside it was lost if the following close failed.

Each reported session now carries owned (null when no inventory has
reported it), and recovery records every same-input session until a
later successful inventory replaces that evidence.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(ida): drop a confirmed worker from retained cleanup blockers

Recovery inventory now records the requested worker beside any peers,
so once idb_close confirmed its release a failing follow-up inventory
still reported it as unreleased. Remove the confirmed session from the
retained blockers at close confirmation and keep the remaining peers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(ida): retry cleanup through a detached stdio connection

The retry coverage used a recording connection whose detach() only
counts calls, so it could not show that detaching keeps a retained
connection usable. Run a real stdio supervisor in a long-lived host,
fail the first post-close inventory, and require the retry through the
same detached connection to confirm release and remove the workspace.
The test fails if detach() destroys the pipes instead of unref-ing them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(ida): close the stdio supervisor in failed retry tests

Merge main and give its new IdaNativeProtocol connection double the
required detach() method. The real-stdio retry test now closes its
connection in an unconditional teardown, so a failing assertion cannot
leave the supervisor waiting on stdin.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(ida): share the cleanup diagnostic expectation in session tests

Repeated upstream_sessions assertions and the unrelated live peer
literal pushed repository duplication over the jscpd threshold. Build
the expected diagnostic with one helper and reuse one peer constant.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(ida): distinguish observed ownership from unreported input bindings

* fix(ida): keep requested-ID conflicts in recovery cleanup

Recovery inventory kept only same-input entries, unlike the startup and
post-close filters. After an explicitly rejected open, an entry under
the requested session ID on another input was dropped, so cleanup could
close the connection, remove the private workspace and report success
while that session stayed registered. Match the requested ID as well so
the entry is retained as a blocker and cleanup stays incomplete.

Also split the cleanup diagnostics suite under the lint:built function
length limit, and make two startup-inventory fixtures pass a defined
session id so the test file typechecks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Xingchuan Ma <xingchuan.ma@msn.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: morluto <morluto@users.noreply.github.com>
2026-10-13 21:36:52 +08:00
Jose Camargo 811bd9f635 docs(contributing): explain Windows native prerequisites for source checkouts (#2295) 2026-10-13 21:34:59 +08:00
Danish Rafie Ekaputra 9e019ad27d feat(clients): register Factory Droid during setup (#2309) 2026-10-13 21:34:38 +08:00
117bd23dd5 test(clients): verify Qwen Code on native Windows (#2103)
Co-authored-by: AICodeLion <190163814+AICodeLion@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 21:27:45 +08:00
Saim Shafique 6637161e02 fix(ghidra): warn when callee/caller reads stop at a truncated function body (#2360)
procedure_callees and procedure_callers returned a bare address list, so an
inferred no-return call that truncates Ghidra's function body silently dropped
real edges (fixes #2326). Wrap both views in the function-observation envelope
with noReturnCallLimitations, matching procedure_info, procedure_assembly,
procedure_pseudo_code, read_function_instructions, and analyze_function
(precedent: 69a8b3a95). Version the boundary-observation contract to
ghidra-terminal-call-limitations-v2 so older snapshots cannot replay
unqualified results.

Validation: red->green unit test for the envelope requirement, MCP boundary
evidence test extended for both views (limitations retained through Evidence
and snapshot replay), 8 harness test files + ghidra behavior tests green,
check:fast, docs:check, verify:test-discovery green. Real Ghidra host lane
unavailable here (no Ghidra installed); Java change mirrors the existing
procedureInfo call pattern.
2026-10-13 21:26:57 +08:00
To3akaRinandmorluto dea770d5d9 fix(javascript): retain getter receivers through local aliases (#2778)
* fix(javascript): retain getter receivers through local aliases

* fix(javascript): resolve projected private getter receivers

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 21:26:13 +08:00
Joshwong1908andmorluto 48b92e1be1 fix(go): classify JVM class files instead of universal Mach-O for CAFEBABE bytes (#2458)
* test(go): pin JVM class files as unsupported Go images

The regression fails on main: readGoBinaryImage(javaClassFile()) reports
"Universal/fat Mach-O containers require selecting a thin slice" for a
JVM class file whose bytes 4..7 are minor/major version, not nfat_arch.

* fix(go): classify JVM class files instead of universal Mach-O

Route the four FAT magics through the shared readFatContainerHeader and
classifyFatContainerIdentity facts added for the same disambiguation in
the artifact and target lanes (921bb9575). Keep the thin-slice failure
only when the container identity is confirmed Mach-O; a JVM class file
now falls through to the generic only-ELF/PE/thin-Mach-O failure.

* test(go): pin universal Mach-O thin-slice failure with FAT fixtures

Cover the kept behavior with the repository's own fatImage/machoImage
builders (narrow and FAT64 tables) so the regression also proves real
universal containers keep the slice-selection failure.

* test(go): cover little-endian FAT magics in the universal container control

* fix(go): require positive universal Mach-O identity

Require positive Mach-O identity before reporting a universal thin-slice remedy; malformed FAT-shaped bytes are not proof. Add the JVM constant-pool CPU lookalike to the existing fixture control.

Source inspection and git diff --check; tests/builds/typechecks/CI reruns skipped at explicit user request.

* fix(go): retain malformed FAT header diagnostics

Preserve the owning FAT header parser reason for malformed headers while requiring positive Mach-O proof before the universal-slice remedy.

Source inspection and git diff --check only. Tests, builds and typechecks skipped by explicit user request. GitHub review writes paused globally.

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 21:25:32 +08:00
Luisandmorluto 003707f712 fix(verification): align Linux Hopper setup lane with current contracts (#1942)
* fix(verification): align Linux Hopper setup lane with current contracts

* fix(verification): retain orphan checks on clean Linux runners

* fix(verification): detect Hopper through alternate launcher paths

* fix(verification): report identified Hopper leaks before proc errors

* fix(verification): distinguish Hopper string recognition from byte fidelity

* fix(verification): report full literal decoding coverage explicitly

* test(verification): require a clean host before orphan checks

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 21:24:40 +08:00
dd4bfed614 fix(javascript): keep ASAR-named files without an ASAR header opaque (#2347)
Inventory expands an ASAR-named filesystem file only when its bytes are
an ASAR. JavaScript reconstruction expanded it on the suffix alone, so
a directory holding any other file named *.asar failed the whole
analysis with an invalid ASAR header.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 21:23:58 +08:00
xingzhou 84ec40f1af fix(browser): distinguish SVG links from HTML XLink attributes (#2480)
* fix(browser): respect SVG desc and title HTML integration points

* fix(browser): preserve moved SVG XLink destinations

* fix(browser): bound concurrent SVG identity reads
2026-10-13 21:23:33 +08:00
5eb296649d fix(native): refuse inspect_signature on non-Mach-O targets (#2262)
* fix(native): refuse inspect_signature on non-Mach-O targets

This provider's signature inspection covers Mach-O code signatures only.
On a xar pkg, codesign's "code object is not signed" verdict cannot see
the archive-level signature layer, so a signed or invalidly-signed package
was reported as signed:false with "Artifact is not signed." Match the
existing inspect_macho/list_architectures gate and report
capability_unavailable instead of an unsigned claim for a layer that was
never evaluated.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* test(native): scope the signature-gate comment to this provider

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* fix(native): align signature capability discovery with Mach-O support

Bind inspect_signature availability to Mach-O targets in capability discovery and align the public tool description.

Source inspection and git diff --check; tests/builds/typechecks/CI reruns skipped at explicit user request.

---------

Co-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 21:22:42 +08:00
吴佳浩Albenand吴佳浩 cae7620b77 fix(apple): report a recognized connector that cannot name its owner (#2484)
* fix(apple): report a connector that cannot name its owner as an omission

A connector is recognized by class name and then dropped by an early
`continue` when its owner cannot be resolved. Nothing recorded the drop, so
connection coverage derived `complete` with `examined: 0` and `omitted: 0`
for a connector that was present, recognized and discarded.

Both producers of that coverage contract are affected and are changed
together, because they share the facet and the reason value:

- the keyed-archive parser records the drop as `unresolvedConnections` and
  reports it through the `connections:<path>` facet, the document
  `connection_count` and the graph omission total;
- the NIBArchive projection in the artifact adapter recorded nothing at all,
  and the projected dictionary carried no trace of the dropped connector for
  a downstream consumer to find. Its count is taken where the connector is
  discarded and plumbed through the same per-document omission channel that
  already carries incomplete hierarchies, so the reason follows the cause.

An unresolved source cannot be represented as a connection at all, because
connection identity requires a non-empty source; that is unlike an unresolved
destination, which the graph already retains as an unresolved edge.
`connection_limit_reached` stays reserved for connections dropped by the hard
cap and `serialized_view_hierarchy_incomplete` for hierarchy omissions.

`connection_count` also includes the unresolved connectors. That field already
counts connectors the parser recognized but could not emit, because
connections dropped by the hard cap are counted there too, so unresolved
connectors follow the existing rule rather than a new one.

Verified with 98 focused Interface Builder and catalog tests, check:fast and
verify:test-discovery on macOS arm64, and through the public CLI: a rooted
archive holding one ownerless UIRuntimeOutletConnection reported complete with
examined 0 and omitted 0 before this change and reports partial with the reason
connection_unresolved and omitted 1 after it.

* fix(apple): count ownerless NIBArchive connectors and keep the limit reason

Greptile found two defects in the NIBArchive half of this change.

The document summary was left unchanged, so the same recognized connector
counted as one in a keyed archive and zero in a NIBArchive. The dictionary
parser only sees connectors that were projected, so the omission recorded
where the connector was dropped is added to the matching document here.

The facet reason was overwritten unconditionally, so a NIBArchive holding both
a connection dropped by the limit and an ownerless connector reported
connection_unresolved and lost the cause a higher limit could recover. The
existing reason is kept when the graph already named one, which is the
precedence the keyed-archive producer applies.

Verified with `npm run test:focused` over the bundle suite (42 passed, 1
skipped), `npm run lint` (0 errors) and `npm run check:fast` on macOS arm64.
Reverting the reason precedence alone fails only the limit case; reverting the
document count fails the count assertion in both cases.

The new case moved into its own describe block because the enclosing callback
crossed the repository's max-lines-per-function bound otherwise.

---------

Co-authored-by: 吴佳浩 <wujiahao@supremind.com>
2026-10-13 21:19:17 +08:00
The one -Niuandmorluto c44d17fd3c fix(pwntools): let callers widen the artifact input bound (#2486)
* fix(pwntools): let callers widen the artifact input bound

The pwntools adapters rejected every artifact over 32 MiB before any
analysis ran, and nothing in configuration, environment, CLI or MCP
could raise that bound, so real recorded core dumps stayed unanalyzable.

Resolve the bound from REA_PWNTOOLS_MAX_INPUT_BYTES in front of the
shipped default. An absent value keeps 32 MiB, and a value outside
1..1073741824 bytes is refused as unusable provider configuration before
the artifact is read instead of falling back silently.

* fix(pwntools): name the bound variable in its own refusal

An unusable REA_PWNTOOLS_MAX_INPUT_BYTES travelled through the shared
engine-absence helper, which always stamped configuration_key
REA_PWNTOOLS_PYTHON, so a caller following that field was pointed at the
Python executable instead of the bound it had just set. The guide also
described the refusal as an invalid request while the decoder reports
provider_unavailable.

Let the failure context carry the key under review and pass the bound's
own name at that call site, then state the code callers actually receive
in the guide. Assert the key on the projected rejection so the engine
default and the bound override each stay attributed to the right setting.

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 21:18:12 +08:00
FengCi 59d33a2733 fix(native): keep lipo path echoes out of FAT records (#2605)
* fix(native): keep lipo path echoes out of FAT records

Parse FAT records only after separating the exact echoed operand path. Pass the same lipo operand at all three consumers and cover multiline paths through parser, slice-reader and compiled CLI regressions.

* fix(native): use parsed lipo framing for container offsets

* test(native): use explicit FAT fixtures for CLI regressions
2026-10-13 21:16:49 +08:00
Kaoru0822andClaude Opus 5.5 11268fe241 perf(artifacts): share sealed inventory and report in inspect_artifact (#2690)
inspect_artifact parsed a fresh inventory and inspection report but left
both mutable, so each later Evidence boundary cloned and re-digested the
complete payload: createArtifactInspection re-authenticated the nested
inventory Evidence, createAnalysisExecution re-parsed the report, and
recordEvidence re-authenticated the outer Evidence.

Seal both exclusively owned values with freezeJsonSnapshot so those
boundaries take the existing immutable-snapshot path. Output is
byte-identical.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 21:16:29 +08:00
Angelo PerezandClaude Fable 5.1 e46bed73f1 fix(managed): decode the CIL no. prefix (#2241)
The CIL opcode table held five of the six ECMA-335 prefix opcodes and
omitted no. (FE 19 <unsigned int8>, III.2.2). A method body using it was
reported as malformed with "Unsupported CIL opcode 0xfe19", losing its
instruction counts and normalized IL digest.

Decode it with its one-byte check mask and treat it as a prefix, so it
shares an instruction boundary with the opcode it modifies.


Claude-Session: https://claude.ai/code/session_012pRGskbPGjT444CgJfuhhj

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-13 21:16:00 +08:00
xin zhuangandmorluto ece15ed6a1 fix(electron): identify each unresolved scenario path (#2279)
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 21:15:33 +08:00
Kaoru0822andClaude Opus 5.5 97dc6581f4 fix(dotnet): name the instruction when a CIL operand leaves the method body (#2697)
The decoder already reports "Instruction X at IL offset N leaves method
body" when an operand's computed end passes the IL body, but only after
the operand was read. Operand readers use Buffer reads, so an operand
cut off by the body end threw a RangeError first. The catch-all stored
Node's message ("Attempt to access memory outside buffer bounds" or an
offset RangeError) as body.issue, dropping the instruction and offset.
A body ending in a lone 0xFE two-byte opcode prefix failed the same way.

Treat a RangeError from the operand read as the operand leaving the body
so the existing diagnostic applies, and check the second opcode byte
before reading it. Malformed status, partial coverage and earlier
decoded instructions are unchanged.

Fixes #2677

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 06:14:31 -07:00
TonyWuandmorluto cdc41fc86c fix(managed): classify ReadyToRun images without ILONLY as modern .NET (#2075)
* fix(managed): classify ReadyToRun images without ILONLY as modern .NET

crossgen2 clears COMIMAGE_FLAGS_ILONLY and sets IL_LIBRARY on ReadyToRun output. inspect_managed_artifact checked the clear ILONLY flag before the ReadyToRun signature, so real ReadyToRun assemblies were reported as runtime_family mixed-clr-native and implementation cpp-cli-mixed, and cil-and-ready-to-run was unreachable for crossgen2 output. Check the ReadyToRun signature first; a clear ILONLY flag without it still reports mixed CLR/native code.

* test(managed): exhaust capacity through inspected assembly references

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 21:13:18 +08:00
记得吃中饭 9c14999efd feat(ghidra): support raw NMOS 6502 analysis (#2079)
* feat(ghidra): admit raw NMOS 6502 analysis

* test(ghidra): require verified POSIX verifier cleanup

* test(ghidra): verify 6502 low-memory raw imports

* docs(skill): document raw NMOS 6502 support

* fix(ghidra): preserve requested Windows raw source paths

* docs(ghidra): include raw 6502 in Windows installation scope

* test(ghidra): verify Windows raw junction rejection through CLI and MCP

* fix(validation): make fixtures portable and preserve explicit Codex home

* fix(ghidra): attest raw mapping state and address space

* fix(ghidra): align diagnostics and regression groups

* fix(ghidra): bind raw attestation to language address space

* chore(ghidra): avoid verifier insertion conflict

* docs(ghidra): align linked raw admission guides

* fix(ghidra): retain 6502 verifier workspace on failure

* test(ghidra): isolate Windows authority doctor fixture

* fix(binary): re-admit Windows raw paths on session reopen

* test(binary): require fresh provider execution after path change

* Constrain 6502 to raw lifecycle and snapshot targets

* Bypass saved reads for unadmitted Windows paths

* Guard direct CLI snapshot replay on Windows paths

* fix(snapshot): gate Windows Ghidra replay on native admission

* fix(snapshot): constrain nested 6502 subjects to raw images

* test: make dyld slice fixtures portable
2026-10-13 21:13:01 +08:00
Aravind Raoandmorluto 352c46aa75 fix(javascript): keep CommonJS require file URLs unresolved (#2077)
* fix(javascript): keep CommonJS require file URLs unresolved

require("file:///dep.cjs") was reported as a resolved dependency of
the inventoried dep.cjs although Node rejects the string with
MODULE_NOT_FOUND. The shared module-specifier resolver decoded file URLs
before the declared CommonJS loader semantics.

File-URL decoding now applies only to non-require module specifiers, so a
CommonJS file URL reports external with its declared string and source
evidence preserved. ESM file URLs and ordinary relative require paths
keep their established lookup.

* test(javascript): consolidate CommonJS file URL public workflow coverage

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 21:12:50 +08:00
91830f1e0e feat(managed): name the declaring type of call and field targets (#2163)
* feat(managed): name the declaring type of call and field targets

Call edges named external targets by member name alone (Join, ToJson,
WriteAllText), and method-spec edges had no name at all, so an analyst could
not tell which library a method calls or find every use of a type without a
decompiler. Field accesses through MemberRefs had no name either.

Name each TypeDef, TypeRef, and generic-instantiation TypeSpec once, in the
namespace-qualified form TypeDef full_name uses, and add member_refs
parent_name, call_edges target_declaring_type, and field_accesses
field_declaring_type. A method-spec target reports the generic method it
instantiates. Fields, member references, methods, and method specs now
resolve through one token-keyed MemberCore lookup instead of three maps.
Evidence recorded before these fields reads them as null.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(managed): validate spec blobs and member kinds before naming targets

Address review findings on declaring-type naming:

- A generic instantiation must name a TypeDef or TypeRef, so a TypeSpec
  base is reported as malformed instead of followed. Naming no longer
  recurses.
- TypeSpec and MethodSpec instantiation blobs are decoded with the
  member-signature grammar. Malformed blobs and out-of-range TypeDef rows
  are reported against their owning row and make coverage partial.
- A MethodSpec reads both columns. An invalid Method coded index is
  reported; a readable Method keeps its name beside a malformed
  instantiation.
- MemberCore carries its signature kind, so a field operand is named only
  by a field or field-signature MemberRef. This replaces the token-table
  guard.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(managed): validate MethodSpec arity and report unsupported spec blobs

- A MethodSpec now checks that its method signature is generic and that
  the instantiation supplies as many type arguments as it declares. A
  non-generic target, a field reference, or a count mismatch is reported
  as invalid-row; the readable method keeps its name.
- Unsupported TypeSpec and MethodSpec forms raise an owner-scoped
  unsupported-signature issue instead of reading as an intentional
  unknown.
- MethodSpec reading moves to ManagedMethodSpecs.ts, and its tests get
  their own describe block.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(managed): reject non-method signature headers and invalid TypeRef scopes

The method signature header reader now owns the calling-convention rule
that only FNPTR applied: FIELD, LOCAL_SIG, PROPERTY, and GENERICINST
headers, the reserved bit, and EXPLICITTHIS without HASTHIS are malformed.
A MethodSpec whose method has a PROPERTY header is no longer accepted as a
generic method, and member signatures with such headers no longer decode
as methods.

The type-name lookup validates a TypeRef's ResolutionScope coded index and
reports an out-of-range scope as invalid-row, keeping the readable name.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(managed): hold member rows to MethodDefSig and FieldSig headers

Method signature headers are now checked in context. MethodDef and
MemberRef rows accept only the MethodDefSig/MethodRefSig headers: DEFAULT,
VARARG, or GENERIC with DEFAULT. FNPTR keeps the unmanaged conventions
compilers emit for function pointers. A field signature must be exactly
FIELD, so a flagged FIELD header is malformed and never names a field.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(managed): accept MethodSpec arity only from a fully decoded signature

The arity check read only the target's signature header, so a truncated
generic method signature passed. Under type selection a MethodDef target
outside the selection is never decoded elsewhere, so coverage stayed
complete. The arity now comes from a transient full decode, and only a
decoded method signature supplies it. signature() keeps its raw length
and digest around the shared decodeSignature.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* perf(managed): decode each MethodSpec target signature once

edgeTargets caches the generic arity, including a failed decode, by
target method token, so many instantiations of one generic method share
a single signature decode. Unity.InputSystem has 472 distinct MethodSpecs
over about 133 target methods.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(managed): validate spec type operands and transitive reference scopes

* fix(managed): retain readable names when a scope row is unreadable

* test(managed): pin owner-scoped spec operand and scope-chain checks

e59217c83 validates spec type operands inside specSignature, so a
malformed generic TypeSpec base is now invalid-row under its owning
TypeSpec row; the three stale expectations follow that. New cases cover
a TypeSpec argument and a MethodSpec argument that name a missing TypeRef,
and a TypeRef whose ResolutionScope names itself. The scope cases move to
their own describe block.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(managed): keep field targets from naming MethodSpecs

A MethodSpec over a field-signature MemberRef inherited kind "field", so
an ldsfld on that MethodSpec published a field name and declaring type
for malformed IL. A MethodSpec now borrows its target's name only when
the target is not a field; the arity check still reports it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(managed): reuse owner-decoded signatures for MethodSpec arity

MemberRef signatures, and MethodDef signatures in the type selection, are
already fully decoded, but the MethodSpec arity check decoded them again
and admitted a second transient allocation, which could exhaust the
budget. MemberCore now carries the owner's genericArity, and only a target
nothing decoded, such as an unselected MethodDef, falls back to the raw
signature. signatureGenericArity is the one rule for both paths.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 21:12:15 +08:00
Luis Arayaandmorluto df04f99df0 fix(readiness): deduplicate stage-level missing-evidence findings (#2203)
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 21:10:56 +08:00
yasshmittalandClaude Opus 5.5 d2e16b2a49 fix(sqlite): attribute unknown column selections to columns (#2774)
Every worker "selection" rejection was projected onto the table argument,
including an exact column name that does not exist in an existing table.
Give column rejections their own worker reason and map each input reason
to the caller argument it requires correcting. Table rejections keep the
table path.

Fixes #2613

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 21:03:14 +08:00
Aliciawqueandmorluto 928b4d5fe4 fix(plist): decode formatted XML dictionary keys (#1843)
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 20:59:02 +08:00
jhuang-tw 4ea24f9767 fix(ida): retain private input after ambiguous open (#1845)
fix(ida): retain private input after unverified open
2026-10-13 20:58:51 +08:00
JinandClaude Opus 5.5 2a61deafc6 fix(adb): report the device state when a push cannot probe the path (#2354)
A push without overwrite read any unclassified failure of its existence
probe as an existing path, so an offline or unauthorized device was
reported as "the device path already exists". With overwrite it pushed
after a probe that had not established anything. The probe's own
refusal is now returned in both cases.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 20:56:41 +08:00
Mykola Dementiev 5f8af4ee5f feat(ida): resolve containing procedures and report the admitted program as the document (#2374)
* test(mcp): expect trace_feature to report references the provider truncated

A provider that reports a capped xrefs read makes the trace truncated with a residual unknown for that target. An explicit complete report and a provider that reports nothing leave the trace as before.

* fix(mcp): report references a provider truncated in trace_feature

Executions gain an optional flag for a capped xrefs read, set by the IDA adapter from the upstream cap. trace_feature marks the trace truncated with a residual unknown for that target only when the provider reports a truncation; a provider that reports nothing keeps its previous result. The trace_feature workflow profile is revised so earlier results are not replayed.

* refactor(ida): report reference truncation whenever the runner observed an xrefs cap

* test(mcp): cover session forwarding, per-target state, native reports and failures of the trace coverage

* test(mcp): format the session forwarding test

* test(ida): expect headless IDA to resolve containing procedures from lookups and segment facts

Cover positive and negative containment, segment boundaries including addresses above 2^53, failures of the lookup and of the segment survey, cancellation, input validation and the admitted program as the only document.

* test(ida): expect feature tracing and address context to work on headless IDA

Run trace_feature, including a capped reference read, and inspect_address_context with and without an explicit document through the IDA session client.

* test(mcp): expect trace_feature availability to follow the search operations it calls

* fix(mcp): derive trace_feature availability from the search operations it calls

* feat(ida): resolve containing procedures and report the admitted program as the document

The modern headless profile resolves an address through the function lookup and, when no function contains it, tells a mapped address from an unmapped one using the database-scoped minimal survey. A survey that is missing, truncated or inconsistent fails the read. list_documents reports the single admitted program. The attached profiles do not declare either operation.

* docs(ida): describe containing procedures, the admitted document and truncated feature traces

* test(ida): verify containing procedures, the admitted document and feature tracing on real IDA

* docs(testing): list the headless IDA workflows the real lane verifies

* test(ida): make each survey failure case isolate a single defect

* test(ida): pin large addresses, segment order, document identity, attached refusals and each trace_feature prerequisite

* test(mcp): expect a repeated reference source to be resolved once in trace_feature

* fix(mcp): resolve the containing procedure of a repeated reference source once

* test(ida): trace the fixture procedure and derive probe addresses from its facts in the real lane

* test(process): format processCaptureLifecycle.test.ts after the merge

* test(ida): declare the alignment each synthetic universal slice offset satisfies

* refactor(ida): move the real lane's feature trace assertions into a helper

* test(ida): expect the real lane's feature trace check to accept references to every matched address

* fix(ida): check each trace reference against its own matched address in the real lane

* test(ida): pin that the new operations reject a document other than the admitted program

* refactor(ida): move the real lane's mapped non-function probe into a helper

* test(ida): expect the real lane's mapped data probe to skip strings inside functions

* fix(ida): try the target's strings in order for the mapped non-function probe

* test(ida): declare the real lane helper types for the verifier tests

* test(ida): expect the real lane to reject a listed string that resolves as unmapped

* fix(ida): fail the real lane when a listed string resolves as unmapped
2026-10-13 20:56:20 +08:00
Dery FerdikaandClaude Code e0bd31c18c fix(ghidra): bound the Java version probe output (#2361)
The Java version probe captured its output with maxBuffer set to Infinity, so a
chatty or hostile JVM selected as the Ghidra launcher could grow the captured
stdout/stderr without limit and exhaust REA's memory during installation
discovery. Bound the capture to 1 MiB, matching the budget already used for
other Java listings. A probe that exceeds the budget yields no observation and
is reported through the existing failed-probe path, the same one the Java check
already uses whenever the probe cannot produce an observation.

Co-authored-by: Claude Code <noreply@anthropic.com>
2026-10-13 20:55:40 +08:00
8e6f37e2a0 fix(formats): preserve high bits in remaining magic-byte checks (#2384)
* fix(formats): preserve high bits in remaining magic-byte checks

Node's "ascii" decoder masks the high bit, so byte-for-byte signature
comparisons accepted lookalikes such as CD DA ("MZ" | 0x80) or a
high-bit "bplist00"/"NIBArchive" header. ac57c88ef fixed this for the
shared DOS/MZ reader; apply the same latin1 decoding to the remaining
signature checks: NativeAOT and Windows native loader MZ/PE, binary
plist and NIBArchive detection, and PNG IHDR checks.

Display-name decodes (section and metadata stream names) are unchanged.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* style(windows): format signature admission regressions

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 20:54:37 +08:00
AurantiumandAurantium-47 43738bec57 fix(targets): retain unsupported PE machine in diagnostics (#2448)
Assisted-by: OpenAI GPT-6 <noreply@openai.com>

Co-authored-by: Aurantium-47 <280805072+Aurantium-47@users.noreply.github.com>
2026-10-13 20:52:17 +08:00
Rudy Mizrahi Celekliandmorluto 03ab521db3 fix(process): resolve POSIX backslash basenames through PATH (#2471)
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 20:51:44 +08:00
0ca70e121c fix(ghidra): pre-create the launch-support config directory (#2470)
* fix(ghidra): pre-create the launch-support config directory

Ghidra's launch support resolves its JDK before the headless run starts and
persists that choice under `<config>/ghidra/<release directory>`. Inside REA's
private runtime a child JVM cannot create those directories, so the first
Windows Ghidra query aborted at ghidra.launch.AppConfig.saveJavaHome with:

    java.io.IOException: Failed to create directory:
    ...\config\ghidra\ghidra_12.1.4_PUBLIC
        at ghidra.launch.AppConfig.saveJavaHome(AppConfig.java:145)
        at LaunchSupport.findJavaHome(LaunchSupport.java:210)

LaunchSupport then fell back to its interactive prompt, read EOF, and reported
"JDK 21+ (64-bit) could not be found and must be manually chosen!", so the
provider failed with "Ghidra exited before bridge startup" even though
`rea doctor` reported a valid Ghidra installation and JDK.

Create the release directory while this process still owns the runtime, leaving
Ghidra only a file write inside it. The release coordinate comes from the
installation's Ghidra/application.properties (application.version plus
application.release.name) with the extraction directory name as a fallback. On
Windows each level goes through the native private-runtime owner, which opens
every existing component and creates only the final one.

Verified on Windows x64 with Ghidra 12.1.4, a 64-bit Temurin JDK 21, and REA's
packaged native addon: `rea inspect <x64 PE DLL> --provider ghidra` and the
equivalent MCP `binary_overview` call both complete and report 21 segments,
1436 procedures, and 2761 strings. Windows Ghidra remains read-only P0; this
change does not extend that boundary.

* style(ghidra): format launch configuration regressions

* fix(ghidra): reuse Java Properties parsing for launch release identity

---------

Co-authored-by: AbdulAzizShanShi <441650742@QQ.COM>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 20:51:18 +08:00
Danish Rafie Ekaputraandmorluto f63674cac0 docs(browser): clarify attach coverage and Chromium setup (#2287)
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 20:44:41 +08:00
Chaoqi d3a55720fc fix(setup): keep Hermes config numbers as written (#2368)
Setup and uninstall re-printed every number in an existing Hermes
config.yaml from a JavaScript number. Hermes reads the file as YAML 1.1
with exact integers, so a Discord channel ID lost its low digits, an
octal mode such as 0600 became 600, and 0.0000001 became 1e-7, which
YAML 1.1 does not define as a float. Two IDs that round to the same
double were also rejected as duplicate keys, so setup could not
register at all.

Hermes YAML parsing and editing move to HermesConfigurationYaml. A parsed
number is written with its source spelling while that spelling still
reads as the node's value, the rule yaml's own boolean tag follows, and
two keys are duplicates only when both their value and their spelling
match. Parsed values stay JavaScript numbers.
2026-10-13 20:44:17 +08:00
JinandClaude Opus 5.5 36132d3e17 fix(managed): classify 32-bit-preferred assemblies as platform neutral (#2342)
The CLI header's 32BITREQUIRED and 32BITPREFERRED bits are one two-bit
field. Reading 32BITREQUIRED on its own reported Prefer32Bit assemblies
(flags 0x00020003) as x86-only with requires_32bit true, and left
anycpu-prefer-32 reachable only through the reserved combination.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 20:43:39 +08:00
Jack LiangandClaude Opus 5.5 1ca5fd38b4 fix(cli)!: keep a selected JSON null in --filter-output output (#2333)
createCliResultOutput rendered an explicit null selected by
--filter-output as the empty document {}, the same output it uses when
the selection has no value at all. On the streamed JSON/JSONL path,
a consumer could not distinguish a selected null from a missing field.

Write a selected null as null in streamed JSON and JSONL. This also
preserves an unfiltered top-level null. The {} fallback stays for
undefined, functions and symbols. The --full-output envelope already
carried data: null for a selected null. The --token-count path still
hands results that fit the formatted-string limit back to Incur and
prints 0 for a selected null. Other formats and Incur-owned result
paths are unchanged.

Consumers must accept JSON null for a present null and distinguish it
from a missing selection. Use --full-output if an object envelope is
required. Keeping the old shared {} representation would continue
hiding this distinction.


Claude-Session: https://claude.ai/code/session_019TTGAv6fe859ebbCLLfLHP

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 20:43:20 +08:00
Saim Shafique 84cf7791a8 fix(call-path): keep caller-only unknown coverage from blocking not_found (#2371)
* fix(call-path): keep caller-only unknown coverage from blocking not_found

build_call_path expands only callee edges, so relationship coverage that
is unknown solely because callers are unobservable (the modern headless
IDA profile) no longer marks the searched callee closure incomplete.
The caller caveat stays visible in limitations, and callee-side unknowns
still yield unknown. Fixes #2322.

* test(call-path): cover IDA-capped callees beside unknown callers

Greptile review noted the poll-timing coverage only exercised unknown
callees, leaving the partial callees IDA returns at its 500-result cap
untested. Add the combined unknown-callers/partial-callees case:
isCallerOnlyUnknownCoverage must stay false and buildCallPath must keep
search_scope.exhaustive false, so a future change cannot silently report
not_found when IDA has omitted callees.
2026-10-13 20:42:40 +08:00
yasshmittalandClaude Opus 5.5 ec4f4ddc58 fix(ghidra): read UTF-8 signed language definitions (#2772)
Buffer#toString kept a leading UTF-8 byte order mark as U+FEFF, so the
strict XML parser rejected a valid .ldefs file as content outside the root
element and aborted the whole language catalog read. XML 1.0 section 4.3.3
defines the mark as an encoding signature; decode with TextDecoder, which
consumes it. Malformed XML is still rejected.

Fixes #2533

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 20:41:28 +08:00
8823c55b0c feat(ghidra): decode Swift String literals in function dossiers (#2049)
* feat(ghidra): decode Swift String literals in function dossiers

Swift passes a String as two words. Small literals keep their UTF-8 bytes
inside those words, and immortal large literals keep a count plus a pointer
to their bytes biased by 32. Ghidra creates no data reference for either, so
analyze_function reported no strings for Swift code.

On AArch64 programs that contain Swift metadata, the Ghidra bridge now runs
constant propagation over the function and decodes register pairs where
Swift consumes a String: consecutive argument registers at calls and tail
calls, x0 and x1 at returns, and both registers of one STP. Large literals
must resolve to terminated bytes inside one initialized block that is not
executable. A pair whose registers both come from conditional selects is
skipped, because Ghidra forks each select without correlating the
condition and could otherwise pair words from different cases.

Decoded values are a derivation from the ABI layout rather than observed
references, so they appear in a new optional swift_string_literals field
with their source instruction, registers and raw words, and in a matching
inspect_analysis_view facet. referenced_strings is unchanged.

Refs #2030

* fix(ghidra): bound Swift literal reads and compare decoded strings

Large immortal literals are measured against the remaining heap before
allocation, and a failed fit stays visible as a limitation. The canonical
empty string is kept. Small and large literals now require matching
address nullability, and function comparison treats decoded literals as
string evidence without equating a missing record to an empty one.

* fix(ghidra): budget Swift literal decoding and tighten use sites

Large literals now draw on one heap budget per dossier, measured once
before decoding, instead of a separate check per literal. A literal that
does not fit is listed in a new swift_string_literal_omissions field
with its address, byte length and reason, so consumers read omissions
from structured data rather than from limitation text.

strings_names comparison checks referenced strings and names first and
reports a change there even when Swift literal coverage differs; it
returns unknown only when those facts match and literal coverage is
absent on one side or partial.

A computed jump counts as a tail call only when it has no resolved
target inside the function, so jump tables no longer qualify, and a
return requires a RETURN p-code operation, so traps no longer qualify.

Decoded literals keep control characters, which Swift strings may
contain and JSON escapes. The Swift verifier builds the literal fixture
for arm64 on any Mac host and now also covers the empty string and a
literal containing a tab.

* fix(ghidra): charge escaped JSON and follow copied selects

The Swift literal budget now charges the worst case per UTF-8 byte: UTF-16
text, six escaped JSON characters held in Gson's growing buffer and in the
returned String, and the UTF-8 bytes written to the socket. Literals made
of control characters can no longer exceed the reserved heap.

A jump counts as a tail call only when every resolved target lies outside
the function. The conditional select check follows register copies back
to their definition, so selects copied into argument registers are
recognized like selects written there directly.

strings_names reports a change when both dossiers decoded different
literals at the same use site, even when either side has omissions; it
returns unknown only when no such use site disagrees.

* fix(ghidra): bound literal records and trace selects across branches

Every Swift literal record now pays a fixed cost against the dossier
budget in addition to its text, so many empty or small literals are
bounded as well. The first record that does not fit is kept as an
omission and decoding stops there, which keeps the omission record
itself bounded; omissions now describe small or large literals.

The conditional select check searches every predecessor path for the
reaching definitions, following register copies, so selects made
before a branch into the use block are recognized.

A dossier records Swift literals and their omissions together. Coverage
reads omissions first, and strings_names compares every use site that
neither side omitted: a decoded literal is known where it was recorded,
and an absence is known only in a dossier whose decoding examined every
use site.

* fix(ghidra): stop every flow at the literal budget and route omissions

Ending one flow from the evaluator leaves other queued flows running, so
the budget now records a shared stop that every flow checks before it
examines another instruction. A record that exceeds the budget is routed
by that capacity result rather than by whether text was decoded, so a
small literal over budget becomes an omission instead of a literal.

When coverage is partial, the per site comparison keeps each literal's
representation as well as its value, matching the full strings_names
projection.

* fix(ghidra): keep unresolved Swift tail-call sites unknown

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 20:41:10 +08:00
Ysopwnable 2f3dee3fcf test(javascript): verify Windows path case and load scope (#2635)
Select the Windows curated lane for edits to the shared runtime path matcher and its workflow suite. Strengthen the existing POSIX/Windows mapping controls with matching basis, coherent target/script coordinates, caller spelling and static load-state assertions.

Follow-up to #2422; thanks to @Aurantium-47 for identifying the recorded-path grammar issue.

Verified with actual Windows 11 x64, official Node 24.18.0 and production CLI/MCP workflows using published npm 11.16.0 module Evidence. Four exact/mismatched implicit/explicit mapping cases pass with unchanged normalized hashes. The capture provider was Node Inspector; no IDA, Ghidra or Electron GUI verification is claimed.

230 focused tests, all eight CI static tasks, architecture guards and documentation checks pass after rebasing onto main fb28ec153. The unrelated release-checkpoint and dyld fixture corrections were integrated from upstream; no patches to those files are included here. Final Windows MCP replay at e6e86946d preserves all four normalized hashes, with no owned Node processes remaining. Resource probes ran serially at BelowNormal priority with one CPU and 512 MiB Node heaps; calls took 251–620 ms and the sampled process-family working-set sum peaked at 499,380,224 bytes. Source pins, provenance and measurement limits are recorded in experiments/analysis-efficiency/windows-posix-reconciliation.md.

All 34 jobs in current-head CI run 38391921571 passed, including the Windows capability/package lane, all six portable test shards, JavaScript recovery and the required aggregate. Current-head Codex and Greptile reviews are clean; both prior scope-selection findings were fixed and resolved.
2026-10-13 20:37:48 +08:00
Jeongwan Jeonandmorluto d853744969 fix: preserve process identities in pwntools cleanup errors (#2284)
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 20:28:31 +08:00
Jeongwan Jeonandmorluto fcfa5b7e88 fix(artifacts): distinguish JVM classes from universal Mach-O files (#2327)
* test(artifacts): reproduce JVM class inventory misclassification

* fix(artifacts): validate FAT declarations before classifying executables

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 20:27:38 +08:00
Shreyansh Jainandmorluto a8b50ea147 refactor(firmware): split verification into staged functions (#2713)
Refactor firmware verifier into staged functions

Restructure scripts/verify/firmware/analysis.mjs for readability without changing behavior.

- Move prerequisite checks into checkPrerequisites()
- Split the main flow into stageCli, stageMcp, stageExt4 and stageGhidra
- Extract timeouts and size limits into named constants
- Rename cli/call helpers to runCli/callTool

Checks, order of operations, error messages and env vars are unchanged.

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 20:25:58 +08:00
To3akaRin eeeb4ca379 fix(javascript): preserve container ownership for getter mutations (#2765) 2026-10-13 20:10:39 +08:00
Chaoqi f974353c32 fix(javascript): keep paths after __dirname inside the source directory (#2366)
Node's path.join does not reset at an absolute segment, so
path.join(__dirname, '/preload.js') is <dir>/preload.js. The static path
projection skipped the __dirname anchor and joined the remaining parts,
which turned that common spelling into the root path /preload.js. In an
app whose main script lives in src/, the BrowserWindow preload was then
reported not-found, or resolved to an unrelated preload.js at the artifact
root, and the utilityProcess.fork target was never resolved. String
concatenation had the same defect: path.dirname(__filename) +
'/preload.js' projected to /preload.js, and __dirname + '/preload.js'
stayed unknown.

A source directory now contributes "." to a join, so path.join(__dirname)
projects to "." like path.resolve(__dirname). A concatenation that starts
with __dirname or the source file's dirname keeps text that begins with a
separator inside that directory; other text extends the directory's own
name and stays unknown. The empty-string projection of
path.dirname(__filename), which produced the wrong concatenation, is
removed, so a bare path.dirname(__filename) is unknown like a bare
__dirname.
2026-10-13 20:10:10 +08:00
Kaoru0822andClaude Opus 5.5 cd2ad1f240 fix(artifacts): report malformed ZIP archives as format failures (#2683)
* fix(artifacts): report malformed ZIP archives as format failures

zip.js signals archive faults (missing end-of-central-directory record,
unrecognized format, overlapping members, corrupt member data,
unsupported compression) as plain Errors. ZipArtifactReader passed them
through untyped, so the artifact provider's fallback reported every one
as `io` with no detail and told the caller to "check file access".

Type these faults at the ZIP reader boundary: structural and data
corruption become `format` failures and unsupported archive features
become `unavailable`, each carrying the zip.js reason as detail. This
covers enumeration, the getData rejection, and the WritableStream abort
path that delivers corrupt-data errors first.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(artifacts): share the ZIP inventory failure assertion

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 20:07:28 +08:00
Iven Koo f90382756d fix(native): keep bounded trace edges and depth consistent (#2758) 2026-10-13 20:06:50 +08:00
Shenrui Ma 68ecdcd7a1 fix(reference): preserve directory intent when resolving source imports (#2770)
Keep directory requests from becoming unresolved or pointing at sibling files
when constructing reference-source dependency graphs. Canonicalize index
candidates and preserve directory intent while retaining ordinary lookup rules.

Constraint: Preserve current TypeScript candidate objects and exact-before-Unicode lookup
Rejected: Reorder all file probes before index probes | Changes ordinary CommonJS candidate priority
Confidence: high
Scope-risk: narrow
Tested: Public CLI regression fails on base; focused reference and resolver suites pass; npm run check
Not-tested: Full suite on refreshed base; earlier full run had 12 baseline-reproduced failures
2026-10-13 20:06:19 +08:00
Guangqi Zhang 1dd6c2dff9 fix(setup): resolve DeepSeek Harness home overrides (#2461) 2026-10-13 20:05:31 +08:00
Liu Jie c95315d2ce fix(cli): reject unterminated output filter slices (#2315) 2026-10-13 20:04:33 +08:00
Kaoru0822 fb28ec1537 test(apple): keep the same-named slice dyld fixture openable on x64 hosts (#2755)
Add a plain x86_64 slice to the dyld fixture while retaining both same-named arm64e slices and their independent environment assertions.

The original fixture was rejected by real x64 CLI admission before tracing. The contributing PR's complete Linux CI run passes all 30 jobs, including the portable suite. Codex and Greptile reviews completed without findings; no unresolved threads remain.

This restores the main test baseline needed for #2635. The change is limited to test data and preserves the original author's contribution.

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-13 19:31:18 +08:00
morluto 490a285070 ci: reuse current-run builds across provider lanes 2026-10-13 19:30:50 +08:00
morluto fa0a9b2d32 ci: observe affected-owner test plans against full results 2026-10-13 19:21:09 +08:00
morluto c8d861f976 ci: scope timing helpers to portable verification 2026-10-13 19:12:26 +08:00
morluto bd2c202dd9 fix(ci): reject incomplete verification plans 2026-10-13 19:06:31 +08:00
morluto 9f3f392eb8 ci: bound core job execution time
Limit classification to five minutes, static/build/docs/report jobs to ten minutes, and each portable shard to thirty minutes. The saved successful-job sample peaked at 392 seconds per shard and 61 seconds for other core jobs, leaving generous setup and coverage headroom. Runner queue waiting is unaffected.
2026-10-13 19:00:47 +08:00
morluto f0f09950f3 ci: finish main validation and balance complete test costs 2026-10-13 18:54:05 +08:00
morluto 3af8f847f0 ci: fix PR metadata triggers and defer expensive runner work 2026-10-13 18:38:46 +08:00
morluto 747e729914 test(release): validate only the selected preflight job
Scope the steps schema to the release job whose preflight is executed, allowing unrelated reusable-workflow jobs such as qualification. Preserve all four valid and invalid metadata execution checks.

Reproduced on main ec3db3745: 35 passed, 4 failed. After the fix: 104 release and CI boundary tests pass; check:fast and format:check pass.
2026-10-13 18:24:40 +08:00
记得吃中饭 2995ec6910 fix(ghidra): resolve file offsets in decompile (#2101)
* fix(native): expose loaded image bases in overviews

* test(ghidra): retain resources when verifier cleanup fails

* docs(ghidra): spell out file-offset address mapping

* fix(ci): separate and retry NativeAOT dependency restoration

* fix(test): resolve CI script file URLs on Windows

* fix(native): retain conflict observations and actionable CLI guidance

* fix(fixtures): keep NativeAOT retry logs credential-safe

* fix(fixtures): redact authenticated URLs at diagnostic creation

* fix(ghidra): restrict source-coordinate observations to decompilation

* fix(cli): identify conflicting file-offset selectors accurately

* fix(ghidra): bind file offsets to admitted target sources

* fix(ghidra): preserve resolved file-offset failure context

* test(ghidra): verify ELF address and file-offset round trips

* test(nativeaot): align diagnostic regressions with fixture host support
2026-10-13 18:23:17 +08:00
Yijie Lu ec3db37457 fix(native): preserve literal line breaks in signing identifiers (#2107)
* fix(native): preserve literal line breaks in signing identifiers

* fix(native): infer codesign framing without a matching path
2026-10-13 18:22:34 +08:00
N0zoM1z0 f1f5582ce8 fix(website): clarify translated evidence and runtime observations 2026-10-13 18:21:09 +08:00
N0zoM1z0 3d78a57043 docs(website): publish model essay in all supported languages 2026-10-13 18:21:09 +08:00
N0zoM1z0 b06751b703 docs(website): simplify reconstruction evidence in model essay
Explain each case through the obstacle, useful hypothesis and verified result. Remove byte totals, relocation counts and trial counts from the main narrative; preserve commit references and the constant-value example that explains the oracle defect. Make the experiment table and compiler terminology easier to follow.

Validation: website.py check, formatting, preserved source links, and eight article layout checks across both preview paths and light/dark themes.
2026-10-13 18:21:09 +08:00
N0zoM1z0 806ff57567 docs(website): use straight quotes in model essay
Normalize the three quoted phrases to straight English quotation marks. Website checks and formatting pass; the local preview serves the revised punctuation.
2026-10-13 18:21:09 +08:00
N0zoM1z0 ca07373e44 docs(website): improve model essay transitions and narrative flow
Connect the reconstruction cases to the questions about specialization, retrieval and feedback-driven search. Replace fragmented sentence sequences with explicit causal links and smooth the progression into oracle design and the proposed comparison.

Validation: website.py check, formatting, preserved source links and evidence figures, and eight desktop/mobile light/dark layout checks at both preview paths.
2026-10-13 18:21:09 +08:00
N0zoM1z0 8583a190c0 fix(website): keep omitted language selectors free of whitespace
Indent the optional language selector with its value so a nonindexable draft does not generate a line containing only spaces. Indexed-page markup is unchanged.

Validation: website.py check passes all 12 maintenance regressions; synchronization changes only the draft header blank line.
2026-10-13 18:21:09 +08:00
N0zoM1z0 fe496bd1cf docs(website): add essay on reverse engineering models
Add an English review draft connecting Touhou reconstruction evidence with model specialization, repository knowledge and feedback-driven learning. Preserve model attribution and historical verification scope in supporting notes, and translate the new Blog listing for all existing editions.

Validation: website.py check, focused formatting checks, and 32 Chromium cases covering both preview paths, light/dark themes, responsive layouts, translated indexes and no-JavaScript reading.
2026-10-13 18:21:09 +08:00
Yijie Lu f8e95799f9 fix(artifacts): preserve NIB field and class identities (#2106) 2026-10-13 18:20:54 +08:00
Harly 6c816af119 fix(managed): preserve negative zero in CIL operands (#2437) 2026-10-13 18:19:44 +08:00
morluto ab02f3c8d6 ci: reduce reporter fan-out and preserve shard history 2026-10-13 18:16:10 +08:00
Chaoqi 9928f357fb fix(artifacts): read ASCII control bytes in legacy ZIP names as ASCII (#2365)
* fix(artifacts): read ASCII control bytes in legacy ZIP names as ASCII

ZIP names without the UTF-8 flag were decoded with zip.js's CP437 table,
which maps bytes 0x01-0x1F and 0x7F to IBM PC display glyphs. A folder
zipped on macOS with a custom icon therefore listed and extracted
`Folder/Icon\r` as `Folder/Icon♪`, a name that is not in the archive and
differs from REA's directory inventory of the same folder. Tabs became `○`
and DEL became `⌂`.

Decode legacy names in REA's existing decodeText hook with the Unicode
CP437 mapping that Python's zipfile also uses: bytes below 0x80 as ASCII,
and the upper half with the same table zip.js uses.

Windows file names cannot contain U+0001-U+001F, so a Windows extraction
destination now refuses such a path with a typed path failure before
creating anything, as it already does for ':'.

* test(artifacts): check extracted legacy ZIP control names on disk

The control-name cases checked only the logical path that inventory
reports. Extraction writes through SafeOutputTree, so a change that
renamed the extracted files would still pass those cases.

Extract `Icon\r` and `é\t\u007F.a`, then compare the reported
relative_path, the directory listing, and the file contents. The new
cases skip on Windows, which refuses these names.
safeOutputTree.test.ts covers that refusal.
2026-10-13 18:05:20 +08:00
morluto d13c9f840d ci: reduce redundant compatibility and qualification work
Select SQLite compatibility by explicit source, fixture, lifecycle, and script ownership while retaining every version in full qualification. Share the canonical scope inventory between planning and the required gate.

Reuse recent exact-SHA full qualification only for hourly schedules, with trusted successful-run metadata and receipts. Force a daily refresh, reject stale original runs even after retries, and never renew proof from reused validation.

Report attempt-specific queue, execution, and matched step timing in a separate completion workflow using trusted default-branch code and read-only API access. Preserve raw metadata and unknown timings without delaying validation or publication.

Validation: 177 focused tests; typecheck/lint; knip; actionlint 1.7.7; all 1046 tests discovered; live 37-job API report smoke. Remote workflow scheduling and real-provider matrices remain CI verification.
2026-10-13 17:46:04 +08:00
LouisChenandLouisChen 0cf224e762 fix(javascript): retain distinct chunk registration callback metadata (#2737)
Co-authored-by: LouisChen <3361008+chqcjq@users.noreply.github.com>
2026-10-13 17:42:41 +08:00
Kaoru0822andClaude Opus 5.5 52d4ce6ced fix(apple): derive dyld environment findings from each traced slice (#2733)
deriveFindings looked up each traced root's slice again by architecture
name. A universal executable with arm64e and arm64e.x1 slices reports
both as "arm64e", so both roots resolved to the first slice: a later
slice's LC_DYLD_ENVIRONMENT produced no finding, or the first slice's
command was reported twice. The per-slice dyld_environment arrays were
already correct.

traceDylibLoading already has the exact slice when it records a root, so
pass it to deriveFindings instead of re-deriving it from the name. The
public roots and findings shapes are unchanged.

Fixes #2661

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 17:42:09 +08:00
morluto e05d3399f1 test(javascript): expect unknown parameter provenance 2026-10-13 17:23:36 +08:00
morluto db3c9f084b fix(ci): keep cancellation ownership in the calling workflow
Remove redundant ref-based concurrency from reusable provider and website workflows. Their old groups could cancel qualification from another CI tier even though the callers used separate groups. Disposable hosted jobs now inherit caller scheduling; direct manual dispatches remain independent.

Validate every called CI workflow has no independent cancellation group. Validation: 100 focused scope/gate tests and actionlint 1.7.7.
2026-10-13 17:15:42 +08:00
morluto 0c7f4dfbd9 ci: separate fast feedback from full qualification
Scope main pushes from a successful ancestor so canceled runs cannot hide changes. Keep the complete portable suite in fast feedback and move coverage plus Intel macOS to hourly, manual, explicit-full, and release qualification. Let qualification finish independently of superseded feedback.

Bind publication to full qualification at its exact revision and publish the verified tarball after checking workflow identity, version, and SHA-256. Keep trusted cache writers and reject incomplete merged test inventories.

Validation: 151 focused CI tests; typecheck/lint; actionlint 1.7.7; knip; all 1044 files discovered; bounded blob/merge smoke; macOS package verifier with retention and cleanup; npm pack dry run. Native Linux/ARM/Windows and remote workflow execution remain CI verification.
2026-10-13 17:09:53 +08:00
2247 changed files with 406965 additions and 68936 deletions

No files matched your search

+3 -3
View File
@@ -68,9 +68,9 @@ closing-issue reporters. Grouping preserves all those credits in one line.
When Highlights, detailed entries or migrations reference the same work,
put its thanks in one of those entries, not all of them. Include issue links.
Direct-commit bullets carry the commit link and known human credit.
Exclude GitHub bots and known agent accounts. REA's observed Anthropic and
Cursor co-author trailers resolve to `@claude` and `@cursoragent` with GitHub
type `User`; the credit policy excludes them while retaining their metadata.
Exclude GitHub bots and known agent accounts. REA's observed Anthropic, Cursor
and Codex co-author trailers resolve to `@claude`, `@cursoragent` and `@codex`
with GitHub type `User`; the credit policy excludes them while retaining their metadata.
At the REA maintainer's request, omit `@morluto` self-thanks while preserving
authorship in the inventory. Other human contributors remain eligible.
Use explicit GitHub links for cross-repository references to avoid ambiguous
+112
View File
@@ -0,0 +1,112 @@
---
name: rea-ci-tests-audit
description: Audit REA CI and test suites for correctness, fixture drift, false passes, and measured performance; use for suite or workflow investigations and efficiency changes, rather than ordinary feature testing or a single test failure.
---
# REA CI and tests audit
Establish which checks still catch real regressions and which costs can be
removed without losing that protection. The outcome may be a verified fix,
an evidence-backed recommendation, or an explicit decision to stop.
Follow the checkout's `AGENTS.md`, [testing guide](../../../docs/testing.md),
and [contribution rules](../../../CONTRIBUTING.md). Use their current commands
and pinned toolchain, rather than commands remembered from an earlier audit.
Preserve the requested scope: investigation alone does not authorize edits;
this skill does not grant permission to publish issues, push, or merge. Existing
user authorization still applies. Keep audit notes and raw results outside the
tracked tree, normally in a task-specific `/tmp` directory.
## Establish the executed baseline
Fetch when the user asks for latest main. Pin the baseline SHA and inspected
workflow revision; use an isolated worktree when changing or comparing code.
Do not modify another task's checkout. Record dirty changes and the exact
command, toolchain, host, selected tests, instrumentation, and prerequisites.
An old run is evidence for its executed revision, even if main advances.
Trace workflow triggers, changed-path selection, job dependencies, reusable
actions, package scripts, test discovery, artifact producers, and report gates
through to the commands that actually execute. Include scripts, generated data,
runtime registration, and subprocess boundaries missing from import graphs.
Check positive and negative selection paths: required files must be discovered
once per intended test configuration; deliberate platform/configuration repeats
are separate coverage. A missing explicit test or missing required report must
not turn green. Optional lanes may skip; preserve their stated prerequisites.
Use existing timing and failure artifacts to select suspicious families. Run
relevant checks to establish a baseline; reserve a full local suite for broad
CI diagnosis or uncertainty that focused checks cannot resolve. Do not rebuild
or rerun an unchanged complete suite after every documentation edit.
## Prove the failure or protection
For each candidate, state the expected observable behavior and its owner,
then construct the smallest reproduction against production behavior. Follow
the producer and affected consumers before deciding where to fix it.
- For drift, compare fixtures with current admission requirements, protocol
replies, named contracts, tool versions, and actual public entrypoints. A
plausible-looking mock can fail before the behavior its test claims to cover.
Search the whole affected fixture family, including other version strings.
Preserve intentionally malformed fixtures and test their intended rejection.
- For a suspicious green test, perturb the relevant behavior in an isolated
copy or inject a controlled failure. Verify that the existing assertion or
gate detects it. A test may merely repeat its mock, import abandoned code,
or hide a subprocess/checker failure. Restore mutations after the experiment.
- For shared setup or pruning, map distinct assertions to their retained
scenarios. Preserve malformed inputs, cancellation, cleanup failure,
permissions, capacity, identity, and meaningful concurrency interactions.
A faster run that skips a case or weakens its oracle is not an improvement.
- For provider coverage, separate fake-producer, boundary, and actual-engine
evidence. Inspect named skips and prerequisites; fixtures do not establish
real Ghidra, LLDB, browser, or other provider behavior.
Classify findings as reproduced defects, supported costs, hypotheses, or
unavailable evidence. Record the baseline command, observed output/exit status,
expected result, affected scope, and competing explanation. A failure on main
may be pre-existing; that does not by itself make it irrelevant to a CI audit.
Attribute PR failures using the executed revision and a clean baseline, not
just their resemblance to a previous failure. Retry only to test a specific
transient explanation; retain the original failure and stop when repetition
adds no evidence. A passing retry does not establish a root cause or a fix.
## Assess efficiency without losing the gate
Read [measurement and CI checks](references/measurement-and-ci.md) when
investigating slowness, caching, runner waits, shared artifacts, or log noise.
Compare equivalent completed work before claiming a speedup. Distinguish
setup/build time, test execution, coverage instrumentation, dependency waits,
and runner scheduling; state which costs remain unmeasured.
Prefer removing proven duplicate preparation, reusing immutable setup, and
reducing repeated subprocess work before shrinking correctness coverage.
Preserve independent sessions where lifecycle or concurrency is the oracle.
Keep live tests fresh; a cached previous pass cannot validate current host,
installed provider contents, permissions, or external state.
## Fix, verify, and hand off within scope
If implementation is authorized, make the smallest structural change at the
owning boundary and add a regression that fails on the baseline for the right
reason. Retest affected consumers and meaningful negative paths; verify cache
invalidation or discovery when those mechanisms change. Read the installed
Turbo documentation before changing its configuration or commands.
Run the repository's applicable checks and inspect complete current-head CI
results before an authorized merge. Preserve required coverage and discovery
gates. Do not suppress a failure, add blanket retries, raise timeouts, or delete
distinct assertions solely to make CI green. Changes to these controls need
their own demonstrated reason and retained failure detection.
Report each confirmed issue with exact revision, reproduction, consequence,
proposed correction, and validation limits. Report performance with workload
and measurements, not just a percentage. Use English issue/PR records when
requested; follow the user's assignment, commit identity, review, and merge
preferences without assuming them for other users. Open ready-for-review PRs
only when that lifecycle is authorized. Resolve necessary review findings and
check final main against the tested tree after merging.
Stop when the authorized work is verified, remaining benefits are unproven,
or further changes would trade correctness for speculative speed. List genuine
uncertainties without turning them into additional optimization rounds.
@@ -0,0 +1,4 @@
interface:
display_name: "REA CI and tests audit"
short_description: "Audit CI/test correctness and measured performance"
default_prompt: "Use $rea-ci-tests-audit to investigate CI and test correctness, drift, and performance before proposing changes."
@@ -0,0 +1,65 @@
# Measurement and CI checks
## Equivalent completed work
Record the source/workflow SHA, command, test identities and counts, skips,
assertions or output contract, exit status, coverage scope and thresholds,
toolchain/provider versions, host resources, and prepared artifacts. Inspect
both the child result and its report: a profiling wrapper may exit successfully
after the measured command failed. Focused tests can pass while global coverage
thresholds fail; describe both rather than calling the command green.
Measure wall time, user/system CPU, and peak process-family memory for relevant
completed tasks. Parent-only RSS misses workers and their children. On Linux,
summed RSS can double-count shared pages; report RSS/PSS semantics, sampling
interval, missing samples, and observed cleanup limits. Use equivalent host
tools elsewhere. State unknown costs, including agent tokens; fewer log bytes
alone prove neither fewer tokens nor faster tests.
Separate cold execution, warm build/file caches, and warm runtime caches.
Use repeated or alternating runs when variance affects the decision, with
enough observations to distinguish the proposed benefit from noise. Keep
workload, instrumentation, and host conditions comparable; record interference.
A run with fewer tests, early rejection, lost coverage, or a different runner
does not prove suite acceleration. CI-wide claims need end-to-end measurements,
not an extrapolation from one improved family.
## Workflow, caches, and shared artifacts
Trace each reused result from producer to consumers and identify its lifetime.
Check key/input coverage against the actual file inventory and result-affecting
configuration, environment, generated data, tool versions, and host state.
Exercise an affected input change to prove invalidation; exercise an irrelevant
change to support reuse where useful. Cache deterministic preparation and
static analysis only when their inputs fully describe the result.
Verify downloaded artifacts match the expected revision, platform, runtime,
and schema. Check restore misses, failed/partial producers, stale reports,
removed output files, and consumers after producer failures. Shared writers
need output ownership or locks; tests need isolated state, not an unnecessary
global lock. Worker counts do not bound children spawned within each test.
Preserve intentionally concurrent scenarios while bounding unrelated batches.
Check matrix expansion and all job pages before saying CI passed. Inspect
required gates after failed, cancelled, and skipped dependencies. `always()`
alone does not preserve failures; inspect the aggregation logic. Test/report
uploads should retain available diagnostics after failure, while absent or
malformed required results must fail closed. Do not demand an optional report
from a lane intentionally excluded by the selector.
## Runner waits and console noise
Use per-job and per-step timestamps where available. Workflow creation to job
start includes dependencies and other delays; it is not necessarily runner
queue time. Identify when the job became eligible before attributing waits to
runner capacity. If eligibility/queue timestamps are unavailable, report the
combined delay and unknown split. Compare equivalent lanes and runner types
across enough runs; a single busy runner is not a workflow regression.
For console reductions, retain checker exit codes, actionable errors, useful
summary counts, and complete machine-readable artifacts. Verify authored code
remains in the actual checker inventory when excluding generated outputs.
Introduce an authored error as a control; it must still fail. An ignore pattern
can hide real source as well as noise. Show that cached reports are restored,
stale successful reports cannot mask current failures, and report parse errors
remain failures. Measure readability or byte reduction separately from runtime.
@@ -2,7 +2,7 @@
name: reverse-engineer-anything
description: Reverse engineer native, managed, Electron/JavaScript, packaged, firmware, and browser targets with REA. Use shipped-artifact or requested runtime evidence to explain features, compare versions, decompile code, or guide a reconstruction. Skip REA for ordinary source-repository architecture analysis.
metadata:
version: "41"
version: "42"
---
# REA
@@ -31,12 +31,55 @@ Diagnose the observed blocker, continue useful supported work, and resume the
original task after repair. Client approval, shell PATH, registration and engine
availability have different remedies; avoid repeating setup for all of them.
1. Diagnose without changing REA or client configuration. `npx` may download
the package and write to npm's cache. For Codex, run
`npx -y rea-agents@latest doctor --client codex --json`. Substitute the current
supported client: `claude_code`, `claude_desktop`, `codex`, `cursor`,
`gemini_cli`, `windsurf`, `devin`, `opencode`, `antigravity`, `copilot_cli`,
`commandcode`, `vscode`, `grok_build`, `omp`, or `grok_bot`. `grok_bot` has no local
file registration: doctor reports the Grok Bot chat step, and setup does not
write `mcp.json`. If the client is unknown, use `doctor --json` and
inspect its registration results before choosing a setup scope.
2. Distinguish the reason. Missing, malformed, or stale registration needs a
scoped configuration repair. An aligned registration with no tools in the
active session needs a restart/reconnection; doctor cannot prove that the
current agent has connected. A missing provider affects only tasks requiring
that provider: static JavaScript inspection needs neither Hopper nor Ghidra,
and Android inspection has separate bring-your-own JADX/Java prerequisites.
3. For a configuration repair, prepare the read-only plan:
`npx -y rea-agents@latest setup --client codex --dry-run --json`.
Use the current client's ID, show its exact proposed paths, backups, and
changes, and obtain approval before setup writes configuration or installs
Hopper. Setup normally installs the matching bundled skill too; include that
replacement in the reviewed plan. After approval, apply the same scope with
`npx -y rea-agents@latest setup --client codex --yes`. Add `--install-hopper`
only if that separate installation was needed and explicitly approved.
4. Restart/reconnect the affected agent. Verify that REA tools actually appear
in the session, then resume the original investigation. If they remain
absent, inspect the client's MCP launch error rather than repeating setup.
REA setup never installs or upgrades Node.js, npm, Homebrew, Java, Ghidra, IDA,
JADX, Kuna, Binwalk, or Unblob. Use existing prerequisites; do not install unrelated
software to repair
MCP registration. For an unsupported client, use manual stdio registration
with a version-pinned `rea-agents` package or continue through the CLI.
A concrete CLI fallback for an operator-supplied JavaScript tree or ASAR is:
```bash
npx -y rea-agents@latest analyze-javascript-application /absolute/path/to/app --json
```
No MCP registration or native engine is required. Read the returned Evidence,
graph, limitations, and unknowns with the same care as an MCP result; the CLI
returns the Evidence record directly. This fallback does not establish that
MCP is configured. Native CLI tasks still require their selected engine.
Prefer an existing `rea` CLI. When none is installed and REA package acquisition
is within the authorized scope, use `npx -y rea-agents@latest` in its place.
Configuration repair starts with a read-only setup plan; show paths, backups and
skill replacement before applying authorized changes. Preserve existing approval
and target-execution boundaries. REA setup never installs or upgrades unrelated
software, including Node.js, npm, Homebrew, Java, Ghidra, IDA, JADX, Binwalk or Unblob.
software, including Node.js, npm, Homebrew, Java, Ghidra, IDA, JADX, Kuna, Binwalk or Unblob.
## Route the target first
@@ -61,6 +104,10 @@ explicit path or endpoint and do not need it.
consult the NativeAOT workflow in `references/native-and-artifacts.md` for
optional metadata recovery and its supported host/layout boundary.
- User-owned browser page already open: `list_browser_targets`.
- Browser action the agent must perform (click, type, navigate):
`capture_browser_scenario` with declared actions. See
[runtime observation](references/runtime-observation.md) for connect/launch
and the focused follow-up browser tools.
- Fiddler Everywhere traffic or a native `.saz` capture: read the
[Fiddler workflow](references/runtime-observation.md#fiddler-everywhere-traffic-debugging).
Inspect saved SAZ with `inspect_web_network_capture` and `format: "saz"`;
@@ -75,7 +122,7 @@ explicit path or endpoint and do not need it.
pages; select one trace, stream ID and direction for contribution/range pages.
Facts retain all-recorded-events-before-selection scope. Other qlog layouts
and PCAP correlation are unsupported.
- User-owned Electron runtime already open: `list_electron_targets`.
- User-owned Electron runtime already open: `list_electron_targets`. Before an electron-main Inspector attempt, check packaged fuses with `inspect_electron_fuses`; disabled `EnableNodeCliInspectArguments` means the app ignores `--inspect`.
- Packet-field discovery: `inspect_packet_fields` searches installed protocols
and field names/types before choosing fields for `inspect_packet_capture`.
Query/page the relevant records; registrations do not establish observed traffic.
@@ -122,6 +169,14 @@ explicit path or endpoint and do not need it.
absent metadata does not prove a binary is not Go, and unparsed text records
remain explicit. See the
[Go binary guide](https://github.com/morluto/rea/blob/main/docs/go-binaries.md).
- Embedded cargo-auditable Rust dependency metadata: `inspect_rust_binary`
with the explicit local ELF, PE, or thin Mach-O file. This built-in
target-free inspection requires no Rust installation or disassembler. Preserve
package order, dependency indices, and the section file range. A missing
`.dep-v0` section is null metadata, not proof the binary has no dependencies.
A rustc commit hash is an inference from a read-only `/rustc/<40-hex>/` path.
See the
[Rust binary guide](https://github.com/morluto/rea/blob/main/docs/rust-binaries.md).
- Explicit local SQLite database snapshot: `inspect_sqlite_database`. Inspect
with Node.js 24.x >=24.15 or 26+ providing native SQLite limits. Read schema
@@ -160,6 +215,20 @@ after opening; IDA does not supply `binary_overview`. Consult session availabili
when composing broader workflows. Modern direct callers and unsupported dossier
facets remain unknown; live IDA results are not replayed from snapshots.
With `REA_KUNA_COMMAND` configured, `provider_id: "kuna"` answers procedure
lists, lookups, pseudocode, assembly and `analyze_function` from Kuna's cached
whole-program export. Callers/callees use a separate body-free graph shared by
the session; `xrefs` returns exact-address incoming sites and filters forwarding
aliases. These queries and inventory lookups do not trigger a C export. Indirect
targets remain unknown and can make relationship coverage partial. Reference
caches expire on close; a fresh CLI process repeats the native reference walk.
Typed references, CFG, raw-memory tools and annotations remain unsupported. A cold first
answer can come from a single-function run (its limitations say so); assembly
waits for the export. For pseudocode and assembly, `raw_result.source` names the
cached `.c`/`.asm` file and byte span, which you may search directly instead of
calling per function. See the
[Kuna provider guide](https://github.com/morluto/rea/blob/main/docs/kuna-provider.md).
If the user says only "my app" without a name, path, or target already selected
for this task, ask which app before searching for artifacts. Paths in unrelated
saved reports and nearby test fixtures do not select the user's target. Resolve
@@ -272,7 +341,8 @@ as complete while required questions remain open.
[references/javascript-applications.md](references/javascript-applications.md)
- Android APK declarations, classes, methods, and static references:
[references/android-applications.md](references/android-applications.md)
- Passive browser/Electron observation and static/runtime reconciliation:
- Browser/Electron observation (passive and scenario) and static/runtime
reconciliation:
[references/runtime-observation.md](references/runtime-observation.md)
- Evidence paging, comparisons, residual unknowns, and verification:
[references/evidence-workflows.md](references/evidence-workflows.md)
@@ -46,7 +46,7 @@ rea setup --client codex --dry-run --json
Use the current supported client's ID: `claude_code`, `claude_desktop`, `codex`,
`cursor`, `gemini_cli`, `windsurf`, `devin`, `opencode`, `antigravity`,
`copilot_cli`, `commandcode`, `qwen_code`, `vscode`, `trae`, `grok_build`, `omp`, `pi`,
`hermes`, `qoder`, `zcode`, `dsh`, or `grok_bot`. If unknown, inspect `doctor --json` before choosing a
`hermes`, `qoder`, `droid`, `zcode`, `dsh`, or `grok_bot`. If unknown, inspect `doctor --json` before choosing a
scope. `grok_bot` uses a chat registration step rather than local `mcp.json`.
For unsupported clients, use version-pinned manual stdio registration or CLI.
@@ -65,3 +65,7 @@ Use `inspect_analysis_view` with `view: {kind: "api-surface"}` on retained
method, origin and path template. Format matches are inferences, not handler
identity or schemas. Group pages cite parent record ordinals; consult record
coverage and exclusions before claiming completeness. No requests are replayed.
When the connected schema advertises `include_query_parameter_names`, select
it in the view for distinct observed names with original record ordinals. Query
values are omitted; the names establish neither required parameters nor a
complete server schema. Keep the original parent Evidence for exact URLs.
@@ -94,6 +94,36 @@ recorded observations, identity, hashes, exports and source ranges; it does not
include every child syntax observation, related edge or original source file.
An absent item fact is not evidence of absence from the whole application.
For canonical identities and retained source-range counts, use a typed reference
page instead of reading every full item:
```json
{
"source": {
"kind": "retained-evidence",
"evidence_id": "ev_ACTUAL_PARENT_ID"
},
"view": {
"kind": "javascript",
"collection": "modules",
"projection": "references",
"where": { "path": "main.js", "kind": "javascript-module" },
"offset": 0,
"limit": 16
}
}
```
Filters match exact retained strings; omit a filter to include all values, use
`path: null` for unavailable paths, and `path: ""` for a recorded empty path.
Rows preserve canonical `identity` and distinct observation support states in
`basis`. `retained_source_range_count` counts retained source-range observations,
not runtime imports or unique reference sites. Use a row's `node_id` with the item
view above for exact locations and full supporting observations. Follow
`coverage.next_offset`; page exhaustion describes matching retained rows only.
Check `source_coverage` for source selection, partial/unknown graph coverage and
unknown counts before drawing a negative conclusion.
## Trace only the remaining question
Use `trace_application_feature` on the parent application Evidence for one
@@ -11,6 +11,18 @@ unparsed module-text line. A missing record is an unknown, not proof of a
non-Go binary; observed metadata does not authenticate its claims. Function,
source and type recovery remain a separate native-analysis question.
## Rust dependency metadata
Use `inspect_rust_binary` for an explicit local ELF, PE, or thin Mach-O when
the question concerns the cargo-auditable crate list embedded in `.dep-v0`
(Mach-O: `__DATA,.dep-v0`). No `open_binary`, Rust installation, or target
execution is required. The CLI equivalent is `inspect-rust-binary PATH`.
Preserve artifact SHA-256, package order, dependency indices, and the section
file range. A missing section is null metadata, not proof the binary has no
dependencies. An omitted package kind stays null. A rustc commit hash is
inferred from a read-only `/rustc/<40-hex>/` path and does not by itself
identify the compiler. See [Rust binaries](https://github.com/morluto/rea/blob/main/docs/rust-binaries.md).
## SQLite snapshots
Use `inspect_sqlite_database` with an explicit local database snapshot for its
@@ -119,6 +131,8 @@ loaded bytes, file mappings, relocations and the entry against the immutable
snapshot. Keep `mismatch` and `unsupported` explicit. Verified import covers the
reported static image, not runtime DOS or PC-98 hardware behavior.
For Ghidra decompilation, `procedure_pseudo_code` also accepts `procedure: "file-offset:0x..."`; CLI `rea decompile <path> --file-offset=0x... --provider ghidra` resolves the source byte offset internally. An unresolved 0x-prefixed numeric procedure selector also falls back to a unique file mapping. Existing symbols and analysis addresses win; multiple mappings fail as ambiguous. Preserve the returned file-offset and address locations and raw selection observations. ELF virtual addresses are distinct from file offsets; never assume an arbitrary offset plus image base is an analysis address.
Use `read_bytes` for initialized analysis memory and `address_to_file_offset` to
anchor one observed address to original file bytes. Loader fixups can change a
word; a source offset does not imply byte equality. Complete function body ranges
@@ -155,6 +169,14 @@ repeat parsing. Returned-row exhaustion
does not prove complete metadata or absence under partial/unavailable coverage;
preserve build-local tokens and their artifact/MVID scope.
`inspect_managed_members` always returns the assembly-wide `member_refs`, even
for one selected type. For questions about a selected type, select
`detail: "summary"` with `type_names`; it returns the selection (with unmatched
names), coverage and per-collection counts. Page rows with
`view: {kind: "managed-members", collection: "call_edges", offset: 0, limit: 64}`
over `types`, `exported_types`, `fields`, `methods`, `member_refs`, `call_edges`,
`field_accesses` or `user_strings`.
### .NET NativeAOT
NativeAOT binaries contain native machine code, so ordinary CIL decompilers
@@ -173,7 +195,9 @@ additional evidence only when the provider accepts and matches them.
REA has a built-in read-only snapshot parser for x64 PE targets with .NET 8 RTR
9.1 metadata. An optional headless Ghidra adapter adds database annotations and
broader metadata recovery on Linux; it requires an explicitly configured
NativeAOT JAR. These are separate routes. See the
NativeAOT JAR. These are separate routes. The built-in parser also runs on
Windows Ghidra P0; read frozen literals from its report, because
`search_strings` does not include them. See the
[NativeAOT guide](https://github.com/morluto/rea/blob/main/docs/ghidra-nativeaot.md)
for setup, supported layouts, and host restrictions. Preserve binary identity
and distinguish derived metadata from Ghidra database observations. Recovered
@@ -222,10 +246,13 @@ context. PSP/stack/device state remains unmodeled. See [DOS guide](https://githu
For other headerless images (embedded firmware without ELF/Mach-O/PE/MZ
headers), open with `format: "raw-binary"`, `architecture` (`x86`, `x86_64`,
`arm`, or `arm64`), and `entry_point` (`0x` hex). File offset 0 is mapped at
`arm`, `arm64`, or `6502`), and `entry_point` (`0x` hex). File offset 0 is mapped at
that address. Hopper and IDA refuse this interpretation; Ghidra uses
`BinaryLoader` with a little-endian generic language. Big-endian, Thumb-only,
and Cortex-M variants are unsupported. See [raw binary guide](https://github.com/morluto/rea/blob/main/docs/raw-binary.md).
and Cortex-M variants are unsupported. `6502` selects NMOS 6502; the entry and
complete file mapping must fit within `0x0000..0xffff`. 65C02, 65816, cartridge
containers, bank switching, and mapper semantics are unsupported. Windows
Ghidra P0 admits raw images only for `6502`. See [raw binary guide](https://github.com/morluto/rea/blob/main/docs/raw-binary.md).
### Function annotations in Ghidra
@@ -242,3 +269,9 @@ resolution is explicit; missing references and size mismatch remain visible.
Other payloads are opaque, and this operation does not decode images or simulate
Windows resource loading. Consult the budgets and supported tree profile in
[offline binary diagnostics](https://github.com/morluto/rea/blob/main/docs/binary-diagnostics.md).
For exact core-module import/export differences, use `compare_wasm_interfaces`
with two complete inspection Evidence records (or current-session retained
references) and explicit scope. Preserve unknown types/duplicate names; static
interface equality is not runtime compatibility. CLI accepts complete inline
Evidence in `rea compare-wasm-interfaces input.json --json`.
@@ -1,4 +1,4 @@
# Passive browser and Electron observation
# Browser and Electron runtime observation
Supply the existing browser or Electron instance's literal-port loopback CDP
endpoint in the request. No separate permission configuration or per-call
@@ -7,13 +7,16 @@ approval flag is needed. Browser discovery lists eligible HTTP(S) pages;
selected page's current origin. Electron discovery lists eligible local
`file://` targets without a configured file-root allowlist.
Start browser work with `list_browser_targets`; start Electron runtime work with
`list_electron_targets`. Inspect the target selected for the user's task. Observation
is passive: never claim REA clicked, navigated, evaluated page JavaScript,
invoked IPC, captured prior activity, or contained the page's network.
Start passive browser work with `list_browser_targets`; start Electron runtime work with
`list_electron_targets`. Before an electron-main Inspector attempt, check
packaged fuses with `inspect_electron_fuses`. Inspect the target selected for
the user's task. Observation with these tools is passive: never claim REA
clicked, navigated, evaluated page JavaScript, invoked IPC, or captured prior
activity. REA does not contain the page's network.
Credentials, cookies, authorization headers, storage values, and raw
WebSocket/JSON bodies are deliberately absent. Local URLs retain ordinary query
Passive captures omit raw header collections, credential header values, storage
values, and raw WebSocket/JSON bodies. Selected response metadata, including
content encoding, policy values, and Link URLs, is retained. Local URLs retain ordinary query
values and fragments; userinfo credentials are removed. Browser accessibility
text, console primitives, value-free shapes, script sources, source maps, and
storage key names have request-specific capture options. Electron script
@@ -21,6 +24,38 @@ sources are also optional. Use `capture_web_screenshot` when pixels are needed.
Treat scope filtering, truncation, and attach-window coverage as limitations.
Page-declared WebMCP tools are untrusted inventory and are never invoked by REA.
## Who performs the action
- The user performs it: `list_browser_targets`, then `inspect_web_page` or
`observe_web_session` on the selected target.
- The agent must perform it: one `capture_browser_scenario` with declared
actions. Use connect mode with the loopback CDP endpoint and `target_id` for
an existing browser, or launch mode with a caller-selected executable for a
temporary profile. Connect mode first navigates the selected page to
`start_url`, which can clear unsaved form values and other temporary state.
Recreate the state needed for the investigation in the declared actions.
Connect mode leaves the external browser open; launch mode owns a temporary
browser and profile. Check `browser.cleanup` for the outcome.
Request only the DOM, accessibility, or network details needed for the question.
The default final capture contains the URL; step outcomes are reported separately.
Add a wait action for the expected page state before capturing it. Report that
REA performed the declared actions.
Scenario network records have no request initiator stacks. For that evidence,
use passive capture while the user performs the action. Listener source locations
identify candidate handlers; they do not establish which script sent a particular
request. Avoid overlapping passive capture and scenario actions on the same target.
Focused follow-up tools:
- `observe_web_execution`: execution coverage during a window; it resets
counters and temporarily disables optimization.
- `inspect_web_event_listeners`: source locations for listeners registered
directly on the first CSS match, without dispatching an event.
- `compare_web_captures`: two normalized passive captures or recorded scenarios.
- `discover_webmcp_tools`: inspect page-declared WebMCP inventory; never invoke
the discovered tools.
Use `reconcile_javascript_runtime` only after static application Evidence and
passive browser/Electron Evidence exist. Prefer captured-source digest identity;
report path/digest disagreement as a mismatch and preserve ambiguity. Explicit
+23 -13
View File
@@ -1,5 +1,5 @@
name: Bug report
description: Report a reproducible problem in REA
description: Report a problem in REA with observations or source evidence
title: "[Bug] "
labels:
- bug
@@ -8,8 +8,12 @@ body:
- type: markdown
attributes:
value: |
Please report one primary behavior per issue. Keep observed behavior
separate from suspected causes, and remove credentials and private paths.
Report one primary behavior per issue. Keep answers concise and grounded
in available evidence. Distinguish observed behavior, source-derived
reasoning, and suspected causes; do not claim a reproduction you did not run.
If a narrative answer is not known, write `NOT_ENOUGH_INFO` and describe
what is missing. Intermittent failures and clearly labeled source-only
reports are welcome. Do not include credentials or sensitive target data.
- type: checkboxes
id: before-submitting
@@ -42,7 +46,7 @@ body:
id: observed-behavior
attributes:
label: Observed behavior
description: What happened? Include the command, MCP tool, provider, target type, and conditions that matter.
description: Describe the actual result, including the exact command or MCP request, relevant provider/target, and supporting evidence below. Label source-only findings instead of claiming execution.
validations:
required: true
@@ -50,7 +54,7 @@ body:
id: reproduction
attributes:
label: Reproduction
description: Give the smallest realistic command, configuration, input, or event sequence that demonstrates the problem.
description: Give the shortest steps you actually ran, with the relevant input or configuration. State how often they reproduced the problem when known. If reproduction is unavailable, say so and provide the observations or source reference you have.
placeholder: |
1. Configure or start ...
2. Run ...
@@ -62,7 +66,7 @@ body:
id: expected-behavior
attributes:
label: Expected behavior
description: What result, contract, invariant, recovery action, or state transition should have occurred?
description: State the expected result, contract, or recovery action. Cite documentation, source, or a known-good version when available; mark an unverified expectation or unknown reference clearly.
validations:
required: true
@@ -70,7 +74,7 @@ body:
id: impact-scope
attributes:
label: Impact and scope
description: What is affected, what is not affected, and does this affect evidence integrity, process isolation, compatibility, or recovery?
description: Describe observed impact and frequency, including effects on evidence integrity, process isolation, compatibility, or recovery when known. Leave untested scope and severity unknown rather than guessing.
validations:
required: true
@@ -79,7 +83,7 @@ body:
attributes:
label: REA version
description: Use `rea --version` or provide the source revision.
placeholder: 3.1.0 or git revision
placeholder: Exact rea --version output or source commit SHA
validations:
required: true
@@ -116,7 +120,7 @@ body:
id: failure-stage
attributes:
label: Earliest failure stage
description: Select where the incorrect behavior first becomes observable.
description: Select where the incorrect behavior first becomes observable. Choose Unknown if this has not been established.
options:
- Request parsing or validation
- Setup or provider admission
@@ -133,8 +137,14 @@ body:
id: relevant-evidence
attributes:
label: Relevant output or evidence
description: Paste the smallest useful CLI output, MCP result, error, trace, or evidence excerpt. Redact sensitive values.
render: text
description: Include the smallest useful output, MCP result, error, screenshot, artifact, or exact source reference. Say how it was obtained and which claim it supports. If unavailable, write NOT_ENOUGH_INFO and explain what is missing. Redact credentials and sensitive target data.
placeholder: |
Command or MCP request actually run:
Relevant result, error, or artifact:
For source-only findings, link the file/revision and relevant code:
Missing evidence or remaining uncertainty:
validations:
required: true
- type: textarea
id: typed-outcome
@@ -146,10 +156,10 @@ body:
id: analysis
attributes:
label: Analysis or suspected cause
description: Optional. Clearly mark source-derived reasoning or a hypothesis; do not present an unverified cause as fact.
description: Optional. Link supporting source and distinguish source-derived reasoning from a hypothesis. A root-cause analysis or proposed fix is not required.
- type: textarea
id: additional-context
attributes:
label: Additional context
description: Add links, reduced fixtures, screenshots, or a proposed regression test.
description: Add reduced fixtures, screenshots, or related issues. For observed regressions, include the last known good and first known bad versions when available. Label untested checks as proposals.
+1 -1
View File
@@ -41,7 +41,7 @@ body:
id: context-evidence
attributes:
label: Context and evidence
description: Explain the problem or motivation. If you have evidence, distinguish observed behavior from ideas or guesses.
description: Explain the problem or motivation and link relevant evidence when available. Distinguish observed behavior, source-derived reasoning, proposed behavior, and unknowns. A question without measurements or a finished design is welcome.
validations:
required: true
+12 -3
View File
@@ -5,6 +5,13 @@ labels:
- documentation
- triage
body:
- type: markdown
attributes:
value: |
Report one documentation problem with the exact location and the smallest
useful supporting passage, example, or result. Keep answers concise.
If something is unknown, write `NOT_ENOUGH_INFO` and explain what is missing.
- type: checkboxes
id: before-submitting
attributes:
@@ -28,7 +35,7 @@ body:
id: problem
attributes:
label: Problem
description: What is unclear, missing, outdated, or incorrect, and who is affected?
description: Quote or describe the current content, explain what is unclear, missing, outdated, or incorrect, and say how to observe the problem. State who is affected when known.
validations:
required: true
@@ -36,7 +43,7 @@ body:
id: expected-content
attributes:
label: Expected content
description: Describe the accurate behavior or example that should be documented.
description: Describe the accurate behavior or example that should be documented, with a reference when available. Label a proposed clarification or unknown behavior clearly.
validations:
required: true
@@ -44,7 +51,9 @@ body:
id: evidence
attributes:
label: Supporting evidence
description: Add source, command output, provider documentation, or a link that supports the correction.
description: Include an exact passage, source reference, command output, provider documentation, or screenshot supporting the report. For missing or unclear guidance, point to the relevant section and explain the gap. If evidence is unavailable, write NOT_ENOUGH_INFO and explain what is missing.
validations:
required: true
- type: textarea
id: additional-context
+4 -2
View File
@@ -11,6 +11,8 @@ body:
Describe one problem and the outcome you would like. Short answers are
welcome. Choose "Not sure yet" wherever needed; you do not need code, a
finished design, or a complete test plan to propose an idea.
Use concrete examples when available, distinguish current limitations
from proposed behavior, and leave unmeasured impact or unknowns explicit.
Read the [contribution scope](https://github.com/morluto/rea/blob/main/CONTRIBUTING.md#contribution-scope).
Maintainers can help resolve scope and technical questions. Unresolved
@@ -58,7 +60,7 @@ body:
id: use-cases
attributes:
label: Use cases
description: Optional additional examples of applications, targets, or callers. Leave blank if covered above.
description: Optional examples of affected workflows, targets, or callers and their practical impact. Include observed frequency or measurements when available; do not invent them. Leave blank if covered above.
- type: textarea
id: desired-outcome
@@ -138,4 +140,4 @@ body:
id: references
attributes:
label: References
description: Link relevant provider documentation, source, measurements, artifacts, issues, or discussions.
description: Optional supporting examples, provider documentation, source, measurements, artifacts, issues, or prior art. Distinguish observed results from expected benefits; an idea without evidence is welcome.
+42
View File
@@ -0,0 +1,42 @@
name: Cache npm package downloads
description: Restore npm's download cache and save it only from trusted default-branch events.
outputs:
can-save:
description: Whether this run may update the shared npm download cache.
value: ${{ steps.context.outputs.can-save }}
path:
description: The npm download-cache directory.
value: ${{ steps.context.outputs.path }}
runs:
using: composite
steps:
- id: context
shell: bash
env:
DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
LOCKFILES_HASH: ${{ hashFiles('package-lock.json', 'npm-shrinkwrap.json', '.npmrc') }}
run: |
cache_path="$(npm config get cache)"
runtime_arch="$(node -p 'process.arch')"
can_save=false
if [[ -n "${DEFAULT_BRANCH}" && "${GITHUB_REF}" == "refs/heads/${DEFAULT_BRANCH}" && ( "${GITHUB_EVENT_NAME}" == "push" || "${GITHUB_EVENT_NAME}" == "schedule" || "${GITHUB_EVENT_NAME}" == "workflow_dispatch" ) ]]; then
can_save=true
fi
cache_key="npm-download-${RUNNER_OS}-${runtime_arch}-${LOCKFILES_HASH}"
{
printf 'can-save=%s\n' "${can_save}"
printf 'path=%s\n' "${cache_path}"
printf 'key=%s\n' "${cache_key}"
} >> "${GITHUB_OUTPUT}"
- name: Restore and save npm package downloads
if: steps.context.outputs.can-save == 'true'
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: ${{ steps.context.outputs.path }}
key: ${{ steps.context.outputs.key }}
- name: Restore npm package downloads without saving
if: steps.context.outputs.can-save != 'true'
uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: ${{ steps.context.outputs.path }}
key: ${{ steps.context.outputs.key }}
@@ -1,14 +1,24 @@
name: Portable build cache
description: Reuse Turbo build outputs without serializing verification jobs.
description: Restore portable Turbo build outputs from the current run or cross-run cache.
inputs:
save:
description: Save newly built outputs after the job succeeds.
default: "false"
build_cache_artifact:
description: Reuse portable Turbo outputs uploaded by the current workflow run.
default: ""
runs:
using: composite
steps:
- name: Download portable build outputs from this workflow run
if: inputs.build_cache_artifact != ''
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: ${{ inputs.build_cache_artifact }}
path: .turbo/cache/
- name: Resolve portable build task hashes
id: plan
if: inputs.build_cache_artifact == ''
shell: bash
env:
BUILD_CACHE_PREFIX: rea-portable-build-v2-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('.npmrc', '.nvmrc', 'package-lock.json') }}
@@ -28,15 +38,15 @@ runs:
console.log(`prefix=${process.env.BUILD_CACHE_PREFIX}-`);
' >> "$GITHUB_OUTPUT"
- name: Restore portable build outputs
if: inputs.save != 'true'
uses: actions/cache/restore@v4
if: inputs.build_cache_artifact == '' && inputs.save != 'true'
uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: .turbo/cache/
key: ${{ steps.plan.outputs.key }}
restore-keys: ${{ steps.plan.outputs.prefix }}
- name: Restore and save portable build outputs
if: inputs.save == 'true'
uses: actions/cache@v4
if: inputs.build_cache_artifact == '' && inputs.save == 'true'
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: .turbo/cache/
key: ${{ steps.plan.outputs.key }}
@@ -0,0 +1,26 @@
name: Prepare qualified release inputs
description: Restore and validate the exact qualified tarball and its verifier runtime
outputs:
version:
description: Package version validated against the source and qualified manifest
value: ${{ steps.package.outputs.version }}
tarball:
description: Absolute path to the validated qualified npm tarball
value: ${{ steps.package.outputs.tarball }}
runs:
using: composite
steps:
- name: Download the exact qualified package
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: verified-release-package
path: .cache/verified-package/
- name: Verify qualified package provenance and digest
id: package
shell: bash
run: node scripts/ci/verified-package.mjs validate .cache/verified-package
- name: Restore qualified verifier runtime
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: rea-test-runtime
path: .
+41
View File
@@ -0,0 +1,41 @@
name: Publish MCP Registry metadata
description: Validate and publish one released server.json, preserving an identical existing version
inputs:
directory:
description: Directory containing the released server.json
default: .
runs:
using: composite
steps:
- name: Install MCP Registry publisher
shell: bash
working-directory: ${{ inputs.directory }}
env:
MCP_PUBLISHER_VERSION: v1.8.0
MCP_PUBLISHER_SHA256_LINUX_AMD64: 1370446bbe74d562608e8005a6ccce02d146a661fbd78674e11cc70b9618d6cf
run: |
archive="mcp-publisher_linux_amd64.tar.gz"
curl -fsSLo "${archive}" \
"https://github.com/modelcontextprotocol/registry/releases/download/${MCP_PUBLISHER_VERSION}/${archive}"
echo "${MCP_PUBLISHER_SHA256_LINUX_AMD64} ${archive}" | sha256sum --check --strict
tar xzf "${archive}" mcp-publisher
chmod +x mcp-publisher
- name: Validate MCP Registry metadata
shell: bash
working-directory: ${{ inputs.directory }}
run: ./mcp-publisher validate
- name: Check existing registry version
id: registry
shell: bash
working-directory: ${{ inputs.directory }}
run: node "${GITHUB_ACTION_PATH}/registry-state.mjs"
- name: Authenticate to MCP Registry
if: steps.registry.outputs.exists != 'true'
shell: bash
working-directory: ${{ inputs.directory }}
run: ./mcp-publisher login github-oidc
- name: Publish MCP Registry metadata
if: steps.registry.outputs.exists != 'true'
shell: bash
working-directory: ${{ inputs.directory }}
run: ./mcp-publisher publish
@@ -0,0 +1,4 @@
export function registryPublicationState(
server: Readonly<{ name: string; version: string } & Record<string, unknown>>,
request?: (url: URL, init: RequestInit) => Promise<Response>,
): Promise<"present" | "absent">;
@@ -0,0 +1,89 @@
import assert from "node:assert/strict";
import { appendFile, readFile } from "node:fs/promises";
import { resolve } from "node:path";
import { fileURLToPath } from "node:url";
/** Exact-version routes currently return false 404s; enumerate the filtered listing. */
export async function registryPublicationState(server, request = fetch) {
assert.equal(typeof server?.name, "string");
assert.equal(typeof server?.version, "string");
const cursors = new Set();
let cursor;
const matches = [];
do {
const url = new URL(
"https://registry.modelcontextprotocol.io/v0.1/servers",
);
url.search = new URLSearchParams({
search: server.name,
version: server.version,
include_deleted: "true",
limit: "100",
}).toString();
if (cursor !== undefined) url.searchParams.set("cursor", cursor);
const response = await request(url, {
signal: AbortSignal.timeout(30_000),
});
if (!response.ok)
throw new Error(`Registry lookup failed: HTTP ${response.status}`);
const page = await response.json();
assert(Array.isArray(page?.servers), "Registry response omitted servers");
assert(
page.metadata && typeof page.metadata === "object",
"Registry response omitted metadata",
);
for (const entry of page.servers) {
assert(
typeof entry?.server?.name === "string" &&
typeof entry.server.version === "string",
"Malformed registry server record",
);
if (
entry.server.name === server.name &&
entry.server.version === server.version
)
matches.push(entry);
}
cursor = page.metadata.nextCursor;
if (cursor !== undefined) {
assert(
typeof cursor === "string" && cursor.length > 0 && !cursors.has(cursor),
"Registry returned a non-advancing cursor",
);
cursors.add(cursor);
}
} while (cursor !== undefined);
assert(
matches.length <= 1,
"Registry returned ambiguous exact-version records",
);
if (matches.length === 0) return "absent";
assert.equal(
matches[0]._meta?.["io.modelcontextprotocol.registry/official"]?.status,
"active",
"Existing registry version is not active",
);
assert.deepEqual(
matches[0].server,
server,
"Existing registry version differs from released metadata",
);
return "present";
}
if (
process.argv[1] !== undefined &&
resolve(process.argv[1]) === fileURLToPath(import.meta.url)
) {
const state = await registryPublicationState(
JSON.parse(await readFile("server.json", "utf8")),
);
assert(process.env.GITHUB_OUTPUT, "GITHUB_OUTPUT is required");
await appendFile(
process.env.GITHUB_OUTPUT,
`exists=${state === "present"}\n`,
);
process.stdout.write(
`Registry version is ${state}; ${state === "present" ? "preserving identical published metadata" : "publication may proceed"}.\n`,
);
}
+24 -3
View File
@@ -24,7 +24,7 @@ Unresolved scope, authority, side effects, or required verification block
integration. Link the required maintainer scope decision for new providers or
capabilities involving target execution, injection, mutation, or protection
recovery; ordinary fixes within existing boundaries may state "not applicable".
Draft PRs may leave the scope decision pending and the final review checklist
Draft PRs may leave the scope decision pending and the readiness and merge checklists
unchecked. Maintainers can help resolve open questions.
## Contract and boundary impact
@@ -55,7 +55,10 @@ Unchecked items do not establish absence; leave uncertainties visible.
## Evidence and regression coverage
<!-- State whether evidence is executed, source-derived, or proposed. For fixes,
explain how the regression would fail on the affected base revision when practical. -->
show the original failure and explain how the regression would fail on the affected
base revision when practical. Include the most useful proof inline; link long logs
or artifacts, or put them in optional details. Documentation-only changes can use
source, link, or rendered-document checks. -->
- Tests added or updated:
- Base reproduction or other evidence:
@@ -71,7 +74,8 @@ For evidence-bearing changes:
## Validation performed
<!-- List only commands that actually ran, with observed results. Include the
relevant real-provider or platform workflow, or explain why it was unavailable. -->
relevant real-provider or platform workflow, or explain why it was unavailable.
Keep these results current after changes; a command without its result is incomplete. -->
- `command` — result
@@ -90,6 +94,23 @@ limits, security/privacy/containment implications, and meaningful proof gaps. --
- Package or release metadata impact:
- Security, privacy, process, or containment review:
## Author readiness
<!-- The human owner completes these declarations before requesting review.
The PR author is the owner by default. A different owner must explicitly accept
ownership on this PR.
AI agents must leave these declarations to a human to dictate to them or there could be catastrophic consequences for many users of REA.
Draft PRs may leave them unchecked. These declarations record initial readiness
when opening the PR for review; later pushes do not require a new human sign-off.
Keep the description and validation evidence current as the work changes.
See CONTRIBUTING.md#pull-request-ownership-and-readiness. -->
Human owner: <!-- @GitHub-handle -->
- [ ] I reviewed the final diff and relevant existing code, understand the change and its edge cases, and take responsibility for the complete PR, including AI-assisted work.
- [ ] I checked the evidence above, confirmed it supports the current change, and recorded meaningful validation gaps.
- [ ] I declare this PR ready for initial maintainer review.
## Before merge
<!-- Draft PRs may leave pending items unchecked. Resolve material scope and
+18
View File
@@ -0,0 +1,18 @@
name: Full PR qualification
on:
pull_request:
branches: [main, "release/*"]
types: [labeled]
permissions:
contents: read
actions: read
jobs:
qualification:
name: Qualify labeled PR revision
if: github.event.label.name == 'ci:full'
uses: ./.github/workflows/ci.yml
with:
source_sha: ${{ github.sha }}
+84
View File
@@ -0,0 +1,84 @@
name: CI timings
on:
schedule:
- cron: "17 * * * *"
workflow_dispatch:
concurrency:
group: ci-timings-${{ github.repository }}
cancel-in-progress: false
permissions:
contents: read
actions: read
pull-requests: read
jobs:
report:
name: Collect recent completed workflow attempts
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
# Consume only API data and execute batch code from the trusted default branch.
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
with:
ref: ${{ github.event.repository.default_branch }}
persist-credentials: false
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
- name: Cache npm package downloads
id: npm-cache
continue-on-error: true
uses: ./.github/actions/npm-cache
- name: Diagnose missing and startup-failed PR CI
continue-on-error: true
env:
GH_TOKEN: ${{ github.token }}
CI_REPOSITORY: ${{ github.repository }}
CI_REPOSITORY_ID: ${{ github.repository_id }}
CI_DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
run: node scripts/ci/pr-ci-health.mjs
- name: Record queue, execution, and matched step timing
continue-on-error: true
env:
GH_TOKEN: ${{ github.token }}
CI_REPOSITORY: ${{ github.repository }}
run: node scripts/ci/job-timings-batch.mjs
- name: Upload CI timing artifacts
if: always() && hashFiles('.cache/ci-job-timings/*') != ''
continue-on-error: true
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: ci-job-timings-batch
path: .cache/ci-job-timings/
include-hidden-files: true
if-no-files-found: error
overwrite: true
retention-days: 14
- name: Install dependencies for static failure attribution
id: failure-deps
if: ${{ !cancelled() && hashFiles('.cache/ci-job-timings/batch-manifest.json') != '' }}
continue-on-error: true
run: npm ci
- name: Attribute supported static failures against main
if: ${{ !cancelled() && steps.failure-deps.outcome == 'success' && hashFiles('.cache/ci-job-timings/batch-manifest.json') != '' }}
continue-on-error: true
env:
GH_TOKEN: ${{ github.token }}
CI_REPOSITORY: ${{ github.repository }}
CI_DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
run: node scripts/ci/failure-report.mjs
- name: Upload CI health and failure diagnostics
if: ${{ !cancelled() }}
continue-on-error: true
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: ci-health-diagnostics
path: |
.cache/ci-pr-health/
.cache/ci-failures/
include-hidden-files: true
if-no-files-found: warn
retention-days: 14
+413 -139
View File
@@ -5,42 +5,45 @@ on:
branches: [main]
pull_request:
branches: [main, "release/*"]
schedule:
- cron: "23 0-2,4-23 * * *"
- cron: "23 3 * * *"
workflow_dispatch:
workflow_call:
inputs:
source_sha:
description: Exact qualification source; must match the caller revision.
required: true
type: string
permissions:
contents: read
concurrency:
group: ci-${{ github.event.pull_request.number || github.ref }}
# The latest main push verifies the full tree, including earlier changes.
# Publication uses its own release workflow and concurrency group.
cancel-in-progress: true
# Complete qualification finishes even while main receives more pushes.
# Main feedback also finishes, coalescing newer pushes to one pending run.
# Scheduled/manual main runs coalesce to one active and the latest pending;
# release and label-triggered qualification use exact-revision groups.
group: ci-${{ (github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || inputs.source_sha != '' || contains(github.event.pull_request.labels.*.name, 'ci:full')) && 'qualification' || 'feedback' }}-${{ inputs.source_sha || github.event.pull_request.number || github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' && inputs.source_sha == '' && !contains(github.event.pull_request.labels.*.name, 'ci:full') }}
env:
HUSKY: "0"
jobs:
pr-title:
name: Conventional PR title
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
steps:
- name: Validate title for Release Please
env:
PR_TITLE: ${{ github.event.pull_request.title }}
run: >-
node -e 'const title = process.env.PR_TITLE ?? "";
const conventional = /^(?:feat|fix|docs|refactor|perf|test|build|ci|chore|revert)(?:\([a-z0-9._/-]+\))?!?: .+/u;
if (!conventional.test(title)) {
console.error("PR title must use Conventional Commits, for example: feat: add historical source import");
process.exit(1);
}'
changes:
name: Classify changes
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
timeout-minutes: 5
permissions:
contents: read
actions: read
outputs:
tier: ${{ steps.plan.outputs.tier }}
qualification_run_id: ${{ steps.qualification.outputs.run_id }}
code: ${{ steps.plan.outputs.code }}
reverse_tools: ${{ steps.plan.outputs.reverse_tools }}
sqlite: ${{ steps.plan.outputs.sqlite }}
runtime: ${{ steps.plan.outputs.runtime }}
package: ${{ steps.plan.outputs.package }}
apple: ${{ steps.plan.outputs.apple }}
@@ -57,39 +60,97 @@ jobs:
web_runtime: ${{ steps.plan.outputs.web_runtime }}
web_source_map: ${{ steps.plan.outputs.web_source_map }}
nativeaot: ${{ steps.plan.outputs.nativeaot }}
ghidra_bridge: ${{ steps.plan.outputs.ghidra_bridge }}
website: ${{ steps.plan.outputs.website }}
matrix: ${{ steps.plan.outputs.matrix }}
test_selection: ${{ steps.plan.outputs.test_selection }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
with:
ref: ${{ inputs.source_sha || github.sha }}
fetch-depth: 0
- uses: actions/setup-node@v4
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
- name: Bind qualification to the caller revision
if: inputs.source_sha != ''
env:
SOURCE_SHA: ${{ inputs.source_sha }}
run: |
test "${SOURCE_SHA}" = "${GITHUB_SHA}"
test "${SOURCE_SHA}" = "$(git rev-parse HEAD)"
- name: Find a verified ancestor for push selection
id: baseline
if: github.event_name == 'push' && inputs.source_sha == ''
env:
GH_TOKEN: ${{ github.token }}
HEAD_SHA: ${{ github.sha }}
CI_REPOSITORY: ${{ github.repository }}
CI_DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
run: node scripts/ci/baseline.mjs
- name: Admit scheduled full qualification
id: qualification
if: github.event_name == 'schedule' && inputs.source_sha == ''
env:
GH_TOKEN: ${{ github.token }}
HEAD_SHA: ${{ github.sha }}
CI_REPOSITORY: ${{ github.repository }}
CI_REPOSITORY_ID: ${{ github.repository_id }}
CI_DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
CI_FORCE_FULL: ${{ github.event.schedule == '23 3 * * *' }}
run: node scripts/ci/qualification.mjs
- name: Select verification lanes
id: plan
env:
BASE_SHA: ${{ github.event.pull_request.base.sha }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
CI_FULL: ${{ contains(github.event.pull_request.labels.*.name, 'ci:full') }}
BASE_SHA: ${{ steps.baseline.outputs.base || github.event.pull_request.base.sha }}
BASELINE_REASON: ${{ steps.baseline.outputs.reason }}
HEAD_SHA: ${{ inputs.source_sha || github.event.pull_request.head.sha || github.sha }}
CI_FULL: ${{ inputs.source_sha != '' || contains(github.event.pull_request.labels.*.name, 'ci:full') }}
CI_SKIP_FULL: ${{ steps.qualification.outputs.run_full == 'false' }}
run: node scripts/ci/plan.mjs
static:
name: Static checks
needs: changes
if: ${{ !cancelled() }}
if: ${{ !cancelled() && needs.changes.outputs.tier != 'reused' }}
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Require successful change classification
env:
CHANGES_RESULT: ${{ needs.changes.result }}
run: test "${CHANGES_RESULT}" = "success"
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
cache: npm
- name: Cache npm package downloads
id: npm-cache
uses: ./.github/actions/npm-cache
- run: npm ci
- name: Restore static task cache
if: needs.changes.outputs.code == 'true'
id: static-cache
uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: .turbo/cache/
key: rea-static-v1-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('.nvmrc', '.npmrc', 'package-lock.json') }}-${{ github.sha }}
# Turbo validates each task's inputs after restoring this snapshot.
restore-keys: rea-static-v1-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('.nvmrc', '.npmrc', 'package-lock.json') }}-
- name: Validate workflow syntax and expressions
if: needs.changes.outputs.workflows == 'true'
run: go run github.com/rhysd/actionlint/cmd/actionlint@v1.7.7 -shellcheck= -pyflakes=
# Code tiers run this source-only policy test in their existing shards.
# Workflow-only changes need the guard, without preparing runtime artifacts.
- name: Check action pins for workflow-only changes
if: needs.changes.outputs.workflows == 'true' && needs.changes.outputs.code != 'true'
run: node scripts/run-exact-tests.mjs run tests/boundary/filesystem/workflowActionPinning.test.ts
- name: Record static task source and execution identity
continue-on-error: true
if: needs.changes.outputs.code == 'true'
env:
CI_PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }}
run: npm run ci:static-context
- if: needs.changes.outputs.code == 'true'
run: npm run check:ci
- if: needs.changes.outputs.code == 'true'
@@ -99,9 +160,18 @@ jobs:
run: npm run test:python
- if: needs.changes.outputs.code != 'true'
run: npm run format:check
- name: Save validated static task cache
if: >-
success() && needs.changes.outputs.code == 'true' &&
steps.npm-cache.outputs.can-save == 'true' &&
steps.static-cache.outputs.cache-hit != 'true'
uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: .turbo/cache/
key: ${{ steps.static-cache.outputs.cache-primary-key }}
- name: Upload complete static diagnostics
if: always() && needs.changes.outputs.code == 'true'
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: static-check-reports
path: .cache/static-checks/
@@ -109,82 +179,168 @@ jobs:
if-no-files-found: ignore
retention-days: 7
test-source:
name: Source tests
needs: changes
if: needs.changes.outputs.code == 'true' && needs.changes.outputs.tier == 'fast'
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
- name: Cache npm package downloads
id: npm-cache
uses: ./.github/actions/npm-cache
- run: npm ci
- name: Run source tests without waiting for build
run: npm run test:fast -- --reporter=default --reporter=blob
env:
VITEST_BLOB_LABEL: source
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
if: ${{ !cancelled() }}
with:
name: vitest-report-source
path: .vitest/blob/*
include-hidden-files: true
if-no-files-found: error
retention-days: 7
test-shard:
name: Test shard (${{ matrix.shard }})
needs: [changes, build]
if: needs.changes.outputs.code == 'true'
runs-on: ubuntu-latest
timeout-minutes: 30
strategy:
fail-fast: false
# Failed PRs release the remaining matrix slots; qualification retains
# every shard's diagnostics and coverage. Passing PRs run the full suite.
fail-fast: ${{ github.event_name == 'pull_request' && needs.changes.outputs.tier == 'fast' }}
matrix:
shard: [1/6, 2/6, 3/6, 4/6, 5/6, 6/6]
# Fast feedback spends the same six test jobs on one source group and
# five built groups; full qualification retains six complete shards.
shard: ${{ fromJSON(needs.changes.outputs.tier == 'fast' && '["1/5","2/5","3/5","4/5","5/5"]' || '["1/6","2/6","3/6","4/6","5/6","6/6"]') }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
cache: npm
- name: Cache npm package downloads
id: npm-cache
uses: ./.github/actions/npm-cache
- run: npm ci
- uses: actions/download-artifact@v4
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: rea-test-runtime
path: .
- run: npm run test:ci:shard:run -- --shard=${{ matrix.shard }}
- name: Run complete portable suite with coverage
if: needs.changes.outputs.tier == 'full'
run: npm run test:ci:shard:run -- --shard=${{ matrix.shard }}
env:
REA_CI_TEST_HISTORY: .cache/ci-test-history/test-results.json
- uses: actions/upload-artifact@v4
- name: Run built-dependent tests for fast feedback
if: needs.changes.outputs.tier == 'fast'
run: npm run test:built -- --reporter=default --reporter=blob --shard=${{ matrix.shard }}
env:
VITEST_BLOB_LABEL: built
REA_CI_TEST_HISTORY: .cache/ci-test-history/test-results.json
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
if: ${{ !cancelled() }}
with:
name: vitest-report-${{ strategy.job-index }}
name: vitest-report-built-${{ strategy.job-index }}
path: .vitest/blob/*
include-hidden-files: true
if-no-files-found: error
overwrite: true
retention-days: 7
reverse-tools-quality:
name: Reverse tools quality
needs: [changes, static]
if: needs.changes.outputs.reverse_tools == 'true'
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
- name: Cache npm package downloads
id: npm-cache
uses: ./.github/actions/npm-cache
- run: npm ci
- name: Run reverse tools tests with coverage
run: npm run test:coverage:reverse-tools
- name: Preflight both reverse tools mutation configurations
run: |
node scripts/prepare-reverse-tools-mutation.mjs
npx --no-install stryker run stryker.config.mjs --dryRunOnly
npx --no-install stryker run stryker.semantics.config.mjs --dryRunOnly
- name: Retain available reverse tools diagnostics
if: ${{ !cancelled() }}
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: reverse-tools-quality-${{ needs.changes.outputs.tier }}-${{ github.run_attempt }}
path: |
coverage/reverse-tools/
reports/mutation/
if-no-files-found: warn
retention-days: 7
build:
name: Build package
needs: changes
if: needs.changes.outputs.code == 'true'
# Reusable provider workflows consume this run's portable build artifact,
# even when a workflow-only edit selects the lane without portable tests.
if: >-
needs.changes.outputs.code == 'true' ||
needs.changes.outputs.browser == 'true' ||
needs.changes.outputs.binary_layout == 'true' ||
needs.changes.outputs.evm == 'true' ||
needs.changes.outputs.javascript_recovery == 'true' ||
needs.changes.outputs.recorded_crash == 'true' ||
needs.changes.outputs.web_network == 'true' ||
needs.changes.outputs.web_runtime == 'true' ||
needs.changes.outputs.web_source_map == 'true'
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: read
actions: read
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
cache: npm
- name: Cache npm package downloads
id: npm-cache
uses: ./.github/actions/npm-cache
- run: npm ci
- name: Reuse portable build outputs across runs
uses: ./.github/actions/portable-build-cache
with:
save: "true"
save: ${{ github.event_name != 'pull_request' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }}
- run: npm run test:prepare
- name: Find successful default-branch timing history
- name: Find available trusted full-suite timing history
id: history
continue-on-error: true
env:
GH_TOKEN: ${{ github.token }}
CI_REPOSITORY: ${{ github.repository }}
CI_REPOSITORY_ID: ${{ github.repository_id }}
CI_DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
run: |
run_id="$(gh api --method GET \
"repos/${CI_REPOSITORY}/actions/workflows/ci.yml/runs" \
-f branch="${CI_DEFAULT_BRANCH}" -f event=push -f status=success -f per_page=1 \
--jq '.workflow_runs[0].id // empty')"
echo "run-id=${run_id}" >> "${GITHUB_OUTPUT}"
run: node scripts/ci/test-history.mjs
- name: Download previous timing report
if: steps.history.outputs.run-id != ''
continue-on-error: true
uses: actions/download-artifact@v4
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: test-timings
run-id: ${{ steps.history.outputs.run-id }}
github-token: ${{ github.token }}
path: .cache/ci-test-history
- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: rea-test-runtime
path: |
@@ -201,7 +357,7 @@ jobs:
# test:prepare caches only portable JS, skill, and catalog outputs.
# Native package jobs still run their build and prepack checks; Turbo
# restores matching outputs or rebuilds when their inputs differ.
- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: rea-build-cache
path: .turbo/cache/
@@ -210,38 +366,51 @@ jobs:
retention-days: 7
sqlite-runtime:
needs: [changes, build]
if: needs.changes.outputs.code == 'true'
# Preserve all selected coverage, but admit expensive host/provider work
# only after static validation. Portable tests still provide early feedback.
needs: [changes, static, build]
if: needs.changes.outputs.sqlite == 'true'
uses: ./.github/workflows/sqlite-runtime.yml
test:
name: Test (coverage + timing)
needs: [changes, test-shard]
if: ${{ !cancelled() }}
name: Portable tests
needs: [changes, test-source, test-shard]
if: ${{ !cancelled() && needs.changes.outputs.tier != 'reused' }}
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Require successful change classification
env:
CHANGES_RESULT: ${{ needs.changes.result }}
run: test "${CHANGES_RESULT}" = "success"
- uses: actions/checkout@v4
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
if: needs.changes.outputs.code == 'true' && success()
- uses: actions/setup-node@v4
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
if: needs.changes.outputs.code == 'true' && success()
with:
node-version-file: .nvmrc
cache: npm
- name: Cache npm package downloads
if: needs.changes.outputs.code == 'true' && success()
id: npm-cache
uses: ./.github/actions/npm-cache
- if: needs.changes.outputs.code == 'true' && success()
run: npm ci
- uses: actions/download-artifact@v4
if: needs.changes.outputs.code == 'true' && success()
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
if: >-
!cancelled() && needs.changes.outputs.code == 'true' &&
(needs.test-shard.result == 'success' || needs.test-shard.result == 'failure' ||
(needs.changes.outputs.tier == 'fast' &&
(needs.test-source.result == 'success' || needs.test-source.result == 'failure')))
with:
path: .vitest/blob
pattern: vitest-report-*
merge-multiple: true
- name: Merge coverage, JUnit, and timing reports
id: merge
if: needs.changes.outputs.code == 'true' && success()
if: >-
!cancelled() && needs.changes.outputs.code == 'true' &&
needs.changes.outputs.tier == 'full' &&
(needs.test-shard.result == 'success' || needs.test-shard.result == 'failure')
run: >-
npm run test:ci:merge --
--coverage.reportsDirectory=coverage
@@ -251,27 +420,51 @@ jobs:
--reporter=default
--reporter=junit
--reporter=json
--reporter=./scripts/ci/test-timing-reporter.mjs
--outputFile.junit=test-results.xml
--outputFile.json=test-results.json
- name: Merge fast-suite JUnit and timing reports
id: merge-fast
if: >-
!cancelled() && needs.changes.outputs.code == 'true' &&
needs.changes.outputs.tier == 'fast' &&
(needs.test-shard.result == 'success' || needs.test-shard.result == 'failure' ||
needs.test-source.result == 'success' || needs.test-source.result == 'failure')
run: >-
npm exec -- vitest --merge-reports
--reporter=default
--reporter=junit
--reporter=json
--reporter=./scripts/ci/test-timing-reporter.mjs
--outputFile.junit=test-results.xml
--outputFile.json=test-results.json
- name: Summarize slowest test files
if: needs.changes.outputs.code == 'true' && !cancelled() && hashFiles('test-results.json') != ''
if: needs.changes.outputs.code == 'true' && !cancelled()
env:
SHARD_RESULT: ${{ needs.test-shard.result }}
MERGE_RESULT: ${{ steps.merge.outcome }}
SOURCE_REQUIRED: ${{ needs.changes.outputs.tier == 'fast' }}
SOURCE_RESULT: ${{ needs.test-source.result }}
MERGE_RESULT: ${{ needs.changes.outputs.tier == 'full' && steps.merge.outcome || steps.merge-fast.outcome }}
CI_VERIFY_TEST_INVENTORY: "true"
CI_TEST_SELECTION: ${{ needs.changes.outputs.test_selection }}
CI_PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }}
run: node scripts/ci/test-report.mjs
- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
if: needs.changes.outputs.code == 'true' && !cancelled() && hashFiles('test-results.json') != ''
with:
name: test-results
path: |
test-results.xml
test-results.json
test-timings.json
coverage/
.cache/ci-test-selection/report.json
include-hidden-files: true
if-no-files-found: error
retention-days: 30
- name: Publish successful timing history
if: needs.changes.outputs.code == 'true' && success() && needs.test-shard.result == 'success'
uses: actions/upload-artifact@v4
if: needs.changes.outputs.code == 'true' && needs.changes.outputs.tier == 'full' && success() && needs.test-shard.result == 'success'
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: test-timings
path: .cache/ci-test-history/test-results.json
@@ -283,28 +476,34 @@ jobs:
env:
CODE_REQUIRED: ${{ needs.changes.outputs.code }}
SHARD_RESULT: ${{ needs.test-shard.result }}
run: test "${CODE_REQUIRED}" != "true" || test "${SHARD_RESULT}" = "success"
SOURCE_REQUIRED: ${{ needs.changes.outputs.tier == 'fast' }}
SOURCE_RESULT: ${{ needs.test-source.result }}
run: |
if [ "${CODE_REQUIRED}" = "true" ]; then
test "${SHARD_RESULT}" = "success" || exit 1
test "${SOURCE_REQUIRED}" != "true" || test "${SOURCE_RESULT}" = "success"
fi
conformance-fixtures:
name: Source-built conformance fixtures
needs: changes
needs: [changes, static]
if: needs.changes.outputs.fixtures == 'true'
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- name: Verify fixtures from a clean checkout
run: npm run verify:fixtures
windows-native-build:
name: Build bundled Windows x64 native controls
needs: changes
needs: [changes, static]
if: needs.changes.outputs.runtime == 'true'
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
- name: Prepare cross compiler
@@ -321,12 +520,27 @@ jobs:
- run: npm run build:windows-native
- run: npm run verify:windows-native:artifact
- run: node scripts/build-windows-native.mjs --inspection-fixture
- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: windows-native-inspection-fixture
path: .cache/windows-native-inspection-fixture/
if-no-files-found: error
- uses: actions/upload-artifact@v4
# The process boundary observations are independent of the addon: they
# report the caller's and child's own token, environment and breakaway
# results from a separate executable. Cross-build it in the lane that
# already has MinGW so the Windows job can observe them too.
- name: Build Windows process boundary fixture
run: |
mkdir -p .cache/windows-process-boundary
x86_64-w64-mingw32-g++ -municode -std=c++17 -O2 -static -Wall -Wextra \
-o .cache/windows-process-boundary/process-boundary.exe \
tests/fixtures/windows/processBoundary.cc -ladvapi32
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: windows-process-boundary-fixture
path: .cache/windows-process-boundary/
if-no-files-found: error
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: windows-native-controls
path: native/windows/build/
@@ -334,7 +548,7 @@ jobs:
package-e2e:
name: Native package baseline (${{ matrix.platform }} ${{ matrix.architecture }})
needs: [changes, build, windows-native-build]
needs: [changes, static, build, windows-native-build]
if: needs.changes.outputs.runtime == 'true'
strategy:
fail-fast: false
@@ -349,11 +563,13 @@ jobs:
NODE_OPTIONS: "--max-old-space-size=1024"
UV_THREADPOOL_SIZE: "2"
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
cache: npm
- name: Cache npm package downloads
id: npm-cache
uses: ./.github/actions/npm-cache
- name: Verify native runtime identity
env:
EXPECTED_CI_PLATFORM: ${{ matrix.platform }}
@@ -364,41 +580,51 @@ jobs:
assert.equal(process.arch, process.env.EXPECTED_CI_ARCHITECTURE);
console.log(JSON.stringify({ platform: process.platform, architecture: process.arch, node: process.version }));"
- run: npm ci
- uses: actions/download-artifact@v4
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: rea-build-cache
path: .turbo/cache/
- uses: actions/download-artifact@v4
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: windows-native-controls
path: native/windows/build/
- run: npm run verify:windows-native:artifact
- name: Resolve npm cache before verifier HOME isolation
id: npm-cache
run: |
cache_path="$(npm config get cache)"
echo "path=${cache_path}" >> "${GITHUB_OUTPUT}"
- name: Verify NativeAOT diagnostic applicability on this host
run: npm run test:focused -- tests/evaluation/package/nativeAotDiagnostics.test.ts
- run: npm run verify:package
env:
npm_config_cache: ${{ steps.npm-cache.outputs.path }}
REA_VERIFIED_PACKAGE_DIRECTORY: ${{ needs.changes.outputs.tier == 'full' && matrix.platform == 'linux' && matrix.architecture == 'x64' && '.cache/verified-package' || '' }}
- name: Verify real Node Inspector discovery and observation
timeout-minutes: 10
run: npm run verify:inspector
- name: Retain the exact qualified release package
if: needs.changes.outputs.tier == 'full' && matrix.platform == 'linux' && matrix.architecture == 'x64'
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: verified-release-package
path: .cache/verified-package/
include-hidden-files: true
if-no-files-found: error
overwrite: true
retention-days: 7
apple-artifacts:
name: Real Apple artifact CLI/MCP E2E
needs: [changes, build]
needs: [changes, static, build]
if: needs.changes.outputs.apple == 'true'
runs-on: macos-15
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
cache: npm
- name: Cache npm package downloads
id: npm-cache
uses: ./.github/actions/npm-cache
- run: npm ci
- uses: actions/download-artifact@v4
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: rea-build-cache
path: .turbo/cache/
@@ -427,6 +653,7 @@ jobs:
tests/boundary/cli/nativeSignatureCli.test.ts
tests/boundary/mcp/nativeSignatureTargetBindingMcp.test.ts
src/process/capture/FilesystemSnapshot.test.ts
tests/process-global/filesystemSnapshotFallbackCancellation.test.ts
tests/boundary/process/darwinProcessRunTokenReader.test.ts
tests/boundary/process/directAnalysisSignals.test.ts
tests/boundary/process/processCaptureLifecycle.test.ts
@@ -435,16 +662,18 @@ jobs:
reconstruction-readiness:
name: Reconstruction readiness
needs: changes
needs: [changes, static]
if: needs.changes.outputs.readiness == 'true'
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
cache: npm
- name: Cache npm package downloads
id: npm-cache
uses: ./.github/actions/npm-cache
- run: npm ci
- name: Run the public CLI/MCP reconstruction journey
env:
@@ -452,7 +681,7 @@ jobs:
run: |
set -o pipefail
npm run verify:readiness | tee readiness-summary.json
- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
if: ${{ !cancelled() }}
with:
name: reconstruction-readiness
@@ -464,23 +693,25 @@ jobs:
windows-curated:
name: Windows capability and package lane
needs: [changes, build, windows-native-build]
needs: [changes, static, build, windows-native-build]
if: needs.changes.outputs.package == 'true'
runs-on: windows-latest
timeout-minutes: 25
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
cache: npm
- name: Cache npm package downloads
id: npm-cache
uses: ./.github/actions/npm-cache
- run: npm ci
- uses: actions/download-artifact@v4
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: rea-build-cache
path: .turbo/cache/
- run: npm run build:cached
- uses: actions/download-artifact@v4
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: windows-native-controls
path: native/windows/build/
@@ -503,15 +734,21 @@ jobs:
- name: Run curated Windows capability tests
run: >-
npx vitest run --coverage.enabled=false
src/process/capture/FilesystemSnapshot.test.ts
tests/process-global/filesystemSnapshotFallbackCancellation.test.ts
tests/boundary/filesystem/binaryTarget.test.ts
tests/boundary/filesystem/safeOutputTree.test.ts
tests/boundary/providers/native/artifactProvider.archiveSafety.test.ts
tests/boundary/providers/native/artifactProvider.extractionCompleteness.test.ts
tests/boundary/filesystem/setupUnavailableProvider.test.ts
tests/boundary/filesystem/windowsNpxRegistration.test.ts
tests/boundary/android/workspaceGuardianRetry.test.ts
tests/acceptance/setup/homeDirectoryFallback.test.ts
tests/boundary/filesystem/javascriptApplicationWorkflows.test.ts
tests/boundary/filesystem/webScriptManifestPaths.test.ts
tests/boundary/process/ProcessEvidence.host.test.ts
tests/boundary/process/packageNpmInvocation.test.ts
tests/boundary/process/windowsOutputCapture.test.ts
tests/acceptance/webModuleCli.test.ts
tests/acceptance/webSourceLocationCli.test.ts
tests/boundary/cli/referenceSourceImportStatus.test.ts
@@ -534,8 +771,17 @@ jobs:
src/application/Update.test.ts
tests/process-global/updateMaintenance.test.ts
tests/conformance/providers/ghidra/realGhidraWindowsWorkflow.test.ts
- run: npm run verify:windows-native
- uses: actions/download-artifact@v4
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: windows-process-boundary-fixture
path: .cache/windows-process-boundary/
# Passing the fixture adds the breakaway, caller/child token, environment
# semantics and ancestor reparse observations, which the lane skips
# entirely when it is absent.
- run: >-
npm run verify:windows-native -- .
"${{ github.workspace }}\.cache\windows-process-boundary\process-boundary.exe"
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: windows-native-inspection-fixture
path: .cache/windows-native-inspection-fixture/
@@ -554,7 +800,7 @@ jobs:
tests/boundary/filesystem/directoryArtifactReaderOpen.test.ts
tests/boundary/filesystem/javascriptPackagePrecedence.test.ts
tests/boundary/filesystem/javascriptArtifactContentIdentity.test.ts
- uses: actions/setup-node@v4
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: 22.19.0
- name: Verify native path boundaries on Node 22
@@ -564,7 +810,7 @@ jobs:
tests/boundary/filesystem/javascriptPackagePrecedence.test.ts
tests/boundary/filesystem/javascriptArtifactContentIdentity.test.ts
tests/boundary/process/sourceMapUncHosts.test.ts
- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
if: always()
with:
name: windows-capabilities
@@ -576,17 +822,20 @@ jobs:
needs: changes
if: needs.changes.outputs.docs == 'true'
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
cache: npm
- name: Cache npm package downloads
id: npm-cache
uses: ./.github/actions/npm-cache
- run: npm ci
- run: npm run docs:check
- name: Generation leaves authored files unchanged
run: git diff --exit-code
- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: generated-docs
path: |
@@ -595,76 +844,89 @@ jobs:
skills/reverse-engineer-anything
if-no-files-found: error
workflows:
name: Validate workflows
needs: changes
if: needs.changes.outputs.workflows == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Validate workflow syntax and expressions
run: go run github.com/rhysd/actionlint/cmd/actionlint@v1.7.7 -shellcheck= -pyflakes=
browser:
needs: changes
needs: [changes, static, build]
if: needs.changes.outputs.browser == 'true'
uses: ./.github/workflows/real-browser.yml
with:
build_cache_artifact: rea-build-cache
binary_layout:
needs: changes
needs: [changes, static, build]
if: needs.changes.outputs.binary_layout == 'true'
uses: ./.github/workflows/real-binary-layout.yml
with:
build_cache_artifact: rea-build-cache
evm:
needs: changes
needs: [changes, static, build]
if: needs.changes.outputs.evm == 'true'
uses: ./.github/workflows/real-evm-interface.yml
with:
build_cache_artifact: rea-build-cache
javascript_recovery:
needs: changes
needs: [changes, static, build]
if: needs.changes.outputs.javascript_recovery == 'true'
uses: ./.github/workflows/real-javascript-recovery.yml
with:
build_cache_artifact: rea-build-cache
recorded_crash:
needs: changes
needs: [changes, static, build]
if: needs.changes.outputs.recorded_crash == 'true'
uses: ./.github/workflows/real-recorded-crash.yml
with:
build_cache_artifact: rea-build-cache
web_network:
needs: changes
needs: [changes, static, build]
if: needs.changes.outputs.web_network == 'true'
uses: ./.github/workflows/real-web-network-captures.yml
with:
build_cache_artifact: rea-build-cache
web_runtime:
needs: changes
needs: [changes, static, build]
if: needs.changes.outputs.web_runtime == 'true'
uses: ./.github/workflows/real-web-runtime.yml
with:
build_cache_artifact: rea-build-cache
web_source_map:
needs: changes
needs: [changes, static, build]
if: needs.changes.outputs.web_source_map == 'true'
uses: ./.github/workflows/real-web-source-map.yml
with:
build_cache_artifact: rea-build-cache
nativeaot:
needs: changes
needs: [changes, static]
if: needs.changes.outputs.nativeaot == 'true'
uses: ./.github/workflows/nativeaot-fixtures.yml
ghidra_bridge:
needs: [changes, static]
if: needs.changes.outputs.ghidra_bridge == 'true'
uses: ./.github/workflows/ghidra-bridge-sources.yml
website:
needs: changes
needs: [changes, static]
if: needs.changes.outputs.website == 'true'
uses: ./.github/workflows/website-check.yml
required:
name: CI required
# Report failed dependencies, but release superseded PR concurrency slots.
if: ${{ !cancelled() }}
needs:
[
changes,
pr-title,
static,
build,
reverse-tools-quality,
test-shard,
test-source,
sqlite-runtime,
test,
conformance-fixtures,
@@ -674,7 +936,6 @@ jobs:
reconstruction-readiness,
windows-curated,
docs,
workflows,
browser,
binary_layout,
evm,
@@ -684,15 +945,28 @@ jobs:
web_runtime,
web_source_map,
nativeaot,
ghidra_bridge,
website,
]
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
timeout-minutes: 5
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
- name: Require every selected lane to pass
id: gate
env:
CI_JOBS: ${{ toJSON(needs) }}
CI_QUALIFICATION_DIRECTORY: .cache/ci-qualification
run: node scripts/ci/required.mjs
- name: Upload full qualification receipt artifact
if: needs.changes.outputs.tier == 'full' && success()
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: ${{ steps.gate.outputs.receipt_name }}
path: ${{ steps.gate.outputs.receipt_path }}
if-no-files-found: error
overwrite: true
retention-days: 7
@@ -0,0 +1,48 @@
name: Ghidra bridge source compilation
on:
workflow_dispatch:
workflow_call:
permissions:
contents: read
env:
HUSKY: "0"
jobs:
ghidra-bridge-sources:
name: Compile packaged Ghidra bridge sources
runs-on: ubuntu-24.04
timeout-minutes: 15
env:
GHIDRA_RELEASE_URL: https://github.com/NationalSecurityAgency/ghidra/releases/download/Ghidra_12.1.4_build/ghidra_12.1.4_PUBLIC_20260921.zip
GHIDRA_RELEASE_SHA256: ddac49f903da9d5bac833e5cc79395098b9c33cfd3279be5f31bd00387d2d4db
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
- name: Validate the SDK pin against the provider's verified release
run: node scripts/verify-ghidra-bridge-sources.mjs --check-sdk-pin >> "$GITHUB_ENV"
# Only the jars and application.properties are extracted: the check
# compiles against them and never launches Ghidra or imports a target.
- name: Acquire checksum-verified Ghidra jars in isolated runner storage
shell: bash
run: |
ghidra_root="${RUNNER_TEMP}/rea-ghidra"
mkdir -p "${ghidra_root}"
curl -fsSL --retry 3 --write-out 'ghidra_download_bytes=%{size_download} ghidra_download_seconds=%{time_total}\n' -o "${ghidra_root}/ghidra.zip" "${GHIDRA_RELEASE_URL}"
echo "${GHIDRA_RELEASE_SHA256} ${ghidra_root}/ghidra.zip" | sha256sum -c -
unzip -q "${ghidra_root}/ghidra.zip" '*/Ghidra/*.jar' '*/Ghidra/application.properties' -d "${ghidra_root}"
rm "${ghidra_root}/ghidra.zip"
properties="$(find "${ghidra_root}" -path '*/Ghidra/application.properties' -print -quit)"
test -n "${properties}"
echo "GHIDRA_INSTALL_DIR=$(dirname "$(dirname "${properties}")")" >> "${GITHUB_ENV}"
# JAVA_HOME_<major>_X64 comes from the runner image, not the env context.
- name: Compile bridge sources with the verified runner JDK
shell: bash
run: |
java_home_variable="JAVA_HOME_${GHIDRA_JAVA_MAJOR}_X64"
test -n "${!java_home_variable}"
JAVA_HOME="${!java_home_variable}" npm run verify:ghidra:bridge-sources -- --verified-sdk
+62
View File
@@ -0,0 +1,62 @@
name: Mutation sweep
on:
schedule:
- cron: "47 4 * * *"
workflow_dispatch:
permissions:
contents: read
concurrency:
group: mutation-${{ github.ref }}
cancel-in-progress: false
env:
HUSKY: "0"
jobs:
mutation:
name: Mutation sweep (${{ matrix.suite }})
runs-on: ubuntu-latest
timeout-minutes: 60
strategy:
fail-fast: false
max-parallel: 2
matrix:
include:
- suite: reverse-tools
config: stryker.config.mjs
- suite: reverse-tools-semantics
config: stryker.semantics.config.mjs
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
- name: Cache npm package downloads
uses: ./.github/actions/npm-cache
- run: npm ci
- run: npm run test:prepare
- name: Run the complete configuration
env:
MUTATION_CONFIG: ${{ matrix.config }}
run: |
node scripts/prepare-reverse-tools-mutation.mjs
npx --no-install stryker run "${MUTATION_CONFIG}"
- name: Require both reports from the completed sweep
env:
REPORT_BASE: reports/mutation/${{ matrix.suite }}
run: |
test -s "${REPORT_BASE}.json"
test -s "${REPORT_BASE}.html"
- name: Retain available mutation diagnostics
if: ${{ !cancelled() }}
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: mutation-${{ matrix.suite }}-${{ github.run_attempt }}
path: |
reports/mutation/${{ matrix.suite }}.json
reports/mutation/${{ matrix.suite }}.html
if-no-files-found: warn
retention-days: 7
+6 -5
View File
@@ -12,22 +12,23 @@ jobs:
name: Build Windows x64 NativeAOT fixture
# .NET 8.0.22's bundled linker discovery supports VS 2019/2022.
runs-on: windows-2022
timeout-minutes: 15
# Two bounded restore attempts and one compile can each take five minutes.
timeout-minutes: 20
env:
DOTNET_CLI_TELEMETRY_OPTOUT: "1"
DOTNET_PROCESSOR_COUNT: "1"
COMPlus_GCHeapHardLimit: "30000000"
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
- uses: actions/setup-dotnet@v4
- uses: actions/setup-dotnet@67a3573c9a986a3f9c594539f4ab511d57bb3ce9 # v4.3.1
with:
dotnet-version: "8.0.416"
- name: Build benign fixture and independent byte oracle
run: node scripts/generate-nativeaot-fixtures.mjs
- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: nativeaot-win-x64-${{ github.event.pull_request.head.sha || github.sha }}
path: |
+5 -3
View File
@@ -19,10 +19,12 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
cache: npm
- name: Cache npm package downloads
id: npm-cache
uses: ./.github/actions/npm-cache
- run: npm ci
- run: npm run docs:build
+51
View File
@@ -0,0 +1,51 @@
name: PR readiness
on:
pull_request_target:
branches: [main, "release/*"]
types:
[
opened,
edited,
synchronize,
reopened,
ready_for_review,
converted_to_draft,
]
workflow_dispatch:
inputs:
pull_request_number:
description: Open PR to recheck
required: true
type: string
permissions: {}
concurrency:
group: pr-readiness-${{ github.event.pull_request.number || inputs.pull_request_number }}
cancel-in-progress: true
jobs:
readiness:
name: Validate author declaration
runs-on: ubuntu-24.04
timeout-minutes: 3
permissions:
contents: read
pull-requests: read
statuses: write
steps:
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
ref: ${{ github.event.repository.default_branch }}
persist-credentials: false
- name: Check initial readiness
uses: actions/github-script@373c709c69115d41ff229c7e5df9f8788daa9553 # v9
with:
script: |
const { pathToFileURL } = require("node:url");
const entry = pathToFileURL(
`${process.env.GITHUB_WORKSPACE}/scripts/ci/pr-readiness.mjs`,
);
const { runReadinessCheck } = await import(entry.href);
await runReadinessCheck({ github, context, core });
+29
View File
@@ -0,0 +1,29 @@
name: PR title
on:
pull_request:
branches: [main, "release/*"]
types: [opened, synchronize, reopened, edited]
permissions: {}
concurrency:
group: pr-title-${{ github.event.pull_request.number }}
cancel-in-progress: true
jobs:
pr-title:
name: Conventional PR title
runs-on: ubuntu-24.04
timeout-minutes: 2
steps:
- name: Validate title for Release Please
env:
PR_TITLE: ${{ github.event.pull_request.title }}
run: >-
node -e 'const title = process.env.PR_TITLE ?? "";
const conventional = /^(?:feat|fix|docs|refactor|perf|test|build|ci|chore|revert)(?:\([a-z0-9._/-]+\))?!?: .+/u;
if (!conventional.test(title)) {
console.error("PR title must use Conventional Commits, for example: feat: add historical source import");
process.exit(1);
}'
+13 -7
View File
@@ -8,14 +8,16 @@ on:
type: boolean
default: false
workflow_call:
inputs:
build_cache_artifact:
description: Portable build cache artifact from the calling CI run.
required: false
type: string
default: ""
permissions:
contents: read
concurrency:
group: real-binary-layout-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
env:
NODE_OPTIONS: --max-old-space-size=768
HUSKY: "0"
@@ -26,13 +28,17 @@ jobs:
runs-on: ubuntu-24.04
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
cache: npm
- name: Cache npm package downloads
id: npm-cache
uses: ./.github/actions/npm-cache
- run: npm ci
- uses: ./.github/actions/portable-build-cache
with:
build_cache_artifact: ${{ inputs.build_cache_artifact }}
- name: Acquire exact upstream libraries in an isolated runner environment
shell: bash
run: |
+13 -7
View File
@@ -2,12 +2,14 @@ name: Real browser verification
on:
workflow_call:
inputs:
build_cache_artifact:
description: Portable build cache artifact from the calling CI run.
required: false
type: string
default: ""
workflow_dispatch:
concurrency:
group: real-browser-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
env:
HUSKY: "0"
@@ -19,13 +21,17 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
cache: npm
- name: Cache npm package downloads
id: npm-cache
uses: ./.github/actions/npm-cache
- run: npm ci
- uses: ./.github/actions/portable-build-cache
with:
build_cache_artifact: ${{ inputs.build_cache_artifact }}
- run: npm run verify:browser
env:
REA_BROWSER_EXECUTABLE: /usr/bin/google-chrome
+76
View File
@@ -0,0 +1,76 @@
name: Real upstream Cutter verification
on:
workflow_dispatch:
repository_dispatch:
types: [real-cutter-upstream]
permissions:
contents: read
jobs:
windows-native-artifact:
name: Build Windows bridge security boundary
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
- name: Install the cross compiler used by the Windows native lane
run: |
sudo apt-get update
sudo apt-get install --no-install-recommends -y g++-mingw-w64-x86-64
- run: npm run build:windows-native
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: cutter-windows-native
path: native/windows/build/
if-no-files-found: error
cutter-upstream:
name: Cutter upstream smoke (${{ matrix.platform }})
needs: windows-native-artifact
runs-on: ${{ matrix.runner }}
timeout-minutes: 20
strategy:
fail-fast: false
matrix:
include:
- platform: linux
runner: [self-hosted, cutter-upstream, linux]
- platform: macOS
runner: [self-hosted, cutter-upstream, macOS]
- platform: Windows
runner: [self-hosted, cutter-upstream, Windows]
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
- name: Cache npm package downloads
id: npm-cache
uses: ./.github/actions/npm-cache
- run: npm ci
- if: matrix.platform == 'Windows'
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: cutter-windows-native
path: native/windows/build/
- if: matrix.platform == 'Windows'
run: npm run verify:windows-native:artifact
- if: matrix.platform == 'Windows'
run: npm run build:cached
- if: matrix.platform == 'Windows'
run: npm run verify:windows-native
- name: Verify host and real Cutter bridge
env:
EXPECTED_PLATFORM: ${{ matrix.platform }}
run: npm run verify:cutter
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
if: always()
with:
name: cutter-upstream-${{ matrix.platform }}
path: cutter-smoke.json
if-no-files-found: ignore
retention-days: 30
+15 -7
View File
@@ -3,14 +3,16 @@ name: Real offline EVM interface
on:
workflow_dispatch:
workflow_call:
inputs:
build_cache_artifact:
description: Portable build cache artifact from the calling CI run.
required: false
type: string
default: ""
permissions:
contents: read
concurrency:
group: real-evm-interface-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
env:
NODE_OPTIONS: --max-old-space-size=768
HUSKY: "0"
@@ -20,13 +22,17 @@ jobs:
runs-on: ubuntu-24.04
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
cache: npm
- name: Cache npm package downloads
id: npm-cache
uses: ./.github/actions/npm-cache
- run: npm ci
- uses: ./.github/actions/portable-build-cache
with:
build_cache_artifact: ${{ inputs.build_cache_artifact }}
- name: Acquire unchanged fixture compiler in isolated runner storage
shell: bash
run: |
@@ -39,3 +45,5 @@ jobs:
echo "REA_VERIFY_SOLC_MODULE=${evm_fixture_root}/node_modules/solc/index.js" >> "${GITHUB_ENV}"
- name: Verify source-owned compiled bytecode through public CLI and MCP
run: npm run verify:evm:interface
- name: Verify content-hash-free and CBOR-free owned compiler artifacts
run: npm run verify:solidity:metadata
+28 -8
View File
@@ -16,8 +16,8 @@ jobs:
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
- name: Prepare cross compiler
@@ -31,11 +31,25 @@ jobs:
sudo apt-get install --no-install-recommends -y g++-mingw-w64-x86-64
- run: npm run build:windows-native
- run: npm run verify:windows-native:artifact
- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: real-ghidra-windows-native
path: native/windows/build/
if-no-files-found: error
# The boundary observations need a separate Windows executable, which the
# native lane skips when it is absent. Cross-build it here, where MinGW is
# already installed, so the proof run observes them too.
- name: Build Windows process boundary fixture
run: |
mkdir -p .cache/windows-process-boundary
x86_64-w64-mingw32-g++ -municode -std=c++17 -O2 -static -Wall -Wextra \
-o .cache/windows-process-boundary/process-boundary.exe \
tests/fixtures/windows/processBoundary.cc -ladvapi32
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: real-ghidra-windows-process-boundary
path: .cache/windows-process-boundary/
if-no-files-found: error
verify:
name: Real Ghidra 12.1.4 Windows x64 PE proof
@@ -45,17 +59,23 @@ jobs:
runs-on: [self-hosted, Windows, x64, ghidra-12-1-2]
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
- run: npm ci
- uses: actions/download-artifact@v4
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: real-ghidra-windows-native
path: native/windows/build/
- run: npm run verify:windows-native:artifact
- run: npm run verify:windows-native
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: real-ghidra-windows-process-boundary
path: .cache/windows-process-boundary/
- run: >-
npm run verify:windows-native -- .
"${{ github.workspace }}\.cache\windows-process-boundary\process-boundary.exe"
- name: Verify all admitted Windows Ghidra operations
env:
REA_ANALYSIS_PROVIDER: ghidra
@@ -88,7 +108,7 @@ jobs:
env:
REA_ANALYSIS_PROVIDER: ghidra
run: npm run verify:ghidra:windows:package -- --x86 --dll | Tee-Object -FilePath windows-ghidra-x86-dll-package-proof.log
- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
if: always()
with:
name: windows-ghidra-proof
+6 -4
View File
@@ -44,12 +44,14 @@ jobs:
runs-on: ubuntu-24.04
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
cache: npm
- uses: actions/setup-go@v5
- name: Cache npm package downloads
id: npm-cache
uses: ./.github/actions/npm-cache
- uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5.6.0
with:
go-version: "1.26.1"
cache: false
+10 -6
View File
@@ -15,11 +15,13 @@ jobs:
runs-on: ubuntu-24.04
timeout-minutes: 25
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
cache: npm
- name: Cache npm package downloads
id: npm-cache
uses: ./.github/actions/npm-cache
- run: npm ci
- name: Pack CLI for setup verification
shell: bash
@@ -36,11 +38,13 @@ jobs:
runs-on: [self-hosted, Linux, hopper]
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
cache: npm
- name: Cache npm package downloads
id: npm-cache
uses: ./.github/actions/npm-cache
- run: npm ci
- name: Install packed CLI in isolation
shell: bash
+5 -3
View File
@@ -15,11 +15,13 @@ jobs:
runs-on: [self-hosted, macOS, hopper]
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
cache: npm
- name: Cache npm package downloads
id: npm-cache
uses: ./.github/actions/npm-cache
- run: npm ci
- name: Install packed CLI in isolation
shell: bash
+13 -7
View File
@@ -2,15 +2,17 @@ name: Real JavaScript recovery
on:
workflow_call:
inputs:
build_cache_artifact:
description: Portable build cache artifact from the calling CI run.
required: false
type: string
default: ""
workflow_dispatch:
permissions:
contents: read
concurrency:
group: javascript-recovery-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
env:
HUSKY: "0"
NODE_OPTIONS: "--max-old-space-size=768"
@@ -21,13 +23,17 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
cache: npm
- name: Cache npm package downloads
id: npm-cache
uses: ./.github/actions/npm-cache
- run: npm ci
- uses: ./.github/actions/portable-build-cache
with:
build_cache_artifact: ${{ inputs.build_cache_artifact }}
- name: Fetch pinned Wakaru release into the job workspace
shell: bash
run: |
+5 -3
View File
@@ -43,11 +43,13 @@ jobs:
runs-on: ubuntu-24.04
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
cache: npm
- name: Cache npm package downloads
id: npm-cache
uses: ./.github/actions/npm-cache
- run: sudo apt-get update && sudo DEBIAN_FRONTEND=noninteractive apt-get install -y tshark util-linux python3
- run: npm ci
- run: npm run build:cached
+13 -7
View File
@@ -3,14 +3,16 @@ name: Real recorded crash evidence
on:
workflow_dispatch:
workflow_call:
inputs:
build_cache_artifact:
description: Portable build cache artifact from the calling CI run.
required: false
type: string
default: ""
permissions:
contents: read
concurrency:
group: real-recorded-crash-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
env:
NODE_OPTIONS: --max-old-space-size=768
HUSKY: "0"
@@ -23,13 +25,17 @@ jobs:
runs-on: ubuntu-24.04
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
cache: npm
- name: Cache npm package downloads
id: npm-cache
uses: ./.github/actions/npm-cache
- run: npm ci
- uses: ./.github/actions/portable-build-cache
with:
build_cache_artifact: ${{ inputs.build_cache_artifact }}
- name: Install GDB on the disposable CI runner
shell: bash
run: |
+5 -3
View File
@@ -36,11 +36,13 @@ jobs:
runs-on: ubuntu-24.04
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
cache: npm
- name: Cache npm package downloads
id: npm-cache
uses: ./.github/actions/npm-cache
- run: sudo apt-get update && sudo apt-get install -y sigrok-cli util-linux python3
- run: npm ci
- run: npm run build:cached
+3 -3
View File
@@ -18,7 +18,7 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 45
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- name: Download the pinned Termux app and verify its release checksum
shell: bash
run: |
@@ -38,7 +38,7 @@ jobs:
sudo udevadm control --reload-rules
sudo udevadm trigger --name-match=kvm
- name: Build and capture a page inside Termux
uses: reactivecircus/android-emulator-runner@4c44018e59b437e86cdfc41da381398f93ed8808 # v2
uses: reactivecircus/android-emulator-runner@a421e43855164a8197daf9d8d40fe71c6996bb0d # v2.38.0
with:
api-level: 30
arch: x86_64
@@ -50,7 +50,7 @@ jobs:
script: bash scripts/verify/termux/emulator.sh
- name: Upload Termux and Android diagnostics
if: always()
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: termux-browser-diagnostics
path: ${{ runner.temp }}/rea-termux/diagnostics
@@ -3,14 +3,16 @@ name: Real historical web network captures
on:
workflow_dispatch:
workflow_call:
inputs:
build_cache_artifact:
description: Portable build cache artifact from the calling CI run.
required: false
type: string
default: ""
permissions:
contents: read
concurrency:
group: real-web-network-captures-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
env:
HUSKY: "0"
NODE_OPTIONS: --max-old-space-size=768
@@ -20,15 +22,19 @@ jobs:
runs-on: ubuntu-24.04
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
cache: npm
- name: Cache npm package downloads
id: npm-cache
uses: ./.github/actions/npm-cache
- name: Use pinned package manager
run: npm install --global npm@11.16.0
- run: npm ci
- uses: ./.github/actions/portable-build-cache
with:
build_cache_artifact: ${{ inputs.build_cache_artifact }}
- name: Acquire fixed upstream native profile in runner temporary storage
shell: bash
run: |
+14 -7
View File
@@ -1,12 +1,15 @@
name: Real website runtime attribution
on:
workflow_call:
inputs:
build_cache_artifact:
description: Portable build cache artifact from the calling CI run.
required: false
type: string
default: ""
workflow_dispatch:
permissions:
contents: read
concurrency:
group: real-web-runtime-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
env:
HUSKY: "0"
jobs:
@@ -14,14 +17,18 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
cache: npm
- name: Cache npm package downloads
id: npm-cache
uses: ./.github/actions/npm-cache
- run: npm ci
- uses: ./.github/actions/portable-build-cache
- run: npm run verify:browser:runtime
with:
build_cache_artifact: ${{ inputs.build_cache_artifact }}
- run: env -u DBUS_SESSION_BUS_ADDRESS npm run verify:browser:runtime
env:
REA_BROWSER_EXECUTABLE: /usr/bin/google-chrome
REA_BROWSER_NO_SANDBOX: "true"
+13 -6
View File
@@ -1,12 +1,15 @@
name: Real website source-map verification
on:
workflow_call:
inputs:
build_cache_artifact:
description: Portable build cache artifact from the calling CI run.
required: false
type: string
default: ""
workflow_dispatch:
permissions:
contents: read
concurrency:
group: real-web-source-map-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
env:
HUSKY: "0"
jobs:
@@ -14,13 +17,17 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
cache: npm
- name: Cache npm package downloads
id: npm-cache
uses: ./.github/actions/npm-cache
- run: npm ci
- uses: ./.github/actions/portable-build-cache
with:
build_cache_artifact: ${{ inputs.build_cache_artifact }}
- name: Install the pinned compiler into an isolated fixture directory
run: npm install --prefix "$RUNNER_TEMP/rea-web-source-map-compiler" --no-audit --no-fund --package-lock=false esbuild@0.25.10
- run: npm run verify:browser:source-maps
+109
View File
@@ -0,0 +1,109 @@
name: Recover published release
on:
workflow_dispatch:
inputs:
version:
description: Existing npm and GitHub release version
required: true
type: string
release_run_id:
description: Original Release run with successful exact-source qualification and npm publication
required: true
type: string
concurrency:
group: release-recovery-${{ inputs.version }}
cancel-in-progress: false
jobs:
verify:
name: Verify existing published package
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
timeout-minutes: 30
permissions:
contents: read
actions: read
outputs:
source_sha: ${{ steps.release.outputs.source_sha }}
version: ${{ steps.release.outputs.version }}
steps:
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
fetch-depth: 0
persist-credentials: false
- uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6.5.0
with:
node-version-file: .nvmrc
package-manager-cache: false
- run: npm ci
- name: Validate original release authority
id: release
env:
GH_TOKEN: ${{ github.token }}
RECOVERY_VERSION: ${{ inputs.version }}
RECOVERY_RUN_ID: ${{ inputs.release_run_id }}
run: node scripts/ci/release-recovery.mjs resolve
- name: Check out released metadata
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
ref: ${{ steps.release.outputs.source_sha }}
path: .cache/release-source
persist-credentials: false
- name: Download original qualified package
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: verified-release-package
run-id: ${{ steps.release.outputs.run_id }}
github-token: ${{ github.token }}
path: .cache/verified-package
- name: Restore original verifier runtime
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: rea-test-runtime
run-id: ${{ steps.release.outputs.run_id }}
github-token: ${{ github.token }}
path: .
- name: Match published npm bytes to the qualified package
env:
RECOVERY_VERSION: ${{ steps.release.outputs.version }}
RECOVERY_RUN_ID: ${{ steps.release.outputs.run_id }}
RECOVERY_RUN_ATTEMPT: ${{ steps.release.outputs.run_attempt }}
RECOVERY_SOURCE_SHA: ${{ steps.release.outputs.source_sha }}
RECOVERY_SOURCE_DIRECTORY: .cache/release-source
RECOVERY_ARTIFACT_DIRECTORY: .cache/verified-package
run: node scripts/ci/release-recovery.mjs verify
- name: Rehearse the original qualified tarball
env:
RECOVERY_VERSION: ${{ steps.release.outputs.version }}
run: node scripts/verify-release-artifact.mjs .cache/verified-package/package.tgz "$RECOVERY_VERSION"
- name: Verify published CLI, MCP and update
env:
RECOVERY_VERSION: ${{ steps.release.outputs.version }}
run: node scripts/verify-published-package.mjs "$RECOVERY_VERSION"
- name: Validate released registry metadata
working-directory: .cache/release-source
run: npm run mcp-registry:check
publish-mcp:
name: Finish MCP Registry publication
needs: verify
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
persist-credentials: false
- name: Check out verified released metadata
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
ref: ${{ needs.verify.outputs.source_sha }}
path: .cache/release-source
persist-credentials: false
- uses: ./.github/actions/publish-mcp
with:
directory: .cache/release-source
+79 -76
View File
@@ -48,7 +48,7 @@ jobs:
echo "frozen=true" >> "$GITHUB_OUTPUT"
fi
- uses: googleapis/release-please-action@v4
- uses: googleapis/release-please-action@5c625bfb5d1ff62eadeeb3772007f7f66fdcf071 # v4.4.1
if: steps.editorial.outputs.frozen != 'true'
with:
token: ${{ secrets.RELEASE_PLEASE_TOKEN || secrets.GITHUB_TOKEN }}
@@ -70,13 +70,13 @@ jobs:
sha: ${{ steps.release.outputs.sha }}
steps:
- name: Check out reviewed release merge
uses: actions/checkout@v6
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
ref: ${{ github.event.pull_request.merge_commit_sha }}
fetch-depth: 0
persist-credentials: false
- uses: actions/setup-node@v6
- uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6.5.0
with:
node-version-file: .nvmrc
package-manager-cache: false
@@ -104,7 +104,7 @@ jobs:
node scripts/release-notes.mjs publication --repository "$GH_REPO" \
--source "$SOURCE_SHA" --pr "$RELEASE_PR"
- uses: googleapis/release-please-action@v4
- uses: googleapis/release-please-action@5c625bfb5d1ff62eadeeb3772007f7f66fdcf071 # v4.4.1
id: release
with:
token: ${{ secrets.RELEASE_PLEASE_TOKEN || secrets.GITHUB_TOKEN }}
@@ -191,14 +191,14 @@ jobs:
fi
- name: Check out release controller and Git history
uses: actions/checkout@v6
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
ref: ${{ github.sha }}
fetch-depth: 0
persist-credentials: false
- name: Set up Node.js for checkpoint validation
uses: actions/setup-node@v6
uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6.5.0
with:
node-version-file: .nvmrc
package-manager-cache: false
@@ -226,7 +226,7 @@ jobs:
node scripts/release-notes.mjs publication --repository "$GH_REPO" \
--source "$SOURCE_SHA"
- uses: googleapis/release-please-action@v4
- uses: googleapis/release-please-action@5c625bfb5d1ff62eadeeb3772007f7f66fdcf071 # v4.4.1
id: release
with:
# A dedicated credential starts PR CI automatically; the built-in
@@ -255,7 +255,7 @@ jobs:
- name: Check out release pull request
if: inputs.phase == 'prepare' && steps.release.outputs.prs_created == 'true'
uses: actions/checkout@v6
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
ref: ${{ fromJSON(steps.release.outputs.pr).headBranchName }}
fetch-depth: 0
@@ -263,7 +263,7 @@ jobs:
- name: Set up Node.js for generated documentation
if: inputs.phase == 'prepare' && steps.release.outputs.prs_created == 'true'
uses: actions/setup-node@v6
uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6.5.0
with:
node-version-file: .nvmrc
package-manager-cache: false
@@ -290,24 +290,65 @@ jobs:
if: inputs.phase == 'prepare' && steps.release.outputs.prs_created == 'true'
run: npm run docs:check && git diff --exit-code
publish:
name: Publish to npm
qualification:
name: Qualify exact release revision
needs: [merged-release, release-please]
if: >-
always() && !cancelled() &&
((needs.merged-release.result == 'success' && needs.merged-release.outputs.release_created == 'true') ||
(needs.release-please.result == 'success' && inputs.phase == 'publish' && needs.release-please.outputs.release_created == 'true'))
permissions:
contents: read
actions: read
uses: ./.github/workflows/ci.yml
with:
source_sha: ${{ needs.merged-release.outputs.sha || needs.release-please.outputs.sha }}
rehearsal:
name: Rehearse qualified release on a clean runner
needs: [merged-release, release-please, qualification]
if: >-
always() && !cancelled() && needs.qualification.result == 'success'
runs-on: ubuntu-latest
timeout-minutes: 15
permissions:
contents: read
actions: read
steps:
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
ref: ${{ needs.merged-release.outputs.sha || needs.release-please.outputs.sha }}
persist-credentials: false
- uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6.5.0
with:
node-version-file: .nvmrc
package-manager-cache: false
- run: npm ci
- uses: ./.github/actions/prepare-release-package
id: package
- name: Exercise the exact qualified tarball
env:
VERIFIED_PACKAGE_TARBALL: ${{ steps.package.outputs.tarball }}
VERIFIED_PACKAGE_VERSION: ${{ steps.package.outputs.version }}
run: node scripts/verify-release-artifact.mjs "$VERIFIED_PACKAGE_TARBALL" "$VERIFIED_PACKAGE_VERSION"
publish:
name: Publish to npm
needs: [merged-release, release-please, qualification, rehearsal]
if: >-
always() && !cancelled() &&
needs.qualification.result == 'success' && needs.rehearsal.result == 'success'
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
ref: ${{ needs.merged-release.outputs.sha || needs.release-please.outputs.sha }}
persist-credentials: false
- uses: actions/setup-node@v6
- uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6.5.0
with:
node-version-file: .nvmrc
package-manager-cache: false
@@ -315,55 +356,36 @@ jobs:
- run: npm ci
- name: Prepare Windows native build toolchain
run: sudo apt-get update && sudo apt-get install --no-install-recommends -y g++-mingw-w64-x86-64
- uses: ./.github/actions/prepare-release-package
id: package
- name: Build and verify bundled Windows native controls
run: npm run build:windows-native && npm run verify:windows-native:artifact
- name: Verify packaged release before publishing
run: npm run verify:package
- name: Inspect existing npm version
id: registry
env:
VERIFIED_PACKAGE_TARBALL: ${{ steps.package.outputs.tarball }}
VERIFIED_PACKAGE_VERSION: ${{ steps.package.outputs.version }}
run: node scripts/ci/npm-release.mjs inspect "$VERIFIED_PACKAGE_TARBALL" "$VERIFIED_PACKAGE_VERSION"
- name: Publish
if: steps.registry.outputs.published == 'false'
shell: bash
env:
VERIFIED_PACKAGE_TARBALL: ${{ steps.package.outputs.tarball }}
VERIFIED_PACKAGE_VERSION: ${{ steps.package.outputs.version }}
run: |
version="$(node --input-type=module -e 'import packageJson from "./package.json" with { type: "json" }; process.stdout.write(packageJson.version)')"
published_version="$(npm view "rea-agents@${version}" version --json 2>/dev/null || true)"
if [ "${published_version}" = "\"${version}\"" ]; then
echo "rea-agents@${version} is already published; verifying the existing package."
else
tag="$(node scripts/release-npm-tag.mjs "${version}")"
npm publish --access public --tag "${tag}"
fi
tag="$(node scripts/release-npm-tag.mjs "$VERIFIED_PACKAGE_VERSION")"
npm publish "${VERIFIED_PACKAGE_TARBALL}" --access public --tag "${tag}"
- name: Verify published npm bytes and dist-tag
env:
VERIFIED_PACKAGE_TARBALL: ${{ steps.package.outputs.tarball }}
VERIFIED_PACKAGE_VERSION: ${{ steps.package.outputs.version }}
run: node scripts/ci/npm-release.mjs verify "$VERIFIED_PACKAGE_TARBALL" "$VERIFIED_PACKAGE_VERSION"
- name: Verify published CLI from npm
shell: bash
run: |
version="$(node --input-type=module -e 'import packageJson from "./package.json" with { type: "json" }; process.stdout.write(packageJson.version)')"
verification_root="$(mktemp -d)"
trap 'rm -rf -- "${verification_root}"' EXIT
cd "${verification_root}"
max_attempts=120
sleep_seconds=15
total_window_minutes=$((max_attempts * sleep_seconds / 60))
for attempt in $(seq 1 "${max_attempts}"); do
published_version="$(npm view --prefer-online "rea-agents@${version}" version --json 2>/dev/null || true)"
if [ "${published_version}" = "\"${version}\"" ]; then
echo "rea-agents@${version} is visible on npm after attempt ${attempt}/${max_attempts}; verifying the published CLI."
npm exec --yes --package="rea-agents@${version}" -- rea --help
npm exec --yes --package="rea-agents@${version}" -- rea --llms
node "${GITHUB_WORKSPACE}/scripts/verify-published-package.mjs" "${version}"
exit 0
else
echo "rea-agents@${version} is not visible on npm yet (attempt ${attempt}/${max_attempts}; up to ${total_window_minutes} minutes)" >&2
fi
if [ "${attempt}" -lt "${max_attempts}" ]; then
sleep "${sleep_seconds}"
fi
done
echo "rea-agents@${version} did not become visible on npm within ${total_window_minutes} minutes (${max_attempts} attempts × ${sleep_seconds}s)" >&2
echo "This indicates npm registry propagation delay, not a CLI verification failure." >&2
exit 1
env:
VERIFIED_PACKAGE_VERSION: ${{ steps.package.outputs.version }}
run: node scripts/verify-published-package.mjs "$VERIFIED_PACKAGE_VERSION"
publish-mcp:
name: Publish to MCP Registry
@@ -377,7 +399,7 @@ jobs:
contents: read
id-token: write
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
ref: ${{ needs.merged-release.outputs.sha || needs.release-please.outputs.sha }}
persist-credentials: false
@@ -385,23 +407,4 @@ jobs:
- name: Verify package and registry metadata versions
run: npm run mcp-registry:check
- name: Install MCP Registry publisher
env:
MCP_PUBLISHER_VERSION: v1.8.0
MCP_PUBLISHER_SHA256_LINUX_AMD64: 1370446bbe74d562608e8005a6ccce02d146a661fbd78674e11cc70b9618d6cf
run: |
archive="mcp-publisher_linux_amd64.tar.gz"
curl -fsSLo "${archive}" \
"https://github.com/modelcontextprotocol/registry/releases/download/${MCP_PUBLISHER_VERSION}/${archive}"
echo "${MCP_PUBLISHER_SHA256_LINUX_AMD64} ${archive}" | sha256sum --check --strict
tar xzf "${archive}" mcp-publisher
chmod +x mcp-publisher
- name: Validate MCP Registry metadata
run: ./mcp-publisher validate
- name: Authenticate to MCP Registry
run: ./mcp-publisher login github-oidc
- name: Publish MCP Registry metadata
run: ./mcp-publisher publish
- uses: ./.github/actions/publish-mcp
+6 -4
View File
@@ -22,14 +22,16 @@ jobs:
# The repository-pinned Node 24 version also runs the complete test suite.
node: ["22.x", "24.15.0", "24.x", "26.x"]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: ${{ matrix.node }}
check-latest: true
cache: npm
- name: Cache npm package downloads
id: npm-cache
uses: ./.github/actions/npm-cache
- run: npm ci
- uses: actions/download-artifact@v4
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: rea-test-runtime
path: .
+2 -6
View File
@@ -7,10 +7,6 @@ on:
permissions:
contents: read
concurrency:
group: website-check-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
check:
name: Website checks
@@ -18,9 +14,9 @@ jobs:
timeout-minutes: 10
steps:
- name: Check out the repository
uses: actions/checkout@v4
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- name: Set up Python for website assets
uses: actions/setup-python@v5
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
with:
python-version: "3.12"
cache: pip
+9 -9
View File
@@ -47,9 +47,9 @@ jobs:
exit 1
fi
- name: Check out the repository
uses: actions/checkout@v4
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- name: Set up Python for website assets
uses: actions/setup-python@v5
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
with:
python-version: "3.12"
cache: pip
@@ -74,9 +74,9 @@ jobs:
)
PY
- name: Configure Pages
uses: actions/configure-pages@v5
uses: actions/configure-pages@983d7736d9b0ae728b81ab479565c72886d7745b # v5.0.0
- name: Upload the website
uses: actions/upload-pages-artifact@v4
uses: actions/upload-pages-artifact@7b1f4a764d45c48632c6b24a0339c27f5614fb0b # v4.0.0
with:
path: website/_site
@@ -95,15 +95,15 @@ jobs:
steps:
- name: Check out publishing configuration
if: inputs.target == 'both'
uses: actions/checkout@v4
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- name: Set up Node.js for Wrangler
if: inputs.target == 'both'
uses: actions/setup-node@v4
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
- name: Download the verified website
if: inputs.target == 'both'
uses: actions/download-artifact@v4
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: github-pages
path: ${{ runner.temp }}/website-archive
@@ -114,7 +114,7 @@ jobs:
tar -xf "$RUNNER_TEMP/website-archive/artifact.tar" -C "$RUNNER_TEMP/rea-website-public"
- name: Publish the website to Cloudflare
if: inputs.target == 'both'
uses: cloudflare/wrangler-action@v4
uses: cloudflare/wrangler-action@953926a2e2182532811c01a25e53647d93bf07c0 # v4.1.3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
@@ -123,7 +123,7 @@ jobs:
command: deploy --cwd website --env production --assets "${{ runner.temp }}/rea-website-public"
- name: Publish to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4.0.5
- name: Verify the selected published revisions
env:
PAGES_URL: ${{ steps.deployment.outputs.page_url }}
+3
View File
@@ -33,9 +33,12 @@ codedb.snapshot
htmlcov/
.coverage
coverage/
.stryker-tmp/
reports/mutation/
.vitest/
.vitest-reports/
test-results.xml
test-timings.json
.turbo/
.cache/
docs/.vitepress/cache/
+1 -1
View File
@@ -1,3 +1,3 @@
{
".": "6.4.0"
".": "6.5.0"
}
+33
View File
@@ -20,6 +20,8 @@ Give each interpretation and resource a clear owner. Consumers must use that own
Keep proof of ownership separate from validity, editability, and readiness. Malformed owned data may still be safely removable. Unresolved identity or applicability must remain unknown rather than be treated as absent, foreign, or aligned.
For lifecycle changes, follow [ownership and completion invariants](docs/tool-design.md#own-the-lifecycle) and select the applicable [lifecycle regressions](docs/testing.md#lifecycle-regressions). Include late handshakes, cancellation during final cleanup, mixed child outcomes, and cleanup retry where those stages exist; active-execution cancellation alone does not cover finalization.
Assess brittleness in touched code during implementation, debugging, review, and maintenance. Fix the producing representation or ownership rule before adding downstream repairs. Normalize once at the owning boundary using format-aware parsers. Remove superseded representations, aliases, wrappers, and redundant tests after verifying affected consumers. Introduce abstractions only when they remove observed complexity.
When fixing a bug, inspect the producing boundary and affected consumers for duplicated interpretation or unclear ownership. Complete any directly related refactor in the same change and remove superseded code. Passing regression tests alone does not establish that the brittleness is resolved. If related cleanup remains, explain what remains and why.
@@ -78,6 +80,37 @@ For test changes or provider claims, use [docs/testing.md](docs/testing.md). Pre
Use Conventional Commit subjects and PR titles. Mark breaking changes with `!` or a `BREAKING CHANGE:` footer. Describe behavior changes, relevant validation, and real-provider verification scope.
## Issue Reports
Follow [issue reporting guidance](CONTRIBUTING.md#reporting-issues) and the matching
issue form. Keep reports concise and cite the smallest useful evidence.
Distinguish observed results, source-derived reasoning, hypotheses, and proposed
checks. Never claim runtime reproduction or measurements you did not perform.
Use `NOT_ENOUGH_INFO` with an explanation for missing narrative answers or evidence.
Incomplete reports, source-only findings, and early feature or design ideas are
welcome when their limitations are explicit; a completed diagnosis or fix is not
required to open an issue.
## Pull Request Readiness
Follow [human ownership and readiness](CONTRIBUTING.md#pull-request-ownership-and-readiness)
when preparing a PR. AI-assisted contributions are welcome; a human must review
and own the final diff, check the validation evidence and its limitations, and
explicitly declare the PR ready for initial maintainer review.
Keep the PR body concise and current, with the concrete problem, observable
outcome, approach, and evidence. Report only checks that actually ran as executed;
distinguish source-derived conclusions, proposed checks, and unavailable coverage.
Address actionable automated review findings and explain disagreements.
Leave the template's **Author readiness** declarations unchecked for the human
owner to complete. Never attest to human review on their behalf. Keep the PR in
draft until the human declares the PR initially ready. Later pushes do not
invalidate that declaration or require another human sign-off. Keep the PR body
and affected validation current as work changes. The readiness check validates
the filled declaration; CI and automated review results do not establish that
human review occurred, maintainer scope approval, or merge permission.
<!-- BEGIN:turborepo-agent-rules -->
# This is NOT the Turborepo you know
+113
View File
@@ -1,5 +1,118 @@
# Changelog
## [6.5.0](https://github.com/morluto/rea/compare/rea-agents-6.4.0...rea-agents-6.5.0) (2026-10-13)
Provider prerequisites and recorded verification scope are listed in [the testing guide](docs/testing.md).
### Highlights
- Analyze selected JavaScript files and recover cross-bundle relationships without expanding the selected source scope.
- Read compact summaries and page retained Evidence across binary, managed, Android, Dart, WASM and network workflows.
- Inspect SQLite snapshots, packet and signal recordings, WebAssembly, Rust dependency metadata and Electron fuses offline.
- Keep completed Evidence and partial observations available when analysis or cleanup fails.
### Changes
- **JavaScript source selection:** analyze exact application paths without expanding imports into the selected scope. Unselected files retain identity and explicit unknown coverage; summary delivery remains separate from source selection. ([#1944](https://github.com/morluto/rea/pull/1944), [issue morluto/rea#1848](https://github.com/morluto/rea/issues/1848)). Thanks @kaoru0822-kitauji, @N0zoM1z0.
- **Code-split recovery:** recover related bundles from multiple selected inputs, retaining per-file provenance and downstream cross-chunk imports. Recorded CLI/MCP coverage is Linux x64 with Wakaru 1.14.0 and source-owned esbuild/webpack fixtures; other hosts, releases and arbitrary application equivalence remain unverified. ([#1686](https://github.com/morluto/rea/pull/1686), [issue morluto/rea#1605](https://github.com/morluto/rea/issues/1605)). Thanks @JingHao-Leon, @pionxzh.
- **Package imports:** resolve exact package import aliases using the selected loader and package ancestry, rather than guessing sibling files. ([#1862](https://github.com/morluto/rea/pull/1862), [issue morluto/rea#1838](https://github.com/morluto/rea/issues/1838)). Thanks @AWUQAQ, @mvanhorn.
- **Retained analysis views:** opt into compact inventory/WASM, managed metadata, Android manifest, Dart AOT, Mach-O and EVM summaries, then page retained Evidence without rerunning analysis. Summary delivery preserves the complete recorded result and its original coverage. ([#2122](https://github.com/morluto/rea/pull/2122), [#2193](https://github.com/morluto/rea/pull/2193), [#2413](https://github.com/morluto/rea/pull/2413), [#2438](https://github.com/morluto/rea/pull/2438), [#2455](https://github.com/morluto/rea/pull/2455), [#2634](https://github.com/morluto/rea/pull/2634), [#2308](https://github.com/morluto/rea/pull/2308), [9a18251](https://github.com/morluto/rea/commit/9a18251426f891e6a24856b715ef8002ab8b9610)). Thanks @kaoru0822-kitauji, @N0zoM1z0.
- **Managed assemblies:** Select declaring types when inspecting members, decode inspected `ldstr` literals with independent coverage, and page retained member summaries. Call and field targets expose `target_declaring_type` and `field_declaring_type`, including supported generic method targets; unresolved metadata remains unknown. ([#1706](https://github.com/morluto/rea/pull/1706), [#2125](https://github.com/morluto/rea/pull/2125), [issue morluto/rea#2121](https://github.com/morluto/rea/issues/2121), [#2162](https://github.com/morluto/rea/pull/2162), [issue morluto/rea#2158](https://github.com/morluto/rea/issues/2158), [#3061](https://github.com/morluto/rea/pull/3061), [issue morluto/rea#3010](https://github.com/morluto/rea/issues/3010), [#2163](https://github.com/morluto/rea/pull/2163), [issue morluto/rea#2160](https://github.com/morluto/rea/issues/2160)). Thanks @kaoru0822-kitauji, @N0zoM1z0, @Nelglor, @skoodster.
- **Packet captures:** inspect saved packets with bounded TShark output, compact capture summaries and retained pages; discover installed protocol/field definitions. Recorded real-engine coverage uses Linux and Wireshark 4.6.6; native Windows/macOS and Bun behavior remain unverified. ([#2318](https://github.com/morluto/rea/pull/2318), [#2340](https://github.com/morluto/rea/pull/2340), [#2496](https://github.com/morluto/rea/pull/2496)). Thanks @3QL, @N0zoM1z0, @Olorinm.
- **Recorded signals:** decode existing recordings and discover installed decoders/options through caller-supplied sigrok-cli 0.7.2. This is Linux-only and requires util-linux `prlimit`; it acquires no hardware samples. ([#2339](https://github.com/morluto/rea/pull/2339), [#2487](https://github.com/morluto/rea/pull/2487)). Thanks @N0zoM1z0, @Olorinm.
- **Web captures:** derive an inferred method/origin/path-template API surface from retained HAR data including observed query-parameter names while retaining unknowns; query values, headers and bodies remain excluded. ([#1891](https://github.com/morluto/rea/pull/1891), [#2982](https://github.com/morluto/rea/pull/2982), [issue morluto/rea#2913](https://github.com/morluto/rea/issues/2913)). Thanks @mvanhorn, @wunianze666-netizen, @zhangguiping-xydt.
- **QUIC captures:** inspect retained aioquic 1.5.0 JSON qlog 0.3 objects and page traces/streams/observations. Other qlog layouts remain unsupported; overlap and loss declarations do not prove retransmission or network loss. ([#2277](https://github.com/morluto/rea/pull/2277)). Thanks @LuisTaffelli, @N0zoM1z0.
- **Fiddler captures:** inspect native SAZ archives with retained HTTP provenance and bounded decoding. Recorded producer coverage is the pinned public encoding corpus; current Fiddler Everywhere variants and Bun-native execution remain unverified. ([#2409](https://github.com/morluto/rea/pull/2409)). Thanks @N0zoM1z0, @zahhak.
- **SQLite snapshots:** inspect an explicitly selected offline database with table/column selection and bounded Evidence. This operation requires Node 24.x >=24.15 or 26+ with `DatabaseSync.limits`; supported Node 22 and earlier 24 runtimes cannot use it. A live database/WAL copy does not establish an atomic snapshot. ([#2271](https://github.com/morluto/rea/pull/2271)). Thanks @N0zoM1z0, @Y0UW1SH.
- **WASM artifacts:** Inspect local modules with bring-your-own WABT 1.0.42 without executing them. Compare imports/exports from two complete core-module inspection Evidence records without requiring an active WABT installation; unsupported signatures and duplicate identities remain unknown, and comparisons do not establish runtime compatibility. Recorded real-tool inspection coverage is macOS arm64. ([#1533](https://github.com/morluto/rea/pull/1533), [issue morluto/rea#884](https://github.com/morluto/rea/issues/884), [#2464](https://github.com/morluto/rea/pull/2464), [issue morluto/rea#2356](https://github.com/morluto/rea/issues/2356)). Thanks @matbrgz, @N0zoM1z0, @thangldw.
- **Go binaries:** inspect embedded build metadata, including file-backed unmapped Mach-O debug segments. ([#1363](https://github.com/morluto/rea/pull/1363), [7fb097d](https://github.com/morluto/rea/commit/7fb097d72c9931dd5dfae481ad2b9d3bb6029f15)). Thanks @Y0UW1SH.
- **Flutter APKs:** identify snapshot/engine builds and inspect AOT structure without an external engine. Recorded real APK coverage uses the documented Flutter Gallery fixture; macOS/Windows and complete pool semantics remain unverified, and pool coverage stays partial. ([#1582](https://github.com/morluto/rea/pull/1582)). Thanks @3QL.
- **HarmonyOS packages:** inventory HAP/HSP/App Pack structures without executing code. The recorded real workflow covers one pinned HAP through repository-built CLI/MCP; installed-package and real HSP/App Pack coverage remain unverified. ([#1447](https://github.com/morluto/rea/pull/1447)). Thanks @BaaRai.
- **Ghidra raw inputs and references:** compatible Linux/macOS Ghidra profiles accept caller-selected raw architecture/entry information and expose typed incoming references at exact addresses. Windows Ghidra P0 admits the raw NMOS 6502 profile described below; other raw targets and the incoming-reference facet remain outside Windows P0. ([#1868](https://github.com/morluto/rea/pull/1868), [issue morluto/rea#1836](https://github.com/morluto/rea/issues/1836), [#1715](https://github.com/morluto/rea/pull/1715), [issue morluto/rea#1245](https://github.com/morluto/rea/issues/1245)). Thanks @bugstan, @casesolved-co-uk, @mvanhorn, @N0zoM1z0.
- **Ghidra MIPS:** Add bounded MIPS32r2/o32 relocatable objects, soft-float integer-register storage, admitted MIPS16 shared ELF, and a narrowly admitted recordless legacy MIPS32 executable profile. The latter pins an endian-matched 32-bit language and records inferred o32 as an analysis assumption. Admission follows each documented ABI/flag/relocation boundary. ([#1949](https://github.com/morluto/rea/pull/1949), [issue morluto/rea#1937](https://github.com/morluto/rea/issues/1937), [#1950](https://github.com/morluto/rea/pull/1950), [issue morluto/rea#1938](https://github.com/morluto/rea/issues/1938), [#1991](https://github.com/morluto/rea/pull/1991), [issue morluto/rea#1851](https://github.com/morluto/rea/issues/1851), [#1898](https://github.com/morluto/rea/pull/1898), [issue morluto/rea#1620](https://github.com/morluto/rea/issues/1620)). Thanks @AurelioB, @BaaRai, @bqw371, @kaoru0822-kitauji, @N0zoM1z0.
- **Ghidra RISC-V:** admit little-endian RV32/RV64 `ET_EXEC` ELF declaring the supported double-float ABI. Relocatable/shared, big-endian and other ABI profiles remain refused; see [the profile and recorded lane](docs/ghidra-riscv.md). ([#2151](https://github.com/morluto/rea/pull/2151)). Thanks @NIU-123370.
- **PSP static ELF profile:** the Ghidra profile is Linux x64, bounded little-endian ELF32 `ET_EXEC` with the documented PSP/ABI declarations and a compatible caller-installed Allegrex extension. PRX, shared/relocatable images, PBP/ISO/CSO and runtime emulation are outside scope. Historical extension experiments are separate from public-provider verification; real public Ghidra PSP coverage remains unverified. ([#1599](https://github.com/morluto/rea/pull/1599)). Thanks @jaelliot.
- **Ghidra analysis settings:** apply validated analyzer overrides before analysis while retaining the selected analysis profile. ([#1913](https://github.com/morluto/rea/pull/1913)). Thanks @akushonkamen, @kaoru0822-kitauji, @N0zoM1z0.
- **Attached native IDA MCP:** add an optional native bridge alongside existing legacy/headless profiles. Recorded native runs use Linux IDA Free 9.4 with a navigation correction and Windows IDA Pro 9.3 with a loopback-listener change; stock native switching, native macOS and Windows IDA Free remain unverified. Attached databases remain read-only and are never saved or closed. ([#1544](https://github.com/morluto/rea/pull/1544)). Thanks @imissapixel, @N0zoM1z0.
- **Frida instrumentation:** add explicit local/remote target selection, caller-supplied scripts and retained messages, with script-read/input diagnostics and bounded message eviction. Recorded real coverage is the Linux local Node marker workflow; macOS, Windows and remote devices/servers remain unverified. Instrumentation executes foreign code and is destructive, not read-only. ([#1359](https://github.com/morluto/rea/pull/1359), [#2211](https://github.com/morluto/rea/pull/2211), [issue morluto/rea#2209](https://github.com/morluto/rea/issues/2209), [#2217](https://github.com/morluto/rea/pull/2217), [issue morluto/rea#2213](https://github.com/morluto/rea/issues/2213), [#2628](https://github.com/morluto/rea/pull/2628)). Thanks @dandgabr, @kaoru0822-kitauji, @N0zoM1z0.
- **Client setup:** register Qoder, Trae, ZCode, DeepSeek Harness, Kimi Code, Factory Droid and WorkBuddy through the existing scoped setup flow. DeepSeek is included from 6.5.0; the historical npm 6.4 check did not cover it. Live Trae and DeepSeek API integration remain unverified. ([#1564](https://github.com/morluto/rea/pull/1564), [#1572](https://github.com/morluto/rea/pull/1572), [#1573](https://github.com/morluto/rea/pull/1573), [#1929](https://github.com/morluto/rea/pull/1929), [issue morluto/rea#1839](https://github.com/morluto/rea/issues/1839), [#2744](https://github.com/morluto/rea/pull/2744), [#2309](https://github.com/morluto/rea/pull/2309), [#3041](https://github.com/morluto/rea/pull/3041), [issue morluto/rea#3040](https://github.com/morluto/rea/issues/3040)). Thanks @0xRafie, @BaaRai, @cainiao1992, @IvenKooLab, @whoishaha, @ysntony.
- **Website:** Add maintained language editions, persistent/system-aware themes, keyboard styling and nested Evidence indentation, plus the reverse-engineering models essay. A simpler home page and beginner journey connect platform prerequisites, client/provider setup and a supplied first investigation. ([#1779](https://github.com/morluto/rea/pull/1779), [issue morluto/rea#1022](https://github.com/morluto/rea/issues/1022), [#1699](https://github.com/morluto/rea/pull/1699), [#1714](https://github.com/morluto/rea/pull/1714), [#1894](https://github.com/morluto/rea/pull/1894), [#2250](https://github.com/morluto/rea/pull/2250), [#2432](https://github.com/morluto/rea/pull/2432), [#2732](https://github.com/morluto/rea/pull/2732), [#3274](https://github.com/morluto/rea/pull/3274)). Thanks @CassieFox23, @kaoru0822-kitauji, @N0zoM1z0.
- **Rust dependency metadata:** Inspect embedded cargo-auditable package/dependency records in ELF, PE and thin Mach-O without executing the file. Missing metadata and inferred compiler hashes retain explicit limitations; universal Mach-O requires a selected thin slice. ([#2456](https://github.com/morluto/rea/pull/2456)). Thanks @mvanhorn.
- **Electron fuses:** Read packaged fuse bytes and per-slice runtime implications without launching or modifying the app. Missing, truncated and unknown wires remain explicit; fuse observations do not prove other Inspector-enablement paths. ([#2399](https://github.com/morluto/rea/pull/2399)). Thanks @kaoru0822-kitauji, @mvanhorn, @N0zoM1z0.
- **Ghidra PowerPC:** Admit bounded ELF32 big-endian `ET_EXEC` PowerPC with an explicit language profile on supported Linux/macOS hosts. PPC64, DOL and other variants remain outside this lane; see [the profile and verification prerequisites](docs/ghidra-ppc.md). ([#1747](https://github.com/morluto/rea/pull/1747)). Thanks @arielfikru.
- **Reference-source summaries:** Use CLI `import-reference-source --detail summary` for compact manifest, count, state and path-outline output. The complete graph remains available and is still the default; hidden outline entries retain separate coverage counts. ([#2768](https://github.com/morluto/rea/pull/2768), [issue morluto/rea#2442](https://github.com/morluto/rea/issues/2442)). Thanks @matbrgz, @To3akaRin.
- **Recorded AArch64 crashes:** Decode 34 ABI register values per thread from supported Linux ELF64 little-endian core notes using the documented caller-provided Python dependencies. These are recorded facts, not proof of current process or library identity. ([#2505](https://github.com/morluto/rea/pull/2505), [issue morluto/rea#2353](https://github.com/morluto/rea/issues/2353)). Thanks @matbrgz, @NIU-123370.
- **Ghidra byte search:** Search initialized analyzed memory using wildcards, bit masks, address bounds and paged results. Unloaded, overlay and uninitialized blocks retain skipped coverage; this is not a raw-file search. ([#2341](https://github.com/morluto/rea/pull/2341)). Thanks @mvanhorn.
- **Raw NMOS 6502 images:** Analyze explicitly selected headerless images with Ghidra, including Windows P0, using a caller-selected entry and source mapping within the 16-bit address space. Cartridge parsing, reset-vector inference and 65C02/65816 are outside this profile. ([#2079](https://github.com/morluto/rea/pull/2079)). Thanks @Zhong-fan.
- **Swift String observations:** Ghidra function dossiers derive supported AArch64 Swift String literals from constant-register pairs at recognized use sites, retaining instruction/register provenance and explicit omissions. This is a bounded derivation, not a complete string inventory. ([#2049](https://github.com/morluto/rea/pull/2049), [issue morluto/rea#2030](https://github.com/morluto/rea/issues/2030)). Thanks @thefolahan.
- **Headless IDA procedure lookup:** Resolve addresses to canonical containing-procedure entries and identify the session’s admitted program document in the modern headless profile. Incomplete surveys remain errors; attached profiles do not advertise this containment capability. ([#2374](https://github.com/morluto/rea/pull/2374), [issue morluto/rea#2335](https://github.com/morluto/rea/issues/2335)). Thanks @nikolay-dementiev.
- **Passive browser attachment:** Six existing passive browser tools accept an explicit-port loopback browser WebSocket endpoint when HTTP target discovery is unavailable. Existing consent and target/origin checks remain required; this does not launch a browser or enable runtime instrumentation. ([#2346](https://github.com/morluto/rea/pull/2346)). Thanks @all-black-493.
- **Android DEX references:** Retain exact method descriptors and individual direct-invoke observations with source method, DEX entry, opcode and code-unit offset. These are static references; unavailable instruction facts remain separate from method-level usage totals. ([#2439](https://github.com/morluto/rea/pull/2439)). Thanks @LcokHcak.
- **Offline EVM metadata:** Retain bounded, lossless facts from plausible terminal Solidity CBOR root-map candidates. Candidates do not prove Solidity origin or source/compiler identity, and linked metadata is not fetched. ([#2790](https://github.com/morluto/rea/pull/2790)). Thanks @matbrgz.
### Fixes
- **JavaScript loader and export identity:** Respect import/require conditions and unavailable package metadata, keep export links separate from writes, and resolve default identifier exports to the value captured at the export statement. ([7e5cffa](https://github.com/morluto/rea/commit/7e5cffa2ae9dfc9afaeca0715110d4dd41c0dcdd), [322506d](https://github.com/morluto/rea/commit/322506d0c0d39d3cd1e2e1c285c2cc23cfa73ee7), [81f7638](https://github.com/morluto/rea/commit/81f763820319a762f003cf95651ed14ce9326a4c), [dae1e55](https://github.com/morluto/rea/commit/dae1e55381f0d6cfee7f2082d5d27b938e55704c)). Thanks @paoValle, @VarunGore36, @xu91102.
- **JavaScript traces:** Retain nested bridge calls and surviving early-return predicates. Honor node-ID match options, match route seeds against source values, and keep ambiguous runtime candidates unknown. ([0351328](https://github.com/morluto/rea/commit/0351328e54ce70ea3f2814482e76b132774aff48), [5031099](https://github.com/morluto/rea/commit/503109995f9dcf8a955585d39a344fc846d2f59e), [44c9d29](https://github.com/morluto/rea/commit/44c9d2924d5b95d7a86595e8d7b7cf4fd4f0f6ac), [5e79d46](https://github.com/morluto/rea/commit/5e79d468e5864204cbe79a7ca2898541cee5e012)). Thanks @jpdefo.
- **Browser comparison coverage:** Compare only known redirect response fields and retain network-selection recovery guidance alongside independent event changes. Missing response facts do not prove equality or a change. ([2b60e09](https://github.com/morluto/rea/commit/2b60e097f281f7708c69f1f7ba9263694547bcc0)).
- **Evidence comparison and ledger input:** Report display-only bundle differences as unchanged while retaining digests, and classify noncanonical bundle records as invalid caller input. ([030c477](https://github.com/morluto/rea/commit/030c47732833c9cedcc31db1309eb3a1ca2c62e8), [3a34ad3](https://github.com/morluto/rea/commit/3a34ad37bdb7b8debba0f6515ba85b80141f2716)).
- **Capture and admission cleanup:** Keep deleted-file observations and original bytes when a parent becomes a file, preserve unrelated I/O failures during cancellation, and require explicit pre-launch proof before releasing an unknown LLDB target owner. LLDB failure controls used simulated boundaries. ([e67c364](https://github.com/morluto/rea/commit/e67c3642c8920a2a9c0e231ee2f16f50b245df31), [2daeff4](https://github.com/morluto/rea/commit/2daeff4b243e1cd137c591ca394e18a1c05c8bbc), [45b6a7f](https://github.com/morluto/rea/commit/45b6a7fe8f51a89951aaa823ffa627f6f5453a33)). Thanks @SII-YDD, @xu91102.
- **Client configuration:** Validate MCP output-schema profiles before setup writes, ignore untrusted Gemini workspace settings before parsing them, and identify installed skills independently of marker-only client roots. ([0930e94](https://github.com/morluto/rea/commit/0930e94e077ab7fb4d695f62785919a8a25c2e21), [aade9bb](https://github.com/morluto/rea/commit/aade9bbcb79aa8504ab6921b25f8ab082aba78e7), [ba33c70](https://github.com/morluto/rea/commit/ba33c706d26b63e43308be50c0249c72cff62cc6)). Thanks @CmsChase, @paoValle, @sx4im.
- **Host tool discovery:** Share macOS Hopper launcher discovery across setup and runtime, preserve explicit paths, and report validated ADB and Apktool availability. ([7582f0d](https://github.com/morluto/rea/commit/7582f0dff29ae0e80c97b885ddd386ac61a92570), [e5deb83](https://github.com/morluto/rea/commit/e5deb832cb09bb524877d0df40f49d5b5bb7dea4)). Thanks @Chessing234, @kaoru0822-kitauji, @viniciusdsandrade.
- **SAZ encoding validation:** Reject invalid XML bytes, unsupported encodings and declarations that contradict the archive bytes, including records outside the selected request. ([1250aae](https://github.com/morluto/rea/commit/1250aaedd8b4d1e471d8a382f76418bde65c05d2)).
- **Managed member matching:** Keep malformed TypeDef list ownership unknown, distinguish constants during structural method matching, and accept legacy small exception-section sizes. ([3f6b575](https://github.com/morluto/rea/commit/3f6b5755b3e699bd9373641acffe0f2c5d2ab094), [ee5cfbe](https://github.com/morluto/rea/commit/ee5cfbe05cd309a88003c711721e67a3bee36554)).
- **Bounded diagnostics:** Bound SDK validation messages and terminal normalization before allocation while retaining diagnostics and explicit unknown final output sizes. ([7fb0a0c](https://github.com/morluto/rea/commit/7fb0a0cc577f7c9033a2674d877d8242b5864556), [1d826f3](https://github.com/morluto/rea/commit/1d826f304f032acfe69dad3ff824b300aacaeb43)). Thanks @CRYPTONIKAV.
- **Windows artifact classification:** identify `.exe`, `.com`, `.bat` and `.cmd` artifacts as launchable host executables. ([#1823](https://github.com/morluto/rea/pull/1823), [#1824](https://github.com/morluto/rea/pull/1824), [issue morluto/rea#1819](https://github.com/morluto/rea/issues/1819)). Thanks @dollarsmoney, @JasonBuildAI, @kaoru0822-kitauji.
- **CLI/MCP delivery:** defer command-specific startup work, bundle runtime entries, retain field/input correction details across output formats and preserve shared optional-provider lifetime. MCP selectors, paging continuation and retained partial-error references remain explicit; session liveness/provider-attribution corrections retain their controlled-provider verification limits. ([#1691](https://github.com/morluto/rea/pull/1691), [issue morluto/rea#1685](https://github.com/morluto/rea/issues/1685), [#1705](https://github.com/morluto/rea/pull/1705), [issue morluto/rea#1695](https://github.com/morluto/rea/issues/1695), [#1755](https://github.com/morluto/rea/pull/1755), [issue morluto/rea#1746](https://github.com/morluto/rea/issues/1746), [#1773](https://github.com/morluto/rea/pull/1773), [issue morluto/rea#1771](https://github.com/morluto/rea/issues/1771), [#1780](https://github.com/morluto/rea/pull/1780), [#1814](https://github.com/morluto/rea/pull/1814), [#1910](https://github.com/morluto/rea/pull/1910), [issue morluto/rea#1849](https://github.com/morluto/rea/issues/1849), [#1996](https://github.com/morluto/rea/pull/1996), [#2140](https://github.com/morluto/rea/pull/2140), [issue morluto/rea#2135](https://github.com/morluto/rea/issues/2135), [#2205](https://github.com/morluto/rea/pull/2205), [issue morluto/rea#2195](https://github.com/morluto/rea/issues/2195), [#2230](https://github.com/morluto/rea/pull/2230), [issue morluto/rea#2180](https://github.com/morluto/rea/issues/2180), [#2294](https://github.com/morluto/rea/pull/2294), [#2482](https://github.com/morluto/rea/pull/2482), [a82d14e](https://github.com/morluto/rea/commit/a82d14e4db4dca989ffa2e7dbe78fd43551da256), [a4dd4e8](https://github.com/morluto/rea/commit/a4dd4e8e8700ce350e1a0c612ebd61b91ca5e585), [48c352b](https://github.com/morluto/rea/commit/48c352b6bfa34c820a530272cd36600158c37b1e), [07b36c1](https://github.com/morluto/rea/commit/07b36c148946f46b99a4ce04e55726e97d0ee03a), [570c40c](https://github.com/morluto/rea/commit/570c40cde55d31e455ef9bc484b7b908658ded58), [8fc2043](https://github.com/morluto/rea/commit/8fc2043eb8bcedbf34dce064f977e455438635d6), [7fc0b5e](https://github.com/morluto/rea/commit/7fc0b5ec9c2532bbf789d7783b7a0cab6fc054d8)). Thanks @369Apps, @chqcjq, @cold-atom, @coygeek, @JackOdegard, @kaoru0822-kitauji, @N0zoM1z0, @sx4im.
- **Evidence import/replay:** import bundles across session transitions while authenticating selected target and file identities, preserve literal keys, metadata/mutation intent and relationship coverage, and honor cancellation before session mutation. Reuse authenticated snapshots and comparisons within their owned lifetime; reject malformed/non-object snapshots and unsupported metadata without disguising uncertainty. ([#1694](https://github.com/morluto/rea/pull/1694), [#1716](https://github.com/morluto/rea/pull/1716), [issue morluto/rea#1680](https://github.com/morluto/rea/issues/1680), [#1721](https://github.com/morluto/rea/pull/1721), [issue morluto/rea#1718](https://github.com/morluto/rea/issues/1718), [#1733](https://github.com/morluto/rea/pull/1733), [issue morluto/rea#1723](https://github.com/morluto/rea/issues/1723), [issue morluto/rea#1724](https://github.com/morluto/rea/issues/1724), [#1776](https://github.com/morluto/rea/pull/1776), [#1873](https://github.com/morluto/rea/pull/1873), [#2001](https://github.com/morluto/rea/pull/2001), [#2013](https://github.com/morluto/rea/pull/2013), [issue morluto/rea#2009](https://github.com/morluto/rea/issues/2009), [#2083](https://github.com/morluto/rea/pull/2083), [#2303](https://github.com/morluto/rea/pull/2303), [#2685](https://github.com/morluto/rea/pull/2685), [issue morluto/rea#2675](https://github.com/morluto/rea/issues/2675), [c002b89](https://github.com/morluto/rea/commit/c002b89e2d8130a3c07e25e5fd604876be18c703), [9c85114](https://github.com/morluto/rea/commit/9c8511409c0706f9979c2b68dd56aaaf9f21e191), [aada0ca](https://github.com/morluto/rea/commit/aada0ca8126bf14c60750a4ccd4a79667770a1e4), [4a4d36f](https://github.com/morluto/rea/commit/4a4d36f881861bbab3c734b0dd459f805766caec), [1c64fe3](https://github.com/morluto/rea/commit/1c64fe3eff48679c6880f42d2f0a4af46686ade7), [6fb3a34](https://github.com/morluto/rea/commit/6fb3a345fe952b8e8f59221aec993acc37ccbf31), [3bcc68c](https://github.com/morluto/rea/commit/3bcc68c037e9415c78093cb286069c1fcca6a2f8), [6bae774](https://github.com/morluto/rea/commit/6bae774328724403255590aa2ddad4d7a2aa727f), [e997085](https://github.com/morluto/rea/commit/e9970855ba16ba711816ac30212c5ca17ae085cd), [d1dcebc](https://github.com/morluto/rea/commit/d1dcebce2c0820469ed63e33497a14258971b710), [b123b2e](https://github.com/morluto/rea/commit/b123b2e51cc7d6d2157525664be2df88fb1ba3bd), [049132a](https://github.com/morluto/rea/commit/049132ab43fa962d31774f2b258c15a71217148b), [042dcaa](https://github.com/morluto/rea/commit/042dcaa1ee66f1b8668f08b193f38ab01bceaacb), [acf75e6](https://github.com/morluto/rea/commit/acf75e6700ace28bb1db25e54e56673ac15e0ed3), [1f7f808](https://github.com/morluto/rea/commit/1f7f808c4f3bb5eca6b13bcc71d5cceb2ace6614), [26bffb9](https://github.com/morluto/rea/commit/26bffb94bf80355c35e22a98a8fc629c6210307e)). Thanks @coygeek, @DeryFerd, @jiangjiang248, @kaoru0822-kitauji, @khanukov, @N0zoM1z0, @NIU-123370, @rohitg00, @sx4im.
- **Ghidra setup and analysis:** preserve launcher/JVM diagnostics, Java-property and extension identity, stale-client recovery, UTF-8 bridge output and pending-startup progress. File-offset decompilation is Ghidra-specific; recorded coverage is Linux x64 with Ghidra 12.1.4. Startup/progress/compiler failure paths that use simulations remain unverified with a real engine. ([#1698](https://github.com/morluto/rea/pull/1698), [#1877](https://github.com/morluto/rea/pull/1877), [#1931](https://github.com/morluto/rea/pull/1931), [#2011](https://github.com/morluto/rea/pull/2011), [issue morluto/rea#1992](https://github.com/morluto/rea/issues/1992), [#2014](https://github.com/morluto/rea/pull/2014), [#2101](https://github.com/morluto/rea/pull/2101), [issue morluto/rea#2078](https://github.com/morluto/rea/issues/2078), [#2206](https://github.com/morluto/rea/pull/2206), [issue morluto/rea#2204](https://github.com/morluto/rea/issues/2204), [2187e16](https://github.com/morluto/rea/commit/2187e16f149a7a7b512a4cb45d5f468cc04091fe), [b953f96](https://github.com/morluto/rea/commit/b953f96c0aae27e1876c317a8f7db1d6f292b8ff)). Thanks @EDMND-SRC, @Ha1baraA11, @kaoru0822-kitauji, @lovecontroing, @lyly-bonbon, @N0zoM1z0, @nosmile99, @Petru101, @sx4im, @Zhong-fan.
- **Process and cleanup ownership:** retain private runtime/file owners and detailed incomplete-cleanup observations, bound trace cycles/order state, preserve caller paths, and use monotonic time for budgets while retaining wall dates. Windows affinity/admission ([#2202](https://github.com/morluto/rea/pull/2202), [issue morluto/rea#2181](https://github.com/morluto/rea/issues/2181)) and Bun memory-enforcement paths retain their recorded native-versus-controlled-fixture limits; these fixes are not a general process-ownership guarantee. ([#1700](https://github.com/morluto/rea/pull/1700), [issue morluto/rea#1697](https://github.com/morluto/rea/issues/1697), [#1730](https://github.com/morluto/rea/pull/1730), [#1756](https://github.com/morluto/rea/pull/1756), [issue morluto/rea#1753](https://github.com/morluto/rea/issues/1753), [#1760](https://github.com/morluto/rea/pull/1760), [issue morluto/rea#1757](https://github.com/morluto/rea/issues/1757), [#1826](https://github.com/morluto/rea/pull/1826), [#1907](https://github.com/morluto/rea/pull/1907), [issue morluto/rea#1856](https://github.com/morluto/rea/issues/1856), [#1999](https://github.com/morluto/rea/pull/1999), [issue morluto/rea#1993](https://github.com/morluto/rea/issues/1993), [#2232](https://github.com/morluto/rea/pull/2232), [issue morluto/rea#2170](https://github.com/morluto/rea/issues/2170), [#2252](https://github.com/morluto/rea/pull/2252), [issue morluto/rea#2218](https://github.com/morluto/rea/issues/2218), [#2467](https://github.com/morluto/rea/pull/2467), [issue morluto/rea#2257](https://github.com/morluto/rea/issues/2257), [#2468](https://github.com/morluto/rea/pull/2468), [#2488](https://github.com/morluto/rea/pull/2488), [09b6828](https://github.com/morluto/rea/commit/09b68285166bef2b7633402c8adf7eb25823b47e), [7285e15](https://github.com/morluto/rea/commit/7285e15839a4fecf9940683699b781a60ccc4775), [b5ededd](https://github.com/morluto/rea/commit/b5ededd0f9c28d0f696a205103066928117368ee), [d8c76b6](https://github.com/morluto/rea/commit/d8c76b629639bda4405afa9ae2f7676b694f2f10), [98d8470](https://github.com/morluto/rea/commit/98d8470d36cfbf7cd25820a4ed430aebae20c0c5), [c22111e](https://github.com/morluto/rea/commit/c22111eca2c4bc84a084f12711f405b3e7422a40), [cecc6ec](https://github.com/morluto/rea/commit/cecc6ecbffe84689a173d1634f342e046b0c8ceb), [10935b7](https://github.com/morluto/rea/commit/10935b7b1136accab2a43b308ab380749da0f7ae), [63e796d](https://github.com/morluto/rea/commit/63e796d50ea323ae8dcfef80f86c8154bb106790), [d9e9fb2](https://github.com/morluto/rea/commit/d9e9fb2273543caab4eb21d896c33d462ada6971), [9049dba](https://github.com/morluto/rea/commit/9049dbaf0c3df32357419f9899965df2b611f493), [ed881c6](https://github.com/morluto/rea/commit/ed881c6d2ef6a8adba0c2a241ebc9ab4603b7868)). Thanks @Ashen00001, @charan-rathore, @coygeek, @EDMND-SRC, @JasonBuildAI, @jimpablo, @kaoru0822-kitauji, @N0zoM1z0, @NIU-123370, @Rinkia, @rudycelekli, @sx4im, @xeropresence, @XiaoBinGan, @xthunder0, @hzh666kevin-hue.
- **Native image identity:** preserve universal Mach-O containers and selected slice/subtype identity, chained fixup locations and library ordinals, exact signing text, PE resource capacity and ELF/header framing. Mach-O summary selection and independent slice commands avoid unnecessary delivery/work. Controlled routing/parser fixtures do not establish every installed-provider path. ([#1703](https://github.com/morluto/rea/pull/1703), [issue morluto/rea#1702](https://github.com/morluto/rea/issues/1702), [#1708](https://github.com/morluto/rea/pull/1708), [#1709](https://github.com/morluto/rea/pull/1709), [#1713](https://github.com/morluto/rea/pull/1713), [#1772](https://github.com/morluto/rea/pull/1772), [issue morluto/rea#1765](https://github.com/morluto/rea/issues/1765), [#1784](https://github.com/morluto/rea/pull/1784), [issue morluto/rea#1775](https://github.com/morluto/rea/issues/1775), [#1982](https://github.com/morluto/rea/pull/1982), [#1995](https://github.com/morluto/rea/pull/1995), [issue morluto/rea#1990](https://github.com/morluto/rea/issues/1990), [#2040](https://github.com/morluto/rea/pull/2040), [#2107](https://github.com/morluto/rea/pull/2107), [#2161](https://github.com/morluto/rea/pull/2161), [issue morluto/rea#2105](https://github.com/morluto/rea/issues/2105), [#2248](https://github.com/morluto/rea/pull/2248), [issue morluto/rea#2176](https://github.com/morluto/rea/issues/2176), [#2281](https://github.com/morluto/rea/pull/2281), [issue morluto/rea#2274](https://github.com/morluto/rea/issues/2274), [#2292](https://github.com/morluto/rea/pull/2292), [#2466](https://github.com/morluto/rea/pull/2466), [#2506](https://github.com/morluto/rea/pull/2506), [#2733](https://github.com/morluto/rea/pull/2733), [issue morluto/rea#2661](https://github.com/morluto/rea/issues/2661), [f598bab](https://github.com/morluto/rea/commit/f598bab488e38c378d69346fa3c106f9e18529ed), [108b5f9](https://github.com/morluto/rea/commit/108b5f94254281639badd25b1f448cca35dc89a2), [f1924cf](https://github.com/morluto/rea/commit/f1924cf9c1910d5d29d11e7bf634b51af8d738cf), [8bd5ba2](https://github.com/morluto/rea/commit/8bd5ba2d234cbd9a5bb9dd5daeaa191c3a1cad24), [b38429c](https://github.com/morluto/rea/commit/b38429cb6a0de8f4bb5ae01633e24abe2d7cde46), [8d4d631](https://github.com/morluto/rea/commit/8d4d631606c701aac150cf009d3b80ae4a7ae7d5), [ac57c88](https://github.com/morluto/rea/commit/ac57c88efe64c3125850b54d3d833227e6b8ec51), [dc038d1](https://github.com/morluto/rea/commit/dc038d16bba9046afd28970de9c47f6259a43524), [34a4bb6](https://github.com/morluto/rea/commit/34a4bb6508006fa113463ef20ed799a2994e9ee8), [ebc22d9](https://github.com/morluto/rea/commit/ebc22d99d62225505c0a84f6e39823ba4ef2f829), [c02fb0e](https://github.com/morluto/rea/commit/c02fb0ea8055f98def2b12ad557aed0faefd3639)). Thanks @1NormalGuy, @Aliciawque, @baron, @coygeek, @dhl84, @hemanthrayuduu, @JasonBuildAI, @kaoru0822-kitauji, @N0zoM1z0, @thangldw, @viniciusdsandrade, @XiaoBinGan, @Zburgers.
- **Archive and artifact provenance:** keep physical ZIP occurrence identity, exact signatures/names and partial inventory coverage; bound decompression and reject malformed keys/integers. Directory/ASAR/DMG reads retain ownership through cleanup and publication cancellation. Duplicate ZIP names remain explicitly ambiguous where the public projection cannot represent them; synthetic archive/cleanup cases do not prove every real Unblob or DMG failure path. ([#1726](https://github.com/morluto/rea/pull/1726), [issue morluto/rea#1725](https://github.com/morluto/rea/issues/1725), [#1854](https://github.com/morluto/rea/pull/1854), [issue morluto/rea#1844](https://github.com/morluto/rea/issues/1844), [#1865](https://github.com/morluto/rea/pull/1865), [issue morluto/rea#1860](https://github.com/morluto/rea/issues/1860), [#2069](https://github.com/morluto/rea/pull/2069), [#2106](https://github.com/morluto/rea/pull/2106), [#2144](https://github.com/morluto/rea/pull/2144), [#2365](https://github.com/morluto/rea/pull/2365), [#2493](https://github.com/morluto/rea/pull/2493), [918713a](https://github.com/morluto/rea/commit/918713af376e307ecf24d75e6a0dd9d1b544545b), [eb97da3](https://github.com/morluto/rea/commit/eb97da33881e3d1472d97a7d788ee35944fb6b07), [2260887](https://github.com/morluto/rea/commit/2260887f2092be6a3d9c79509e4ae4a3e52fce7f), [74d7da6](https://github.com/morluto/rea/commit/74d7da6ced0f869936ee0316b00616931b088066), [ed485a3](https://github.com/morluto/rea/commit/ed485a3dc8cc3a4baf8cabe01acb7aa0490b73b3), [921bb95](https://github.com/morluto/rea/commit/921bb9575505065f6cfd57b14d97d80d0f238546), [d8ceb3f](https://github.com/morluto/rea/commit/d8ceb3f276a65c181b146f68c0729ff4b6f9fa61), [9435c7a](https://github.com/morluto/rea/commit/9435c7aae279ba184937c0dfeb713fd574ba3f29), [bb54082](https://github.com/morluto/rea/commit/bb540826d65316c164d2de8ba4ef10f81a8ad073), [a73a55f](https://github.com/morluto/rea/commit/a73a55f53cc2b2e5b7618effe6ecb5cee6e0cee2), [3c57e1a](https://github.com/morluto/rea/commit/3c57e1a8b21b379fca55032d177078e1d8426e64), [ec0b081](https://github.com/morluto/rea/commit/ec0b081371f118e6e5e042fbef3479359c672ddb), [e7882b3](https://github.com/morluto/rea/commit/e7882b34f4ac4a26f903c5f49264acc2c19bb562), [b955b82](https://github.com/morluto/rea/commit/b955b825fc002a4bb408f2910b78768c566c2cd1)). Thanks @1052326311, @1NormalGuy, @baron, @Bortlesboat, @Chaoqi31, @DeryFerd, @kaoru0822-kitauji, @N0zoM1z0, @XiaoBinGan.
- **Setup/uninstall recovery:** preserve unrelated YAML text, caller-selected roots, symlinked data and existing recovery snapshots; explain malformed configuration shapes and retain failed rollback/cleanup owners. Show terminal dry-run plans and each selected skill location. Bunx registration and fault-injected recovery cases remain simulated rather than live-client verification. ([#1731](https://github.com/morluto/rea/pull/1731), [#1754](https://github.com/morluto/rea/pull/1754), [#1821](https://github.com/morluto/rea/pull/1821), [issue morluto/rea#1816](https://github.com/morluto/rea/issues/1816), [#1983](https://github.com/morluto/rea/pull/1983), [#2118](https://github.com/morluto/rea/pull/2118), [issue morluto/rea#2111](https://github.com/morluto/rea/issues/2111), [#2154](https://github.com/morluto/rea/pull/2154), [issue morluto/rea#2152](https://github.com/morluto/rea/issues/2152), [#2157](https://github.com/morluto/rea/pull/2157), [#2182](https://github.com/morluto/rea/pull/2182), [issue morluto/rea#2159](https://github.com/morluto/rea/issues/2159), [#2481](https://github.com/morluto/rea/pull/2481), [bb50813](https://github.com/morluto/rea/commit/bb508134efbba392a404b651d6ec05be8b7eac1e), [c240768](https://github.com/morluto/rea/commit/c2407689cb04df082e8db0b6afa8f8d022f6bcd1), [dcfc5d4](https://github.com/morluto/rea/commit/dcfc5d403ac997c2b08bee4753e57a1a4a8a8538), [21eff3b](https://github.com/morluto/rea/commit/21eff3bae10f58711a35ad44f86e9f96f9bb4790), [94153f1](https://github.com/morluto/rea/commit/94153f1c757e07a6d092e00903aa8b125d4a0afa), [2397137](https://github.com/morluto/rea/commit/23971376158e7815edcbe6c04a8df7c9504650dc), [c339f9e](https://github.com/morluto/rea/commit/c339f9ebe7bd1f30608d6ccb7175da523dd3c485), [f6775a3](https://github.com/morluto/rea/commit/f6775a36694a6a9c620f4248638e8056a6cbcb66)). Thanks @BeqaKs, @Jax367, @kaoru0822-kitauji, @NIU-123370, @shoyann, @viniciusdsandrade, @xeropresence, @zaydiscold.
- **Bun updates:** locate global installations before changing directories and reuse parsed client activation during maintenance. The update flow is covered with package-manager/filesystem simulations; a real Bun global installation update remains unverified. ([#1735](https://github.com/morluto/rea/pull/1735), [e90acae](https://github.com/morluto/rea/commit/e90acaea674f5f7f9f17ea472104afdc1422ada6), [b045df1](https://github.com/morluto/rea/commit/b045df100e1489e08a36d6ff04cea7feecf1332c)). Thanks @xeropresence.
- **JavaScript relationships:** preserve loader-specific package/TypeScript/CommonJS boundaries, exact file-URL and source-map identity, Electron intrinsic receivers/IPC guards and chunk metadata. Index repeated lookups and retain completed facts through resource limits and cancellation. Real Electron integration, Bun worker integration and failure-path cleanup remain unverified where the recorded lanes use fixtures. ([#1740](https://github.com/morluto/rea/pull/1740), [#1759](https://github.com/morluto/rea/pull/1759), [issue morluto/rea#1748](https://github.com/morluto/rea/issues/1748), [#1762](https://github.com/morluto/rea/pull/1762), [issue morluto/rea#1761](https://github.com/morluto/rea/issues/1761), [#1767](https://github.com/morluto/rea/pull/1767), [#1788](https://github.com/morluto/rea/pull/1788), [issue morluto/rea#1781](https://github.com/morluto/rea/issues/1781), [issue morluto/rea#1782](https://github.com/morluto/rea/issues/1782), [#1789](https://github.com/morluto/rea/pull/1789), [#1799](https://github.com/morluto/rea/pull/1799), [issue morluto/rea#1793](https://github.com/morluto/rea/issues/1793), [#1827](https://github.com/morluto/rea/pull/1827), [issue morluto/rea#1802](https://github.com/morluto/rea/issues/1802), [#1833](https://github.com/morluto/rea/pull/1833), [issue morluto/rea#1831](https://github.com/morluto/rea/issues/1831), [#1857](https://github.com/morluto/rea/pull/1857), [issue morluto/rea#1842](https://github.com/morluto/rea/issues/1842), [#1871](https://github.com/morluto/rea/pull/1871), [#1874](https://github.com/morluto/rea/pull/1874), [#1905](https://github.com/morluto/rea/pull/1905), [#1912](https://github.com/morluto/rea/pull/1912), [issue morluto/rea#1909](https://github.com/morluto/rea/issues/1909), [#1915](https://github.com/morluto/rea/pull/1915), [issue morluto/rea#1911](https://github.com/morluto/rea/issues/1911), [#1916](https://github.com/morluto/rea/pull/1916), [#1921](https://github.com/morluto/rea/pull/1921), [#1934](https://github.com/morluto/rea/pull/1934), [#1939](https://github.com/morluto/rea/pull/1939), [issue morluto/rea#1881](https://github.com/morluto/rea/issues/1881), [issue morluto/rea#1882](https://github.com/morluto/rea/issues/1882), [issue morluto/rea#1883](https://github.com/morluto/rea/issues/1883), [#1941](https://github.com/morluto/rea/pull/1941), [#2062](https://github.com/morluto/rea/pull/2062), [issue morluto/rea#2058](https://github.com/morluto/rea/issues/2058), [#2104](https://github.com/morluto/rea/pull/2104), [#2422](https://github.com/morluto/rea/pull/2422), [#2475](https://github.com/morluto/rea/pull/2475), [#2737](https://github.com/morluto/rea/pull/2737), [issue morluto/rea#2663](https://github.com/morluto/rea/issues/2663), [d3088c3](https://github.com/morluto/rea/commit/d3088c3795f6c37cb3b6aec0bb0c371dd0640ee8), [1dfb933](https://github.com/morluto/rea/commit/1dfb933e47db0156cf1cb61f28715b81e740c57b), [f8513a4](https://github.com/morluto/rea/commit/f8513a42dc44451a621127f06edde5d38bc93f69), [b8fab74](https://github.com/morluto/rea/commit/b8fab7429771bb17a7ca0297661de3c9935212af), [42b0ed1](https://github.com/morluto/rea/commit/42b0ed126df7d5b908ac369715378d5e519051b7), [c0904b4](https://github.com/morluto/rea/commit/c0904b4c1aabfccf2f0ab12f44960a0a1fbbbfae), [9f67e44](https://github.com/morluto/rea/commit/9f67e44e5d451d705bae9a579ad9802d5c9f097c), [e4532b5](https://github.com/morluto/rea/commit/e4532b537663026c3e5b784380eecd009c959a36), [1c37914](https://github.com/morluto/rea/commit/1c37914306b3ff257d7a0360f11c9bf2a3f2cac7), [5ab0cbb](https://github.com/morluto/rea/commit/5ab0cbb48ea5a4f11542eac44479b9af48f1dabd), [b7169c8](https://github.com/morluto/rea/commit/b7169c8b747cbc032913b31f008a3b81c77b6219), [f43fc02](https://github.com/morluto/rea/commit/f43fc0247527aebb8efc7d9db04276fe2d489323), [3ae826c](https://github.com/morluto/rea/commit/3ae826ceba6f25b08bce50bcb51001209bc16b57), [1b95302](https://github.com/morluto/rea/commit/1b953021eadf4cac09e20be388d79536903633b3), [eaa9616](https://github.com/morluto/rea/commit/eaa961658578522cecfbba7c7be1acf1c64da65a), [c923fb0](https://github.com/morluto/rea/commit/c923fb0dc9462e302b84a15770131ff899f89455), [c7a890d](https://github.com/morluto/rea/commit/c7a890d12987d9b2d496119c1fbf1a88c0fc298e)). Thanks @Aurantium-47, @baron, @chqcjq, @coygeek, @cyanseek, @fengdwx, @flitzrrr, @immanuwell, @kaoru0822-kitauji, @N0zoM1z0, @Nelglor, @ranmori, @rudycelekli, @shoyann, @xu91102.
- **Browser observations:** preserve effective source-map URLs, empty query/fragment identity, response-body provenance, service-worker policies and explicit uncertainty for malformed events. Bound discovery lifetimes and retain cleanup ownership. Interrupt/retry/failure cases validated with controlled CDP/session fixtures do not establish those paths in real Chromium. ([#1742](https://github.com/morluto/rea/pull/1742), [issue morluto/rea#1729](https://github.com/morluto/rea/issues/1729), [#1829](https://github.com/morluto/rea/pull/1829), [#2074](https://github.com/morluto/rea/pull/2074), [#2099](https://github.com/morluto/rea/pull/2099), [#2239](https://github.com/morluto/rea/pull/2239), [issue morluto/rea#2169](https://github.com/morluto/rea/issues/2169), [#2477](https://github.com/morluto/rea/pull/2477), [486b567](https://github.com/morluto/rea/commit/486b56724c4eaba50719177bcb9eae84d6907297), [a82e37c](https://github.com/morluto/rea/commit/a82e37c8065cc200a0ca3e14e7c4f85e4e468580), [a8d4984](https://github.com/morluto/rea/commit/a8d4984404b611e60b88bc42289c0ba07eee4578), [966f7cc](https://github.com/morluto/rea/commit/966f7cc84b4a8bb0043641cb64d7a7aaf8715e05), [221c856](https://github.com/morluto/rea/commit/221c85648108e9846d9920b0964dafa7f108d581), [951b095](https://github.com/morluto/rea/commit/951b095e11adf9faa9c4a27e212260060aa6b095), [f09bb8a](https://github.com/morluto/rea/commit/f09bb8a99773f79c7306b8799c86f856fe2c0d4b), [6bd2b28](https://github.com/morluto/rea/commit/6bd2b28161411f70ed5dc72e42c2736a1aab758b)). Thanks @1052326311, @chAng-L19, @chqcjq, @coygeek, @evenafterallisaid, @fisforfaheem, @itsF4LCON, @N0zoM1z0, @shayanxcyber.
- **Managed metadata/CIL:** preserve ExportedType and pointer-table ownership, generic/array/function-pointer/modifier signatures, ReadyToRun host identity, metadata ExtraData and signed zero. Charge retained facts and decoded instruction counts instead of broad decoding bounds; malformed layouts remain caller input errors. ([#1743](https://github.com/morluto/rea/pull/1743), [issue morluto/rea#1736](https://github.com/morluto/rea/issues/1736), [#1768](https://github.com/morluto/rea/pull/1768), [#1822](https://github.com/morluto/rea/pull/1822), [issue morluto/rea#1801](https://github.com/morluto/rea/issues/1801), [#1828](https://github.com/morluto/rea/pull/1828), [issue morluto/rea#1812](https://github.com/morluto/rea/issues/1812), [#1835](https://github.com/morluto/rea/pull/1835), [issue morluto/rea#1832](https://github.com/morluto/rea/issues/1832), [#1886](https://github.com/morluto/rea/pull/1886), [#1893](https://github.com/morluto/rea/pull/1893), [#1897](https://github.com/morluto/rea/pull/1897), [issue morluto/rea#1878](https://github.com/morluto/rea/issues/1878), [#1943](https://github.com/morluto/rea/pull/1943), [issue morluto/rea#1940](https://github.com/morluto/rea/issues/1940), [#1985](https://github.com/morluto/rea/pull/1985), [#2194](https://github.com/morluto/rea/pull/2194), [issue morluto/rea#2191](https://github.com/morluto/rea/issues/2191), [#2437](https://github.com/morluto/rea/pull/2437), [52e76c5](https://github.com/morluto/rea/commit/52e76c5d39b58d1685bb5e22bd40d19524dfa2cb)). Thanks @ac1982, @chqcjq, @fisHarly0, @flitzrrr, @JasonBuildAI, @JayOfTheKeyboard, @jhuang-tw, @kaoru0822-kitauji, @N0zoM1z0, @Nelglor.
- **Hopper failure ownership:** preserve startup/process outcomes, retain cleanup leases across one-shot failures, wait for late bridges, and stop owned Linux demo children when their REA owner exits. Denied/crashed/late-bridge/queued-request failure paths use simulated process or bridge fixtures; they are not fresh installed-Hopper verification. ([#1763](https://github.com/morluto/rea/pull/1763), [issue morluto/rea#1751](https://github.com/morluto/rea/issues/1751), [#1817](https://github.com/morluto/rea/pull/1817), [#1866](https://github.com/morluto/rea/pull/1866), [#1927](https://github.com/morluto/rea/pull/1927), [#2474](https://github.com/morluto/rea/pull/2474), [#2499](https://github.com/morluto/rea/pull/2499), [issue morluto/rea#2459](https://github.com/morluto/rea/issues/2459)). Thanks @coygeek, @Joshwong1908, @kaoru0822-kitauji, @N0zoM1z0, @rudycelekli.
- **IDA session boundaries:** retain non-x86 call coverage and unresolved-procedure uncertainty, and bound private universal-Mach-O inputs to the exact selected slice. Transient headless busy recovery was exercised on macOS arm64 IDA Pro 9.1; Linux headless, attached GUI navigation and worker-reaped cleanup paths retain their documented verification gaps. ([#1764](https://github.com/morluto/rea/pull/1764), [issue morluto/rea#1712](https://github.com/morluto/rea/issues/1712), [#1778](https://github.com/morluto/rea/pull/1778), [issue morluto/rea#1766](https://github.com/morluto/rea/issues/1766), [#1785](https://github.com/morluto/rea/pull/1785), [issue morluto/rea#1769](https://github.com/morluto/rea/issues/1769), [#1906](https://github.com/morluto/rea/pull/1906), [issue morluto/rea#1794](https://github.com/morluto/rea/issues/1794), [#2201](https://github.com/morluto/rea/pull/2201), [issue morluto/rea#2139](https://github.com/morluto/rea/issues/2139), [#2291](https://github.com/morluto/rea/pull/2291), [issue morluto/rea#2285](https://github.com/morluto/rea/issues/2285), [#2324](https://github.com/morluto/rea/pull/2324), [12fa8fd](https://github.com/morluto/rea/commit/12fa8fd9f986dbd4ff58165c3ec71a46dc8a8a3b)). Thanks @akushonkamen, @coygeek, @kaoru0822-kitauji, @N0zoM1z0, @nikolay-dementiev, @zecaro.
- **Input and capability diagnostics:** identify invalid paths, format refusals, missing host tools and non-retryable errors without losing caller selections or retryable capability facts. Foreign execution/device mutation annotations remain destructive, analysis-target text is untrusted, and output ordering follows code points rather than host locale. ([#1770](https://github.com/morluto/rea/pull/1770), [#1867](https://github.com/morluto/rea/pull/1867), [#1869](https://github.com/morluto/rea/pull/1869), [issue morluto/rea#1855](https://github.com/morluto/rea/issues/1855), [#1872](https://github.com/morluto/rea/pull/1872), [#1879](https://github.com/morluto/rea/pull/1879), [issue morluto/rea#1853](https://github.com/morluto/rea/issues/1853), [#1899](https://github.com/morluto/rea/pull/1899), [#1902](https://github.com/morluto/rea/pull/1902), [#1904](https://github.com/morluto/rea/pull/1904), [#1908](https://github.com/morluto/rea/pull/1908), [#1925](https://github.com/morluto/rea/pull/1925), [#1987](https://github.com/morluto/rea/pull/1987), [issue morluto/rea#1979](https://github.com/morluto/rea/issues/1979), [#2004](https://github.com/morluto/rea/pull/2004), [issue morluto/rea#2002](https://github.com/morluto/rea/issues/2002), [#2026](https://github.com/morluto/rea/pull/2026), [#2091](https://github.com/morluto/rea/pull/2091), [#2133](https://github.com/morluto/rea/pull/2133), [issue morluto/rea#2129](https://github.com/morluto/rea/issues/2129), [#2145](https://github.com/morluto/rea/pull/2145), [issue morluto/rea#2143](https://github.com/morluto/rea/issues/2143), [#2472](https://github.com/morluto/rea/pull/2472), [#2478](https://github.com/morluto/rea/pull/2478), [issue morluto/rea#2454](https://github.com/morluto/rea/issues/2454), [#2494](https://github.com/morluto/rea/pull/2494), [#2507](https://github.com/morluto/rea/pull/2507), [#2509](https://github.com/morluto/rea/pull/2509), [#2708](https://github.com/morluto/rea/pull/2708), [3d83135](https://github.com/morluto/rea/commit/3d8313598785359c424d7305f503cb69fc8639a6), [c9fed50](https://github.com/morluto/rea/commit/c9fed50691ae014890e91a72534773d2fb58ce44), [8b18171](https://github.com/morluto/rea/commit/8b181716aca2cb467be499cba226abfb5ae19141), [7a2cd1e](https://github.com/morluto/rea/commit/7a2cd1ea0a63e8c44166860b7ee9c7a1aa43492b)). Thanks @astralyx0129, @CassieFox23, @DeryFerd, @dollarsmoney, @fisforfaheem, @flitzrrr, @Ha1baraA11, @jimpablo, @kaoru0822-kitauji, @larinyo, @N0zoM1z0, @rudycelekli, @SII-YDD, @viniciusdsandrade, @XiaoBinGan.
- **Android analysis:** retain decoded values, duplicate-class inventories and executable static initializers, reject malformed JADX UTF-8, and keep inline search provenance. Apktool accepts `--no-strings` and explains host requirements. Windows batch-launcher selection also resolves PATH/PATHEXT before choosing its launch mode. Recorded Apktool coverage is Linux/OpenJDK, and changed JADX/batch failure paths do not establish native Windows behavior. ([#1777](https://github.com/morluto/rea/pull/1777), [issue morluto/rea#1774](https://github.com/morluto/rea/issues/1774), [#1876](https://github.com/morluto/rea/pull/1876), [issue morluto/rea#1846](https://github.com/morluto/rea/issues/1846), [#1890](https://github.com/morluto/rea/pull/1890), [issue morluto/rea#1850](https://github.com/morluto/rea/issues/1850), [#1946](https://github.com/morluto/rea/pull/1946), [issue morluto/rea#1945](https://github.com/morluto/rea/issues/1945), [#2707](https://github.com/morluto/rea/pull/2707), [issue morluto/rea#2672](https://github.com/morluto/rea/issues/2672), [0677468](https://github.com/morluto/rea/commit/0677468877465c8dfbe25c72f142a7b5bc8a3212), [fe8d0bd](https://github.com/morluto/rea/commit/fe8d0bdd36fe12bc0836468d1a702a34fbdc219b), [#2802](https://github.com/morluto/rea/pull/2802)). Thanks @akushonkamen, @coygeek, @HeadmasterEggy, @kaoru0822-kitauji, @N0zoM1z0, @psipher.
- **ADB acquisition:** preserve device paths and directory identity, prevent host destination collisions/overwrites, count UTF-8 logcat bytes and restricted permissions, and evaluate the complete Activity Manager start report. Launch attempts, status, activity and raw failure output remain observations; real-device timeout/mixed-report and Android 8 directory-read paths remain unverified beyond the documented fixtures. ([#1962](https://github.com/morluto/rea/pull/1962), [#2212](https://github.com/morluto/rea/pull/2212), [issue morluto/rea#2175](https://github.com/morluto/rea/issues/2175), [#2221](https://github.com/morluto/rea/pull/2221), [issue morluto/rea#2220](https://github.com/morluto/rea/issues/2220), [#2359](https://github.com/morluto/rea/pull/2359), [c42232f](https://github.com/morluto/rea/commit/c42232f566d55fe7191763649755148c768eef9f), [55b6148](https://github.com/morluto/rea/commit/55b61485e9f7568b69597eef8696ff6abfe53cfd), [3bdc873](https://github.com/morluto/rea/commit/3bdc8730bd98e073522bcb8ba86527be402c0cbe), [c89972c](https://github.com/morluto/rea/commit/c89972c8777d0114566fe76a060849a18bdd2659), [e1cd292](https://github.com/morluto/rea/commit/e1cd292aa6e8f5f13441d408c66126782c8dbb0d)). Thanks @coygeek, @dakjdakd, @DeryFerd, @huiq777, @sx4im.
- **WASM names and diagnostics:** preserve debug/custom-section names including NUL and leading BOM, accept unpadded WABT section rows, and avoid irrelevant tool-remediation advice on unrelated runtime-root failures. ([#1963](https://github.com/morluto/rea/pull/1963), [issue morluto/rea#1956](https://github.com/morluto/rea/issues/1956), [#1967](https://github.com/morluto/rea/pull/1967), [#1981](https://github.com/morluto/rea/pull/1981), [#2084](https://github.com/morluto/rea/pull/2084), [#2102](https://github.com/morluto/rea/pull/2102)). Thanks @1052326311, @bugstan, @N0zoM1z0, @sathariels, @STiFLeR7, @viniciusdsandrade.
- **Source-map and path handling:** retain source-map entries when embedded-content counts differ or an optional `sourceMappingURL` is used, and keep `file:` URLs out of operator-relative path resolution. ([#2873](https://github.com/morluto/rea/pull/2873), [#2886](https://github.com/morluto/rea/pull/2886), [#2882](https://github.com/morluto/rea/pull/2882), [issue morluto/rea#2821](https://github.com/morluto/rea/issues/2821), [issue morluto/rea#2883](https://github.com/morluto/rea/issues/2883), [issue morluto/rea#2577](https://github.com/morluto/rea/issues/2577)). Thanks @JFSAS, @rileygramlich, @shoyann, @shubhamahuja9999, @coygeek.
- **Native metadata:** accept declared facts in native dispatch inventories and share native verification input diagnostics. ([#2906](https://github.com/morluto/rea/pull/2906), [#2901](https://github.com/morluto/rea/pull/2901), [issue morluto/rea#2564](https://github.com/morluto/rea/issues/2564), [issue morluto/rea#2621](https://github.com/morluto/rea/issues/2621)). Thanks @ShitK, @coygeek.
### ⚠ BREAKING CHANGES
- **Runtime attachment state:** `attached` may be `null` in JavaScript, browser and Electron discovery/inspection results. Treat null as unknown; do not coerce it to false. ([e55ef72](https://github.com/morluto/rea/commit/e55ef7213515ba204c3d88a4487b4ee6a2773b06)).
- **Source maps:** original-source and mapping rows preserve a null source name when unavailable. Handle null explicitly instead of expecting an invented unknown-source string; new index fields remain optional when reading older Evidence. ([ee50ed1](https://github.com/morluto/rea/commit/ee50ed121e38cc6e1a2ff0dc8b789f15f71c5402)).
- **Streamed filtered JSON:** `--filter-output` now emits JSON `null` instead of `{}` when the selected value is null; consumers must distinguish a present null from a missing selection. Use `--full-output` when an object envelope is required. ([#2333](https://github.com/morluto/rea/pull/2333)). Thanks @Jackmeson1.
- **Call graphs:** `get_call_graph.normalized_result` is now `{ graph, limitations }`; read traversal-depth groups under `graph` and retain relationship-specific caveats. Contract/fixture validation does not establish new Ghidra or IDA engine coverage. ([5dfb9d0](https://github.com/morluto/rea/commit/5dfb9d050c2cc44a61b183dae765bef4d22a53b7)).
- **MCP discovery:** `binary_session` no longer repeats `server_identity.catalog.tools`. Use the connected server tool list, grouped `tool_availability` and provider capabilities; see [MCP contracts](docs/mcp-contracts.md). Origin: [6395e8d](https://github.com/morluto/rea/commit/6395e8d3007d5ee453477460e7ee1c51cff76be0). ([#1988](https://github.com/morluto/rea/pull/1988)). Thanks @kaoru0822-kitauji, @N0zoM1z0.
- **Provider admission:** MIPS targets with unresolved GNU ABI attributes are refused. Supply supported ABI declarations. Saved Hopper databases on macOS are unavailable because the launcher cannot run the analysis bridge when opening a database; select an executable target instead. Database support requires a script-capable database launch route. ([d4bd65c](https://github.com/morluto/rea/commit/d4bd65c53c0254d0195cc7ab8da163dcadfd3b9b)).
- **Segment extents:** `binary_overview` segment length may be null when Hopper coordinates cannot establish a finite extent in one address space. Preserve unknown rather than converting it to zero or a saturated value. ([36db19c](https://github.com/morluto/rea/commit/36db19c59)).
- **Invocation identity:** `server.command_path` may be null, and registration/setup statuses or doctor classifications may be unknown. Preserve these states and establish the actual invocation before treating a registration as aligned. ([187f113](https://github.com/morluto/rea/commit/187f113f7)). Thanks @hemv-857, @qorvr.
- **ADB installation:** the optional `replace` input is unchanged. Results now require `replace_requested` to record the requested `adb install -r` mode; `replaced` is nullable because prior device package state was not observed. Update result readers to accept null instead of treating reinstall intent as proof of replacement. ([864314a](https://github.com/morluto/rea/commit/864314ac857924a7e6e65bfb37e8dc720fcc21e7)).
- **Managed/native verification:** unresolved P/Invoke results may retain `matched_native` and candidate facts when module identity is unknown. Interpret `status`, `basis` and `limitations`; `module-identity-unknown` does not verify correspondence, and unresolved no longer implies empty observations. ([3f6b575](https://github.com/morluto/rea/commit/3f6b5755b3e699bd9373641acffe0f2c5d2ab094)).
Full contribution record: [893 commits and 529 merged PRs](https://github.com/morluto/rea/blob/rea-agents-6.5.0/docs/releases/6.5.0.contributions.json).
## [6.4.0](https://github.com/morluto/rea/compare/rea-agents-6.3.0...rea-agents-6.4.0) (2026-10-11)
### Highlights
+99
View File
@@ -24,6 +24,82 @@ costs change.
When adding or changing an MCP tool, follow the [tool design guide](docs/tool-design.md) and preserve the canonical contracts and generated catalog.
## Reporting issues
Use the issue form that fits the report and keep it concise. For bugs, include
the actual result, the shortest reproduction you ran, the tested version and
environment, and the smallest useful output, artifact, screenshot, or source
reference. Ground expected behavior in documentation, source, or a known-good
version when available. State observed impact and frequency; keep untested scope
and suspected causes explicit.
Incomplete reports and intermittent failures are welcome. If a narrative answer
or supporting evidence is unavailable, write `NOT_ENOUGH_INFO` and explain the
gap. Clearly label source-only findings and proposed checks; do not claim runtime
reproduction or results you did not observe. A root-cause analysis or fix is not
required to report a problem. Documentation reports should identify the relevant
passage or missing guidance and explain the correction or confusion.
Feature and design proposals can start with a problem and desired outcome.
Examples, prior art, and measurements help when available; ideas without them
are welcome. Maintainers can investigate reports and help resolve open questions.
Human ownership and readiness declarations apply to PRs as described below.
## Pull request ownership and readiness
AI-assisted contributions are welcome. Every PR must have a human owner who
takes responsibility for the complete change, including code, tests,
documentation, and claims produced with AI assistance. The PR author is the
owner unless another human is named in the PR and explicitly accepts ownership.
These requirements also apply to maintainer-authored PRs.
Before requesting maintainer review, the human owner must:
- Review the final diff and relevant existing code. Be able to explain the
problem, the chosen approach, how it fits REA, and meaningful edge cases.
- Provide evidence that the change works. Summarize what was checked, the
observed results, and meaningful gaps in the PR body. Use focused tests,
representative CLI/MCP output, CI artifacts, screenshots, or other checks
appropriate to the change. For fixes, show the original failure and why the
regression coverage catches it when practical. Documentation-only changes
can use source, link, or rendered-document checks. Label source-derived
conclusions and proposed checks accurately; never report an unrun check as
passing. Follow the [verification requirements](#development-feedback-and-pr-verification)
for required checks and real-provider coverage.
- Complete the **Author readiness** declaration in the PR template, explicitly
stating that the PR is ready for initial review.
Keep the PR description concise and current. Explain the problem, observable
outcome, approach, and useful evidence; link long logs or put them in optional
details. Address actionable automated review findings and explain disagreements.
Human ownership and review remain required when CI or an automated reviewer
reports success. Readiness requests maintainer review; it does not grant scope
approval or permission to merge.
Draft PRs may request early feedback with open questions and incomplete checks.
Leave the readiness declaration unchecked until the human owner has completed
it. The declaration records initial readiness; later pushes do not invalidate
it or require another human sign-off. Keep the PR description and affected
validation current as work changes. AI agents must leave human declarations to
the human owner and must not attest to a human review on their behalf.
The `PR readiness` check reports pending for drafts and requires a named human
owner and all three template declarations for non-drafts. It checks that the
declaration is filled, not whether the code or evidence is correct. Existing PRs
can copy the **Author readiness** section from the current template.
Open PRs must have distinct head commits because GitHub shares commit statuses.
Maintainers enable merge enforcement after the workflow is merged: run
`gh workflow run pr-readiness.yml -f pull_request_number=NUMBER` for existing
open PRs, then add `PR readiness` from GitHub Actions to the branch's required
status checks alongside its existing checks. Deploy the workflow to each
protected target branch before requiring it there. The workflow reads PR
metadata using trusted default-branch code and writes only a commit status.
Maintainers may return a PR to the author or close it when the problem is
unsupported, validation is missing, or the owner cannot explain the change.
Contributors are responsible for completing this work before requesting review.
## Contribution scope
REA focuses on software structure, runtime behavior, and evidence through
@@ -142,6 +218,21 @@ caches deterministic builds and static checks across Git worktrees. After a pack
Repository invariants live in [AGENTS.md](AGENTS.md). Keep provider-specific
protocols in adapters and shared CLI/MCP workflows in the application layer.
### Windows source checkouts
The published npm package includes the Windows x64 native addon and its
manifest. A source checkout does not: `native/windows/build/` is ignored by
Git, and neither `npm ci` nor `npm run build:cached` (or `npm run build`)
produces `rea-windows-x64.node` or `manifest.json` in that directory.
Windows workflows and tests that require native controls need these artifacts;
without them, those controls are unavailable even after a successful runtime build.
Follow the [Windows native build guide](native/windows/README.md#build) before
running those workflows from source. The artifact build requires Linux,
MinGW-w64, and `tar`; the build script does not run on Windows. See that guide
for the build and artifact-verification commands and the separate real Windows
verification prerequisites. Keep the generated addon and manifest uncommitted.
## Documentation website
The VitePress site uses the Markdown files in `docs/`. Run `npm run docs:dev`
@@ -166,6 +257,10 @@ The separate GitHub Pages host uses only the manual
## Development feedback and PR verification
For routine merges, wait for the required checks to pass for the current PR
revision. Administrators retain an override for deliberate maintainer recovery.
If `main` is broken, prioritize a focused repair before updating affected PRs.
Select checks for the behavior changed. For a source edit:
```bash
@@ -187,6 +282,10 @@ CI diagnosis. Packaging/setup/distribution changes additionally require
`npm run verify:package` and `npm pack --dry-run`; provider changes require the
matching real-provider lane. State which host/provider workflows ran.
For CI/test correctness, drift, or performance investigations, use the
[`rea-ci-tests-audit` contributor skill](.agents/skills/rea-ci-tests-audit/SKILL.md).
It guides reproduction and comparable measurements before changing checks.
`check:pr` reuses build and static-check outputs while executing the test suite
fresh, including the fast Python producer checks. `docs:check` also runs the
onboarding example consistency suite for README-only changes.
+29 -6
View File
@@ -1,6 +1,7 @@
<div align="center">
**English** · [简体中文](README_zh.md) · [繁體中文](README_zh-TW.md) · [日本語](README_ja.md) · [한국어](README_ko.md) · [Türkçe](README_tr.md) · [Русский](README_ru.md) · [Tiếng Việt](README_vi.md) · [ไทย](README_th.md) · [Deutsch](README_de.md) · [Español](README_es.md) · [Français](README_fr.md) · [Українська](README_uk.md) · [Polski](README_pl.md) · [Português (Brasil)](README_pt-BR.md) · [العربية](README_ar.md) · [فارسی](README_fa.md) · [Bahasa Indonesia](README_id.md) · [Italiano](README_it.md)
**English** · [简体中文](docs/readme/README_zh.md) · [繁體中文](docs/readme/README_zh-TW.md) · [日本語](docs/readme/README_ja.md) · [한국어](docs/readme/README_ko.md) · [Türkçe](docs/readme/README_tr.md) · [Русский](docs/readme/README_ru.md) · [Tiếng Việt](docs/readme/README_vi.md) · [ไทย](docs/readme/README_th.md) · [Deutsch](docs/readme/README_de.md) · [Español](docs/readme/README_es.md) · [Français](docs/readme/README_fr.md) · [Українська](docs/readme/README_uk.md) · [Polski](docs/readme/README_pl.md) · [Português (Brasil)](docs/readme/README_pt-BR.md) · [العربية](docs/readme/README_ar.md) · [فارسی](docs/readme/README_fa.md) · [Bahasa Indonesia](docs/readme/README_id.md) · [Italiano](docs/readme/README_it.md)
· [Ελληνικά](docs/readme/README_el.md)
# REA: Reverse Engineer Anything
@@ -23,6 +24,16 @@
<code>npx rea-agents setup</code>
</div>
> [!IMPORTANT]
> Get REA from [morluto/rea](https://github.com/morluto/rea),
> [rea.tools](https://rea.tools/), or the [rea-agents npm package](https://www.npmjs.com/package/rea-agents).
> Follow the installation and release links from these official sources. A similar
> repository name or copied README does not establish that a download is official.
<div align="center">
<br />
<img src="docs/assets/rea-hopper-analysis.png" alt="REA launching its analysis bridge inside Hopper while inspecting a native binary" width="1200" />
@@ -57,6 +68,11 @@ Setup registers REA with your agent and installs matching workflow instructions.
## Quick start
On Linux, Ghidra, JADX, firmware tool launches and static JavaScript/Electron
analysis require a procps-compatible `ps` on REA's `PATH` for process ownership
checks. Recovery steps are in `docs/installation.md` under
"Linux owned-process prerequisite".
### Set up your agent
With Node.js and npm installed, run:
@@ -152,15 +168,16 @@ Additional tools and host support depend on the target:
| Target | What REA returns | Requirements and guide |
| ---------------------- | ------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------- |
| Native binaries | Pseudocode, assembly, strings, symbols, calls and references | Hopper, Ghidra or IDA; [native analysis](https://rea.tools/guides/native/) |
| Native binaries | Pseudocode, assembly, strings, symbols, calls and references | Hopper, Ghidra or IDA; [native analysis](docs/native-investigation.md) |
| Offline ELF layout | Sections, segments, original symbols/relocations and static mitigation candidates | Caller-supplied pwntools on Linux x64; [binary diagnostics](docs/binary-diagnostics.md) |
| Go binaries | Embedded compiler version, modules, replacements and build settings | Built-in static ELF/PE/thin Mach-O inspection; no Go installation required |
| Rust binaries | Embedded cargo-auditable crate names, versions, sources and dependency edges | Built-in static ELF/PE/thin Mach-O inspection; [Rust binaries](docs/rust-binaries.md) |
| WASM artifact | Validation, sections, exact import/export WAT forms and decoded WAT | Caller-supplied WABT 1.0.42; `inspect_wasm_artifact` |
| EVM bytecode | Dispatch selectors, byte offsets, inferred arguments and mutability | Local raw/hex carrier on Linux x64; [offline EVM guide](docs/evm-bytecode.md) |
| SQLite databases | Tables, columns, indexes, views, triggers and selected ordinary-table rows | Local database/WAL snapshot; Node.js 24.x >=24.15 or 26+ with native SQLite limits |
| Recorded Linux crashes | Raw notes, every recorded thread's registers/signals and optional mapping candidates | Caller-supplied pwntools on Linux x64; optional GDB/pwndbg; [recorded crashes](docs/recorded-crashes.md) |
| JavaScript / Electron | Modules, imports, source maps, routes, IPC and native add-on relationships | Node.js and npm; [application analysis](https://rea.tools/guides/javascript/) |
| Websites | Page structure, scripts, network observations and requested screenshots | A Chrome-family browser; [browser analysis](https://rea.tools/guides/browser/) |
| JavaScript / Electron | Modules, imports, source maps, routes, IPC and native add-on relationships | Node.js and npm; Linux: procps-compatible `ps`; [application analysis](docs/javascript-application-workflows.md) |
| Websites | Page structure, scripts, network observations and requested screenshots | A Chrome-family browser; [browser analysis](docs/browser-observation.md) |
| Saved network captures | Requests, responses, exposed payloads and source locations | HAR or Fiddler Everywhere SAZ; mitmdump on Linux for native mitmproxy; [capture guide](docs/web-network-captures.md) |
| .NET assemblies | Metadata, CIL instructions, declared native dependencies and build comparisons | None (static only); [managed-code guide](docs/managed-code-analysis.md) |
| Android APKs | Manifest declarations, classes, decompiled methods and references | Headless JADX and a full JDK on Linux/macOS/Windows x64; [Android guide](docs/android-analysis.md) |
@@ -179,11 +196,17 @@ Static JavaScript and .NET inspection read the supplied files without running
the application. Runtime capture runs or interacts with the selected target
using your user permissions; each runtime guide describes its effects.
For offline native-binary inspection, REA also supports caller-supplied GNU
objdump and Rizin. See the [reverse-engineering tools guide](docs/reverse-engineering-tools.md)
for provider setup, CLI commands, and Evidence limitations. REA does not install
or update either provider.
<a id="choosing-a-deep-analysis-provider"></a>
Native formats and host support vary by provider. See
[Hopper and Ghidra setup](docs/installation.md#hopper), the
[IDA guide](docs/ida-provider.md) (including an optional native MCP bridge), and
[IDA guide](docs/ida-provider.md) (including an optional native MCP bridge), the
[Kuna decompiler guide](docs/kuna-provider.md), and
[experimental Windows Ghidra support](docs/windows-ghidra-p0.md).
Ghidra also supports [16-bit DOS analysis](docs/ghidra-dos.md).
For large binaries, raise the provider's startup deadline with
@@ -322,7 +345,7 @@ Report vulnerabilities through [SECURITY.md](SECURITY.md).
## Star history
🎉 **170,000 GitHub stars — thank you!**
🎉 **250,000 GitHub stars — thank you!**
Thanks to everyone using REA, reporting bugs, requesting features, testing builds, and contributing fixes.
+1
View File
@@ -58,6 +58,7 @@ pin inspection sources; they are not included in the npm package.
| ghidra-nativeaot | [MIT](https://github.com/Washi1337/ghidra-nativeaot/blob/effeb734fc570c32650f88b159608979dc7b423e/LICENSE.md) | Pinned inspection source; derived parser attribution above. |
| JADX | [Apache-2.0](https://github.com/skylot/jadx/blob/v1.5.6/LICENSE) | The audited jadx-headless-mcp source uses JADX 1.5.6 for Android decompilation; the engine JAR is separately supplied. |
| jadx-headless-mcp | [Apache-2.0](https://github.com/1013503897/jadx-headless-mcp/blob/5844800a486d2248fa949b74c9896285f5b54de2/LICENSE) | v0.7.1; pinned submodule and user-supplied JAR. |
| Kuna | [Apache-2.0](https://github.com/Noelo-Lab/kuna/blob/v1.833/LICENSE), with its [NOTICE](https://github.com/Noelo-Lab/kuna/blob/v1.833/NOTICE) | Bring your own `kuna`; REA verified 1.833 and never downloads, installs or bundles it. REA invokes it as a separate process. |
| Apktool | [Apache-2.0](https://github.com/iBotPeaches/Apktool/blob/master/LICENSE.md) | User-supplied Android resource decoder. |
| Binwalk | [MIT](https://github.com/ReFirmLabs/binwalk/blob/4fdab3d464d97b68e0af9088df3f9e2e1545b21c/LICENSE) | v3.1.0; pinned submodule and user-supplied firmware engine. |
| Unblob | [MIT](https://github.com/onekey-sec/unblob/blob/1fcc7a0a584a70a96c31f5a276c20944d199a089/LICENSE) | 26.6.4; pinned submodule and user-supplied firmware engine. |
+107 -9
View File
@@ -3,8 +3,14 @@ import com.google.gson.*;
import io.modelcontextprotocol.kotlin.sdk.server.*;
import io.modelcontextprotocol.kotlin.sdk.types.*;
import jadx.api.*;
import jadx.api.plugins.input.data.ICodeReader;
import jadx.api.plugins.input.data.IMethodRef;
import jadx.api.plugins.input.insns.InsnIndexType;
import jadx.api.plugins.input.insns.Opcode;
import jadx.core.dex.info.MethodInfo;
import jadx.core.dex.nodes.*;
import java.io.PrintStream;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.stream.Collectors;
@@ -79,11 +85,17 @@ class ReaJadxBridge {
ordered.sort(Comparator.comparing(MethodNode::getAlias));
List<MethodSnapshot> methods = ordered.stream().map(method -> {
String name = method.getAlias();
String shortId = method.getMethodInfo().getShortId();
String rawName = method.getMethodInfo().getName();
if (!shortId.startsWith(rawName + "("))
throw new IllegalStateException("JADX method ID has no raw-name prefix: " + shortId);
String descriptor = shortId.substring(rawName.length());
String signature = name + "(" + method.getArgTypes().stream()
.map(Object::toString).collect(Collectors.joining(", "))
+ "): " + method.getReturnType().toString();
return new MethodSnapshot(name, method.getMethodInfo().getShortId(), Map.of(
return new MethodSnapshot(name, shortId, Map.of(
"name", name, "signature", signature,
"dex_descriptor", descriptor,
"is_constructor", method.isConstructor(), "def_pos", 0));
}).toList();
List<Map<String, Object>> fields = node.getFields().stream()
@@ -140,6 +152,57 @@ class ReaJadxBridge {
"smali", true, List.of("static_initializer_disassembly"));
}
// The local transport admits 8 MiB frames. Reserve 1 KiB for the fixed
// JSON-RPC/CallToolResult envelope and generated numeric request id, and
// account for JSON text being escaped again inside TextContent.
private static final int MAX_XREF_TEXT_BYTES = 8 * 1024 * 1024 - 1024;
private static int escapedJsonBytes(Object value) {
return JSON.toJson(JSON.toJson(value)).getBytes(StandardCharsets.UTF_8).length;
}
private static final class InvokeBudget {
long remaining;
InvokeBudget(long remaining) { this.remaining = Math.max(0, remaining); }
boolean admit(Map<String, Object> site) {
// Check a conservative two-layer escape bound before serialization.
long bound = 256;
for (Object value : site.values())
if (value instanceof String text) bound += 12L * text.length();
if (bound > remaining) return false;
long bytes = escapedJsonBytes(site) - 2L + 1L;
if (bytes > remaining) return false;
remaining -= bytes;
return true;
}
}
private record DirectInvokes(List<Map<String, Object>> sites, boolean exhaustive) {}
/** Raw DEX invokes within one JADX using method; offsets are code units from its code start. */
private static DirectInvokes directInvokes(JavaMethod source, MethodInfo target, InvokeBudget budget) {
MethodNode node = source.getMethodNode();
ICodeReader code = node.getCodeReader();
if (code == null) return null;
List<Map<String, Object>> sites = new ArrayList<>();
boolean[] exhaustive = {true};
code.visitInstructions(insn -> {
insn.decode();
if (insn.getIndexType() != InsnIndexType.METHOD_REF) return;
Opcode opcode = insn.getOpcode();
if (!opcode.name().startsWith("INVOKE_")) return;
IMethodRef ref = insn.getIndexAsMethod();
ref.load();
if (!MethodInfo.fromRef(node.root(), ref).getRawFullId().equals(target.getRawFullId())) return;
Map<String, Object> site = Map.of(
"dex_entry", node.getInputFileName(),
"source_raw_method_id", node.getMethodInfo().getRawFullId(),
"dex_code_unit_offset", insn.getOffset(),
"dex_method_index", insn.getIndex(),
"opcode", opcode.name());
if (budget.admit(site)) sites.add(site);
else exhaustive[0] = false;
});
return new DirectInvokes(sites, exhaustive[0]);
}
private CallToolResult handle(String name, JsonObject args) {
if (name.equals("rea_jvm_status")) return json(Map.of(
"engine_reported_version", BuildInfo.VERSION,
@@ -173,17 +236,52 @@ class ReaJadxBridge {
}
List<JavaNode> uses;
String target = string(args, "class_name");
MethodSnapshot selectedMethod = null;
int overloadIndex = 0;
if (name.equals("get_xrefs_to_class")) uses = cls.node().getUseIn();
else {
List<MethodSnapshot> overloads = methods(cls, args);
if (overloads.size() != 1) throw new InputFailure("method_name", "Method references require a unique overload");
target += "." + overloads.get(0).name();
uses = method(cls, overloads.get(0)).getUseIn();
if (overloads.size() != 1 && !args.containsKey("overload_index"))
throw new InputFailure("overload_index", "Select one of " + overloads.size() + " method overloads");
overloadIndex = integer(args, "overload_index", 0);
if (overloadIndex >= overloads.size())
throw new InputFailure("overload_index", "overload_index out of range: " + overloadIndex);
selectedMethod = overloads.get(overloadIndex);
target += "." + selectedMethod.name();
uses = method(cls, selectedMethod).getUseIn();
}
// REA consumes all references without materializing source line information.
List<Map<String, Object>> items = uses.stream().map(node -> current.describeUsage(node, false)).toList();
return json(Map.of("target", target, "total", uses.size(), "count", items.size(),
"resolve_line", false, "items", items));
MethodInfo targetInfo = selectedMethod == null ? null : method(cls, selectedMethod).getMethodNode().getMethodInfo();
List<Map<String, Object>> items = uses.stream().map(node -> {
Map<String, Object> item = new LinkedHashMap<>(current.describeUsage(node, false));
if (targetInfo != null && node instanceof JavaMethod) {
item.put("direct_invokes", List.of());
item.put("direct_invokes_exhaustive", false);
}
return item;
}).toList();
Map<String, Object> result = new LinkedHashMap<>(Map.of("target", target,
"total", uses.size(), "count", items.size(), "resolve_line", false, "items", items));
if (selectedMethod != null) {
result.put("overload_index", overloadIndex);
result.put("reported_signature", selectedMethod.summary().get("signature"));
}
if (targetInfo != null) {
InvokeBudget budget = new InvokeBudget(MAX_XREF_TEXT_BYTES - escapedJsonBytes(result));
for (int index = 0; index < uses.size(); index++) {
if (!(uses.get(index) instanceof JavaMethod source)) continue;
DirectInvokes invokes = directInvokes(source, targetInfo, budget);
Map<String, Object> item = items.get(index);
if (invokes == null) {
item.remove("direct_invokes");
item.remove("direct_invokes_exhaustive");
} else {
item.put("direct_invokes", invokes.sites());
item.put("direct_invokes_exhaustive", invokes.exhaustive());
}
}
}
return json(result);
}
public static void main(String[] args) throws Exception {
@@ -217,7 +315,7 @@ class ReaJadxBridge {
List<String> fields = switch (name) {
case "list_classes" -> List.of("offset", "limit");
case "get_method_body" -> List.of("class_name", "method_name", "overload_index");
case "get_xrefs_to_method" -> List.of("class_name", "method_name");
case "get_xrefs_to_method" -> List.of("class_name", "method_name", "overload_index");
default -> List.of("class_name");
};
Map<String, kotlinx.serialization.json.JsonElement> properties = new LinkedHashMap<>();
@@ -226,7 +324,7 @@ class ReaJadxBridge {
case "list_classes" -> "Indexed pages of all top-level class names, with offset and limit.";
case "get_class_summary" -> "Parsed member metadata for one exact class name; no source generation.";
case "get_method_body" -> "Source for an exact class and stable same-name overload index; static initializers use smali to preserve folded field initialization.";
default -> "Complete static references to an exact class or unique method; source lines are unresolved.";
default -> "Complete static using-node inventory for an exact class or selected method overload; one node may contain multiple invoke instructions, and source lines are unresolved.";
};
Tool narrowed = new Tool(name, new ToolSchema(null, new JsonObject(properties),
tool.getInputSchema().getRequired(), null), description, null, null, null, null, null, null);
+816
View File
@@ -0,0 +1,816 @@
"""REA's experimental local bridge plugin for Cutter.
Install this file in Cutter's user plugin directory. The bridge is REA-owned
IPC, not an upstream Cutter transport. It binds to loopback and requires a
per-process random token stored in a user-only descriptor file.
"""
from __future__ import annotations
import json
import hashlib
import hmac
import os
import secrets
import socket
import stat
import tempfile
import threading
import time
import uuid
from pathlib import Path
from typing import Any
if os.name == "nt":
import ctypes
from ctypes import wintypes
import cutter
try:
from PySide6.QtCore import QObject, Signal
except ImportError:
from PySide2.QtCore import QObject, Signal
MAX_FRAME_BYTES = 16 * 1024 * 1024
SOCKET_TIMEOUT_SECONDS = 20
AUTH_FRAME_TIMEOUT_SECONDS = 1
MAX_DESCRIPTOR_BYTES = 64 * 1024
MAX_COMPLETION_RECEIPTS = 64
def _windows_api() -> tuple[Any, Any]:
kernel32 = ctypes.WinDLL("kernel32", use_last_error=True)
advapi32 = ctypes.WinDLL("advapi32", use_last_error=True)
kernel32.GetCurrentProcess.restype = wintypes.HANDLE
kernel32.LocalFree.argtypes = [wintypes.HLOCAL]
kernel32.LocalFree.restype = wintypes.HLOCAL
kernel32.CloseHandle.argtypes = [wintypes.HANDLE]
kernel32.CloseHandle.restype = wintypes.BOOL
kernel32.GetFileAttributesW.argtypes = [wintypes.LPCWSTR]
kernel32.GetFileAttributesW.restype = wintypes.DWORD
kernel32.CreateDirectoryW.argtypes = [wintypes.LPCWSTR, wintypes.LPVOID]
kernel32.CreateDirectoryW.restype = wintypes.BOOL
kernel32.CreateFileW.argtypes = [wintypes.LPCWSTR, wintypes.DWORD, wintypes.DWORD,
wintypes.LPVOID, wintypes.DWORD, wintypes.DWORD, wintypes.HANDLE]
kernel32.CreateFileW.restype = wintypes.HANDLE
kernel32.WriteFile.argtypes = [wintypes.HANDLE, wintypes.LPVOID, wintypes.DWORD,
ctypes.POINTER(wintypes.DWORD), wintypes.LPVOID]
kernel32.WriteFile.restype = wintypes.BOOL
kernel32.FlushFileBuffers.argtypes = [wintypes.HANDLE]
kernel32.FlushFileBuffers.restype = wintypes.BOOL
kernel32.MoveFileExW.argtypes = [wintypes.LPCWSTR, wintypes.LPCWSTR, wintypes.DWORD]
kernel32.MoveFileExW.restype = wintypes.BOOL
kernel32.DeleteFileW.argtypes = [wintypes.LPCWSTR]
kernel32.DeleteFileW.restype = wintypes.BOOL
advapi32.OpenProcessToken.argtypes = [wintypes.HANDLE, wintypes.DWORD, ctypes.POINTER(wintypes.HANDLE)]
advapi32.OpenProcessToken.restype = wintypes.BOOL
advapi32.GetTokenInformation.argtypes = [wintypes.HANDLE, ctypes.c_int, wintypes.LPVOID,
wintypes.DWORD, ctypes.POINTER(wintypes.DWORD)]
advapi32.GetTokenInformation.restype = wintypes.BOOL
advapi32.ConvertSidToStringSidW.argtypes = [wintypes.LPVOID, ctypes.POINTER(wintypes.LPWSTR)]
advapi32.ConvertSidToStringSidW.restype = wintypes.BOOL
advapi32.GetNamedSecurityInfoW.argtypes = [wintypes.LPWSTR, ctypes.c_int, wintypes.DWORD,
ctypes.POINTER(wintypes.LPVOID), ctypes.POINTER(wintypes.LPVOID),
ctypes.POINTER(wintypes.LPVOID), ctypes.POINTER(wintypes.LPVOID),
ctypes.POINTER(wintypes.LPVOID)]
advapi32.GetNamedSecurityInfoW.restype = wintypes.DWORD
advapi32.GetSecurityDescriptorControl.argtypes = [wintypes.LPVOID, ctypes.POINTER(wintypes.WORD),
ctypes.POINTER(wintypes.DWORD)]
advapi32.GetSecurityDescriptorControl.restype = wintypes.BOOL
advapi32.EqualSid.argtypes = [wintypes.LPVOID, wintypes.LPVOID]
advapi32.EqualSid.restype = wintypes.BOOL
advapi32.GetAclInformation.argtypes = [wintypes.LPVOID, wintypes.LPVOID, wintypes.DWORD, ctypes.c_int]
advapi32.GetAclInformation.restype = wintypes.BOOL
advapi32.GetAce.argtypes = [wintypes.LPVOID, wintypes.DWORD, ctypes.POINTER(wintypes.LPVOID)]
advapi32.GetAce.restype = wintypes.BOOL
advapi32.IsValidSid.argtypes = [wintypes.LPVOID]
advapi32.IsValidSid.restype = wintypes.BOOL
advapi32.GetLengthSid.argtypes = [wintypes.LPVOID]
advapi32.GetLengthSid.restype = wintypes.DWORD
advapi32.ConvertStringSecurityDescriptorToSecurityDescriptorW.argtypes = [
wintypes.LPCWSTR, wintypes.DWORD, ctypes.POINTER(wintypes.LPVOID), ctypes.POINTER(wintypes.DWORD),
]
advapi32.ConvertStringSecurityDescriptorToSecurityDescriptorW.restype = wintypes.BOOL
return kernel32, advapi32
def _windows_user_sid_text() -> str:
kernel32, advapi32 = _windows_api()
token = wintypes.HANDLE()
if not advapi32.OpenProcessToken(kernel32.GetCurrentProcess(), 0x0008, ctypes.byref(token)):
raise ctypes.WinError(ctypes.get_last_error())
try:
required = wintypes.DWORD()
advapi32.GetTokenInformation(token, 1, None, 0, ctypes.byref(required))
if required.value == 0:
raise ctypes.WinError(ctypes.get_last_error())
buffer = ctypes.create_string_buffer(required.value)
if not advapi32.GetTokenInformation(token, 1, buffer, required, ctypes.byref(required)):
raise ctypes.WinError(ctypes.get_last_error())
class SID_AND_ATTRIBUTES(ctypes.Structure):
_fields_ = [("Sid", wintypes.LPVOID), ("Attributes", wintypes.DWORD)]
class TOKEN_USER(ctypes.Structure):
_fields_ = [("User", SID_AND_ATTRIBUTES)]
sid = ctypes.cast(buffer, ctypes.POINTER(TOKEN_USER)).contents.User.Sid
text = wintypes.LPWSTR()
if not advapi32.ConvertSidToStringSidW(sid, ctypes.byref(text)):
raise ctypes.WinError(ctypes.get_last_error())
try:
return text.value
finally:
kernel32.LocalFree(text)
finally:
kernel32.CloseHandle(token)
def _verify_windows_private_path(path: Path, directory: bool) -> None:
kernel32, advapi32 = _windows_api()
attributes = kernel32.GetFileAttributesW(str(path))
if attributes == 0xFFFFFFFF:
raise ctypes.WinError(ctypes.get_last_error())
if attributes & 0x400 or bool(attributes & 0x10) != directory:
raise RuntimeError("Cutter bridge path is a reparse point or has the wrong object type")
class ACL_SIZE_INFORMATION(ctypes.Structure):
_fields_ = [("AceCount", wintypes.DWORD), ("AclBytesInUse", wintypes.DWORD), ("AclBytesFree", wintypes.DWORD)]
class ACE_HEADER(ctypes.Structure):
_fields_ = [("AceType", wintypes.BYTE), ("AceFlags", wintypes.BYTE), ("AceSize", wintypes.WORD)]
class SID_HEADER(ctypes.Structure):
_fields_ = [("Revision", wintypes.BYTE), ("SubAuthorityCount", wintypes.BYTE),
("IdentifierAuthority", wintypes.BYTE * 6)]
class ACCESS_ALLOWED_ACE(ctypes.Structure):
_fields_ = [("Header", ACE_HEADER), ("Mask", wintypes.DWORD), ("SidStart", wintypes.DWORD)]
owner = wintypes.LPVOID()
dacl = wintypes.LPVOID()
descriptor = wintypes.LPVOID()
result = advapi32.GetNamedSecurityInfoW(
str(path), 1, 0x1 | 0x4, ctypes.byref(owner), None, ctypes.byref(dacl), None, ctypes.byref(descriptor),
)
if result != 0:
raise ctypes.WinError(result)
try:
control = wintypes.WORD()
revision = wintypes.DWORD()
if not advapi32.GetSecurityDescriptorControl(descriptor, ctypes.byref(control), ctypes.byref(revision)):
raise ctypes.WinError(ctypes.get_last_error())
if not control.value & 0x1000 or not dacl:
raise RuntimeError("Cutter bridge ACL must be present and protected from inheritance")
token = wintypes.HANDLE()
if not advapi32.OpenProcessToken(kernel32.GetCurrentProcess(), 0x0008, ctypes.byref(token)):
raise ctypes.WinError(ctypes.get_last_error())
try:
required = wintypes.DWORD()
advapi32.GetTokenInformation(token, 1, None, 0, ctypes.byref(required))
token_user = ctypes.create_string_buffer(required.value)
if not advapi32.GetTokenInformation(token, 1, token_user, required, ctypes.byref(required)):
raise ctypes.WinError(ctypes.get_last_error())
class SID_AND_ATTRIBUTES(ctypes.Structure):
_fields_ = [("Sid", wintypes.LPVOID), ("Attributes", wintypes.DWORD)]
class TOKEN_USER(ctypes.Structure):
_fields_ = [("User", SID_AND_ATTRIBUTES)]
current_sid = ctypes.cast(token_user, ctypes.POINTER(TOKEN_USER)).contents.User.Sid
if not advapi32.EqualSid(owner, current_sid):
raise RuntimeError("Cutter bridge path owner does not match the current user")
finally:
kernel32.CloseHandle(token)
info = ACL_SIZE_INFORMATION()
if not advapi32.GetAclInformation(dacl, ctypes.byref(info), ctypes.sizeof(info), 2) or info.AceCount != 2:
raise RuntimeError("Cutter bridge ACL must allow only the current user and SYSTEM")
expected_user = _windows_user_sid_text()
user_allowed = False
system_allowed = False
for index in range(info.AceCount):
ace_pointer = wintypes.LPVOID()
if not advapi32.GetAce(dacl, index, ctypes.byref(ace_pointer)):
raise ctypes.WinError(ctypes.get_last_error())
header = ctypes.cast(ace_pointer, ctypes.POINTER(ACE_HEADER)).contents
if header.AceType != 0 or header.AceSize < ctypes.sizeof(ACE_HEADER) + ctypes.sizeof(wintypes.DWORD) + ctypes.sizeof(SID_HEADER):
raise RuntimeError("Cutter bridge ACL contains a malformed or unsupported ACE")
ace_address = ctypes.cast(ace_pointer, ctypes.c_void_p).value
if ace_address is None:
raise RuntimeError("Cutter bridge ACL returned a null ACE")
sid_pointer = ctypes.c_void_p(ace_address + ctypes.sizeof(ACE_HEADER) + ctypes.sizeof(wintypes.DWORD))
sid_header = ctypes.cast(sid_pointer, ctypes.POINTER(SID_HEADER)).contents
sid_size = ctypes.sizeof(SID_HEADER) + sid_header.SubAuthorityCount * ctypes.sizeof(wintypes.DWORD)
if sid_size > header.AceSize - ctypes.sizeof(ACE_HEADER) - ctypes.sizeof(wintypes.DWORD) or not advapi32.IsValidSid(sid_pointer) or advapi32.GetLengthSid(sid_pointer) != sid_size:
raise RuntimeError("Cutter bridge ACL contains an invalid SID")
ace = ctypes.cast(ace_pointer, ctypes.POINTER(ACCESS_ALLOWED_ACE)).contents
sid_text = wintypes.LPWSTR()
if ace.Mask != 0x001F01FF or ace.Header.AceFlags != 0x03:
raise RuntimeError("Cutter bridge ACL contains an unsupported ACE")
if not advapi32.ConvertSidToStringSidW(sid_pointer, ctypes.byref(sid_text)):
raise ctypes.WinError(ctypes.get_last_error())
try:
if sid_text.value == expected_user:
user_allowed = True
elif sid_text.value == "S-1-5-18":
system_allowed = True
else:
raise RuntimeError("Cutter bridge ACL allows an unrelated principal")
finally:
kernel32.LocalFree(sid_text)
if not user_allowed or not system_allowed:
raise RuntimeError("Cutter bridge ACL must allow only the current user and SYSTEM")
finally:
kernel32.LocalFree(descriptor)
def _windows_security_attributes() -> tuple[Any, Any]:
kernel32, advapi32 = _windows_api()
sid = _windows_user_sid_text()
sddl = f"O:{sid}G:{sid}D:P(A;OICI;FA;;;{sid})(A;OICI;FA;;;SY)"
descriptor = wintypes.LPVOID()
if not advapi32.ConvertStringSecurityDescriptorToSecurityDescriptorW(sddl, 1, ctypes.byref(descriptor), None):
raise ctypes.WinError(ctypes.get_last_error())
class SECURITY_ATTRIBUTES(ctypes.Structure):
_fields_ = [("nLength", wintypes.DWORD), ("lpSecurityDescriptor", wintypes.LPVOID), ("bInheritHandle", wintypes.BOOL)]
attributes = SECURITY_ATTRIBUTES(ctypes.sizeof(SECURITY_ATTRIBUTES), descriptor, False)
return kernel32, (descriptor, attributes)
def _ensure_windows_bridge_directory(directory: Path) -> None:
directory.parent.mkdir(parents=True, exist_ok=True)
if not directory.exists():
kernel32, (descriptor, attributes) = _windows_security_attributes()
try:
if not kernel32.CreateDirectoryW(str(directory), ctypes.byref(attributes)):
error = ctypes.get_last_error()
if error != 183:
raise ctypes.WinError(error)
finally:
kernel32.LocalFree(descriptor)
_verify_windows_private_path(directory, True)
def _ensure_posix_bridge_directory(directory: Path) -> None:
directory.mkdir(mode=0o700, parents=True, exist_ok=True)
if not hasattr(os, "O_NOFOLLOW") or not hasattr(os, "O_DIRECTORY"):
raise RuntimeError("Cutter bridge directory requires no-follow directory handles")
descriptor = os.open(directory, os.O_RDONLY | os.O_DIRECTORY | os.O_NOFOLLOW)
try:
directory_info = os.fstat(descriptor)
if directory_info.st_uid != os.getuid():
raise RuntimeError("Cutter bridge directory must be owned by the current user")
os.fchmod(descriptor, 0o700)
directory_info = os.fstat(descriptor)
if stat.S_IMODE(directory_info.st_mode) != 0o700:
raise RuntimeError("Cutter bridge directory must have mode 0700")
finally:
os.close(descriptor)
def _write_windows_descriptor(directory: Path, destination: Path, payload: bytes) -> None:
if len(payload) > MAX_DESCRIPTOR_BYTES:
raise RuntimeError("Cutter bridge descriptor exceeds its byte limit")
kernel32, (security_descriptor, attributes) = _windows_security_attributes()
temporary = directory / f".{destination.name}.{uuid.uuid4().hex}.tmp"
handle = wintypes.HANDLE()
try:
handle = kernel32.CreateFileW(str(temporary), 0x40000000 | 0x00020000 | 0x00040000,
0x1 | 0x2 | 0x4, ctypes.byref(attributes), 1, 0x80, None)
if handle == wintypes.HANDLE(-1).value:
raise ctypes.WinError(ctypes.get_last_error())
_verify_windows_private_path(temporary, False)
if payload:
data = ctypes.create_string_buffer(payload)
written = wintypes.DWORD()
if not kernel32.WriteFile(handle, data, len(payload), ctypes.byref(written), None) or written.value != len(payload):
raise ctypes.WinError(ctypes.get_last_error())
if not kernel32.FlushFileBuffers(handle):
raise ctypes.WinError(ctypes.get_last_error())
kernel32.CloseHandle(handle)
handle = wintypes.HANDLE()
if not kernel32.MoveFileExW(str(temporary), str(destination), 0x1 | 0x8):
raise ctypes.WinError(ctypes.get_last_error())
finally:
if handle and handle != wintypes.HANDLE(-1).value:
kernel32.CloseHandle(handle)
kernel32.DeleteFileW(str(temporary))
kernel32.LocalFree(security_descriptor)
def _bridge_directory() -> Path:
configured = os.environ.get("REA_CUTTER_BRIDGE_DIR")
if configured:
return Path(configured).expanduser()
local_app_data = os.environ.get("LOCALAPPDATA")
if os.name == "nt" and local_app_data:
return Path(local_app_data) / "REA" / "CutterBridge"
return Path.home() / ".cache" / "rea" / "cutter-bridge"
class _RequestDispatcher(QObject):
dispatch = Signal(object, object)
def __init__(self) -> None:
super().__init__()
self.dispatch.connect(self._handle)
self.generation = 0
self.completed_commands: dict[str, str] = {}
self._receipt_lock = threading.Lock()
self.current_file = self._read_current_file()
try:
self.cutter_version = str(cutter.core().getVersionInformation())
except Exception:
self.cutter_version = None
@staticmethod
def _read_current_file() -> str | None:
try:
value = cutter.core().currentlyOpenFile()
return str(value) if value else None
except Exception:
return None
def _handle(self, request: dict[str, Any], reply: dict[str, Any]) -> None:
kind = request.get("kind")
request_id = request.get("request_id")
if kind in ("command", "ack"):
try:
valid_request_id = (
isinstance(request_id, str)
and str(uuid.UUID(request_id)) == request_id.lower()
)
except (ValueError, AttributeError):
valid_request_id = False
if not valid_request_id:
reply.update({
"ok": False,
"error": "invalid-command-request-id",
"execution_state": "not_started",
})
request["_event"].set()
return
if kind == "ack":
with self._receipt_lock:
acknowledged = self.completed_commands.pop(request_id, None) is not None
reply.update({"ok": True, "acknowledged": acknowledged})
request["_event"].set()
return
if request["_cancelled"].is_set():
reply.update({
"ok": False,
"error": "request-cancelled-before-execution",
"execution_state": "not_started",
})
if kind == "command":
self._record_command_completion(request_id, "not_started")
request["_event"].set()
return
self._refresh_document_identity()
if kind == "status":
with self._receipt_lock:
completed_commands = [
{"request_id": receipt_id, "execution_state": state}
for receipt_id, state in self.completed_commands.items()
]
reply.update({
"ok": True,
"session_id": request.get("session_id"),
"current_file": self.current_file,
"document_generation": self.generation,
"cutter_version": self.cutter_version,
"identity_status": "partial",
"completed_commands": completed_commands,
})
request["_event"].set()
return
if kind != "command":
reply.update({"ok": False, "error": "unsupported-request-kind"})
request["_event"].set()
return
if request.get("expected_generation") != self.generation:
reply.update({
"ok": False,
"error": "active-document-generation-changed",
"execution_state": "not_started",
})
self._record_command_completion(request_id, "not_started")
request["_event"].set()
return
command = request.get("command")
if not isinstance(command, str) or not command.strip():
reply.update({
"ok": False,
"error": "invalid-command",
"execution_state": "not_started",
})
self._record_command_completion(request_id, "not_started")
request["_event"].set()
return
reply["execution_state"] = "running"
try:
output = cutter.cmdj(command) if request.get("json") is True else cutter.cmd(command)
command_error = None
except Exception as error:
output = None
command_error = str(error)
self._refresh_document_identity()
if command_error is None:
reply.update({
"ok": True,
"output": output,
"current_file": self.current_file,
"document_generation": self.generation,
"cutter_version": self.cutter_version,
"identity_status": "partial",
"execution_state": "complete",
})
self._record_command_completion(request_id, "complete")
else:
reply.update({
"ok": False,
"error": command_error,
"current_file": self.current_file,
"document_generation": self.generation,
"cutter_version": self.cutter_version,
"identity_status": "partial",
"execution_state": "unknown",
"message": "The command raised an exception; it may have produced partial effects. Do not retry automatically.",
})
self._record_command_completion(request_id, "failed")
request["_event"].set()
def _record_command_completion(
self, request_id: str, execution_state: str
) -> None:
with self._receipt_lock:
if request_id in self.completed_commands or len(self.completed_commands) < MAX_COMPLETION_RECEIPTS:
self.completed_commands[request_id] = execution_state
def can_accept_command(self) -> bool:
with self._receipt_lock:
return len(self.completed_commands) < MAX_COMPLETION_RECEIPTS
def _refresh_document_identity(self) -> None:
current_file = self._read_current_file()
if current_file != self.current_file:
self.current_file = current_file
self.generation += 1
class _BridgeServer(threading.Thread):
def __init__(self, dispatcher: _RequestDispatcher) -> None:
super().__init__(name="rea-cutter-bridge", daemon=True)
self.dispatcher = dispatcher
self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.server.bind(("127.0.0.1", 0))
self.server.listen(8)
self.server.settimeout(0.5)
self.port = self.server.getsockname()[1]
self.session_id = str(uuid.uuid4())
self.token = secrets.token_urlsafe(32)
self.poisoned = False
self.connection_ordinal = 0
self.directory = _bridge_directory()
self.descriptor = self.directory / f"cutter-{os.getpid()}-{self.session_id}.json"
def publish(self) -> None:
if os.name == "nt":
_ensure_windows_bridge_directory(self.directory)
else:
_ensure_posix_bridge_directory(self.directory)
if os.name == "nt" and (self.directory.is_symlink() or not self.directory.is_dir()):
raise RuntimeError("Cutter bridge directory must be a real directory")
payload = {
"session_id": self.session_id,
"pid": os.getpid(),
"host": "127.0.0.1",
"port": self.port,
"token": self.token,
"document_generation": self.dispatcher.generation,
"current_file": self.dispatcher.current_file,
"cutter_version": self.dispatcher.cutter_version,
"identity_status": "partial",
}
if os.name == "nt":
_write_windows_descriptor(self.directory, self.descriptor, json.dumps(payload).encode("utf-8"))
return
descriptor, temporary_path = tempfile.mkstemp(prefix=f".{self.descriptor.name}.", dir=self.directory)
try:
os.fchmod(descriptor, 0o600)
descriptor_info = os.fstat(descriptor)
if descriptor_info.st_uid != os.getuid() or stat.S_IMODE(descriptor_info.st_mode) & 0o077:
raise RuntimeError("Cutter bridge descriptor must be owned by the current user with mode 0600")
with os.fdopen(descriptor, "w", encoding="utf-8") as stream:
stream.write(json.dumps(payload))
stream.flush()
os.fsync(stream.fileno())
os.replace(temporary_path, self.descriptor)
except Exception:
try:
os.close(descriptor)
except OSError:
pass
try:
os.unlink(temporary_path)
except OSError:
pass
raise
def run(self) -> None:
try:
self.publish()
except Exception:
self.server.close()
return
while True:
try:
connection, _ = self.server.accept()
except socket.timeout:
if getattr(self, "stopping", False):
break
continue
except OSError:
break
self.connection_ordinal += 1
connection_ordinal = self.connection_ordinal
with connection:
connection.settimeout(AUTH_FRAME_TIMEOUT_SECONDS)
authenticated = False
try:
hello = self._receive_frame(connection)
if not isinstance(hello, dict) or hello.get("kind") != "rea-auth-v2" or not isinstance(hello.get("nonce"), str) or len(hello["nonce"]) != 64:
self._send(connection, {"ok": False, "error": "authentication-failed"})
continue
client_nonce = hello["nonce"]
server_nonce = f"{connection_ordinal:016x}{secrets.token_hex(24)}"
server_proof = hmac.new(self.token.encode(), f"rea-cutter-server-v2:{self.session_id}:{client_nonce}:{server_nonce}".encode(), hashlib.sha256).hexdigest()
self._send(connection, {"session_id": self.session_id, "nonce": server_nonce, "proof": server_proof})
authenticated_frame = self._receive_frame(connection)
request_bytes = authenticated_frame.get("request") if isinstance(authenticated_frame, dict) else None
expected_proof = hmac.new(self.token.encode(), f"rea-cutter-client-v2:{self.session_id}:{client_nonce}:{server_nonce}:{request_bytes}".encode(), hashlib.sha256).hexdigest()
if not isinstance(request_bytes, str) or not isinstance(authenticated_frame.get("proof"), str) or not hmac.compare_digest(authenticated_frame["proof"], expected_proof):
self._send(connection, {"ok": False, "error": "authentication-failed"})
continue
request = json.loads(request_bytes)
if not isinstance(request, dict) or request.get("session_id") != self.session_id:
self._send(connection, {"ok": False, "error": "authentication-failed"})
continue
authenticated = True
connection.settimeout(SOCKET_TIMEOUT_SECONDS)
if self.poisoned and request.get("kind") not in ("status", "ack"):
self._send_authenticated(connection, {
"ok": False,
"error": "session-poisoned-after-ui-timeout",
"execution_state": "not_started",
}, client_nonce, server_nonce)
continue
if (
request.get("kind") == "command"
and not self.dispatcher.can_accept_command()
):
self._send_authenticated(connection, {
"ok": False,
"error": "completion-receipt-capacity-exceeded",
"execution_state": "not_started",
}, client_nonce, server_nonce)
continue
request["session_id"] = self.session_id
event = threading.Event()
cancelled = threading.Event()
reply: dict[str, Any] = {}
request["_event"] = event
request["_cancelled"] = cancelled
self.dispatcher.dispatch.emit(request, reply)
if not event.wait(SOCKET_TIMEOUT_SECONDS):
cancelled.set()
self.poisoned = True
self._send_authenticated(connection, {
"ok": False,
"error": "ui-thread-request-timeout",
"execution_state": "unknown",
"message": "The UI operation may still complete after this timeout; restart Cutter before issuing further commands.",
}, client_nonce, server_nonce)
else:
if request.get("kind") == "status" and reply.get("ok") is True:
reply["completed_connection"] = f"{connection_ordinal - 1:016x}"
self._send_authenticated(connection, reply, client_nonce, server_nonce)
except socket.timeout:
if authenticated:
self._send_authenticated(connection, {
"ok": False,
"error": "request-timeout",
"execution_state": "unknown",
"message": "The command may have produced partial or persistent effects. Do not retry automatically.",
}, client_nonce, server_nonce)
except Exception as error:
self._send(connection, {"ok": False, "error": str(error)})
self.server.close()
try:
self.descriptor.unlink(missing_ok=True)
except OSError:
pass
@staticmethod
def _receive_frame(connection: socket.socket) -> dict[str, Any]:
data = bytearray()
deadline = time.monotonic() + AUTH_FRAME_TIMEOUT_SECONDS
while b"\n" not in data:
if len(data) >= MAX_FRAME_BYTES:
raise ValueError("frame-limit-exceeded")
remaining = deadline - time.monotonic()
if remaining <= 0:
raise socket.timeout("authentication frame timed out")
connection.settimeout(remaining)
part = connection.recv(min(65536, MAX_FRAME_BYTES + 1 - len(data)))
if not part:
raise ValueError("incomplete authentication frame")
data.extend(part)
line, remainder = bytes(data).split(b"\n", 1)
if len(line) > MAX_FRAME_BYTES or remainder:
raise ValueError("invalid framed request")
value = json.loads(line)
return value if isinstance(value, dict) else {}
def _send_authenticated(self, connection: socket.socket, value: dict[str, Any], client_nonce: str, server_nonce: str) -> None:
encoded = self._encode_authenticated_response(value, client_nonce, server_nonce)
if encoded is None:
value = self._truncated_response(value)
encoded = self._encode_authenticated_response(value, client_nonce, server_nonce)
if encoded is None:
# This fixed-size fallback is checked through the same signed framing path.
value = {
"ok": False,
"error": "response-limit-exceeded",
"execution_state": value.get("execution_state", "unknown"),
"output": None,
"output_truncated": True,
"message": "The result exceeded the bridge frame limit; command completion state is preserved.",
}
encoded = self._encode_authenticated_response(value, client_nonce, server_nonce)
if encoded is None:
return
try:
connection.sendall(encoded)
except OSError:
pass
def _encode_authenticated_response(self, value: dict[str, Any], client_nonce: str, server_nonce: str) -> bytes | None:
if not _within_response_limit(value):
return None
prefix = b'{"response":"'
suffix = b'","proof":"' + (b"0" * 64) + b'"}\n'
frame_size = len(prefix) + len(suffix)
response_parts: list[bytes] = []
escaped_parts: list[bytes] = []
for fragment in json.JSONEncoder(ensure_ascii=False, separators=(",", ":")).iterencode(value):
raw = fragment.encode("utf-8")
escaped = json.dumps(fragment, ensure_ascii=False)[1:-1].encode("utf-8")
frame_size += len(escaped)
if frame_size > MAX_FRAME_BYTES:
return None
response_parts.append(raw)
escaped_parts.append(escaped)
proof = hmac.new(self.token.encode(), digestmod=hashlib.sha256)
proof.update(f"rea-cutter-response-v2:{self.session_id}:{client_nonce}:{server_nonce}:".encode())
for part in response_parts:
proof.update(part)
return prefix + b"".join(escaped_parts) + b'","proof":"' + proof.hexdigest().encode() + b'"}\n'
@staticmethod
def _truncated_response(value: dict[str, Any]) -> dict[str, Any]:
response: dict[str, Any] = {
"ok": value.get("ok") is True,
"output": None,
"execution_state": value.get("execution_state", "unknown"),
"output_truncated": True,
"message": "The command result exceeded the bridge frame limit; completion state is preserved and output was omitted.",
}
for key in ("current_file", "document_generation", "cutter_version", "identity_status"):
if key in value:
response[key] = value[key]
if value.get("ok") is not True and isinstance(value.get("error"), str):
response["error"] = value["error"]
return response
@staticmethod
def _send(connection: socket.socket, value: dict[str, Any]) -> None:
if not _within_response_limit(value):
value = {
"ok": False,
"error": "response-limit-exceeded",
"execution_state": value.get("execution_state", "unknown"),
"output_truncated": True,
"message": "The command completed or may have completed, but its result exceeded the bridge response limit. Do not retry automatically.",
}
parts: list[bytes] = []
encoded_size = 0
for part in json.JSONEncoder(ensure_ascii=False).iterencode(value):
encoded_part = part.encode("utf-8")
encoded_size += len(encoded_part)
if encoded_size > MAX_FRAME_BYTES:
fallback = {
"ok": False,
"error": "response-limit-exceeded",
"execution_state": value.get("execution_state", "unknown"),
"output_truncated": True,
"message": "The command completed or may have completed, but its result exceeded the bridge response limit. Do not retry automatically.",
}
parts = [json.dumps(fallback, separators=(",", ":")).encode("utf-8")]
break
parts.append(encoded_part)
encoded = b"".join(parts)
try:
connection.sendall(encoded + b"\n")
except OSError:
# A disconnected caller must not terminate the bridge thread or
# prevent later commands from being served.
pass
def stop(self) -> None:
self.stopping = True
try:
self.server.close()
except OSError:
pass
class ReaCutterPlugin(cutter.CutterPlugin):
name = "REA Local Bridge"
description = "Local REA analysis bridge for the active Cutter session"
version = "1.0"
author = "REA"
def __init__(self) -> None:
super().__init__()
self._dispatcher: _RequestDispatcher | None = None
self._bridge: _BridgeServer | None = None
def setupPlugin(self) -> None:
return
def setupInterface(self, main: Any) -> None:
self._dispatcher = _RequestDispatcher()
self._bridge = _BridgeServer(self._dispatcher)
self._bridge.start()
def terminate(self) -> None:
if self._bridge is not None:
self._bridge.stop()
self._bridge.join(timeout=2)
self._bridge = None
self._dispatcher = None
def create_cutter_plugin() -> ReaCutterPlugin:
return ReaCutterPlugin()
def _within_response_limit(value: Any) -> bool:
pending = [value]
seen: set[int] = set()
size = 0
while pending:
current = pending.pop()
if isinstance(current, str):
size += 2
if current.isascii():
size += len(current)
size += current.count('"') + current.count("\\")
size += sum(5 for character in current if ord(character) < 0x20)
else:
size += 4 * len(current)
size += current.count('"') + current.count("\\")
size += sum(5 for character in current if ord(character) < 0x20)
elif isinstance(current, dict):
identity = id(current)
if identity in seen:
return False
seen.add(identity)
size += 2 + 4 * len(current)
if size > MAX_FRAME_BYTES:
return False
pending.extend(current.keys())
pending.extend(current.values())
elif isinstance(current, (list, tuple)):
identity = id(current)
if identity in seen:
return False
seen.add(identity)
size += 2 + 2 * len(current)
if size > MAX_FRAME_BYTES:
return False
pending.extend(current)
else:
size += 16
if size > MAX_FRAME_BYTES:
return False
return True
File diff suppressed because it is too large. Load diff
+15 -2
View File
@@ -1,6 +1,8 @@
import ghidra.app.script.GhidraScript;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.MemoryBlock;
import ghidra.program.model.mem.MemoryBlockSourceInfo;
/** Seed the caller-supplied raw-binary entry before default auto-analysis. */
public final class ReaGhidraPrepareRaw extends GhidraScript {
@@ -21,8 +23,19 @@ public final class ReaGhidraPrepareRaw extends GhidraScript {
);
}
Address entry = currentProgram.getAddressFactory().getDefaultAddressSpace().getAddress(entryHex);
Address start = currentProgram.getMinAddress();
if (entry == null || !entry.equals(start)) {
MemoryBlock block = entry == null ? null : currentProgram.getMemory().getBlock(entry);
boolean fileStart = false;
if (block != null) {
// Processor-defined zero page/stack blocks can precede the imported file.
for (MemoryBlockSourceInfo source : block.getSourceInfos()) {
if (source.getFileBytes().isPresent() && source.getFileBytesOffset() == 0 &&
entry.equals(source.getMinAddress())) {
fileStart = true;
break;
}
}
}
if (!fileStart) {
throw new IllegalArgumentException(
"Raw binary import must map file offset 0 at the caller-supplied entry"
);
+23 -4
View File
@@ -644,7 +644,7 @@ def _analyze_function(document, params):
"end": _hex(_basic_block_end(procedure, block)),
"successors": sorted(set(successors), key=lambda value: int(value, 16)),
})
pseudo = _pseudocode(document, procedure) or ""
pseudo = _pseudocode(document, procedure)
assembly_lines = _assembly(procedure).splitlines()
callers = sorted((_procedure_identity(item) for item in procedure.getAllCallerProcedures()), key=lambda item: int(item["address"], 16))
callees = sorted((_procedure_identity(item) for item in procedure.getAllCalleeProcedures()), key=lambda item: int(item["address"], 16))
@@ -706,7 +706,7 @@ def _analyze_function(document, params):
"signature": procedure.signatureString(),
"locals": _procedure_locals(procedure),
},
"pseudocode": pseudo,
"pseudocode": pseudo or "",
"assembly": assembly_lines,
"comments": comments,
"callers": callers, "callees": callees,
@@ -718,6 +718,12 @@ def _analyze_function(document, params):
"basic_blocks": blocks,
"native_api": None,
"native_value_flow": None,
"facet_availability": {
"pseudocode": {"available": True} if pseudo is not None else _unavailable("Hopper did not supply pseudocode."),
**{facet: {"available": True} for facet in (
"assembly", "comments", "callers", "callees", "references", "strings_names", "cfg"
)},
},
"limitations": [
"Native CallReference classifications are observed where available; detailed reference flags remain unknown.",
"Hopper's public Python API does not expose equivalent external or thunk classification in this dossier.",
@@ -730,7 +736,12 @@ def _analyze_function(document, params):
def _dispatch(method, params):
"""Dispatch only the closed operation set implemented by REA's public tools."""
if method == "health":
return {"name": "REA Hopper bridge", "version": "1.0.0", "run_id": REA_RUN_ID}
return {
"name": "REA Hopper bridge",
"version": "1.0.0",
"run_id": REA_RUN_ID,
"process_id": os.getpid(),
}
if method in ("shutdown", "shutdown_document"):
document = _session_document()
if document is None:
@@ -1046,6 +1057,10 @@ def _serve_connection(connection):
):
raise PermissionError("Invalid bridge capability")
authenticated = True
# The connection grace period only bounds unauthenticated idle
# clients. Native analysis and a connected owner's idle time
# remain unbounded after capability validation.
connection.settimeout(None)
_write_message(file, {"id": request_id, "event": {
"type": "progress",
"phase": "hopper_bridge",
@@ -1137,13 +1152,17 @@ def _run():
server.bind(REA_SOCKET)
os.chmod(REA_SOCKET, 0o600)
server.listen(1)
# A bridge can start after its caller's startup/cleanup deadline. Bound
# the first accept as well as reconnects so an orphan cannot pin Hopper's
# dedicated Python thread and prevent later documents from starting REA.
server.settimeout(REA_RECONNECT_TIMEOUT_SECONDS)
try:
while True:
try:
connection, _ = server.accept()
except socket.timeout:
break
connection.settimeout(None)
connection.settimeout(REA_RECONNECT_TIMEOUT_SECONDS)
if _serve_connection(connection):
break
# Match REA's cleanup handshake budget without pinning Hopper's
+188 -12
View File
@@ -6,11 +6,13 @@ The containing mitmdump process and its configuration directory are REA-owned.
import base64
import hashlib
import ipaddress
import json
import math
import re
import resource
from pathlib import Path
from urllib.parse import unquote_to_bytes
from mitmproxy import ctx, version
from mitmproxy.io import tnetstring
@@ -41,6 +43,167 @@ class BoundedReader:
return self.handle.read(count)
SPECIAL_SCHEMES = {"ftp", "file", "http", "https", "ws", "wss"}
SCHEME_PREFIX = r"^[\x00-\x20]*([a-z][a-z0-9+.-]*):"
NETWORK_PREFIX = r"^[\x00-\x20]*[/\\]{2}"
URL_IGNORABLE_CONTROLS = "\t\n\r"
URL_CONTROL_TRANSLATION = {ord(char): None for char in URL_IGNORABLE_CONTROLS}
C0_CONTROL_OR_SPACE = "".join(chr(code) for code in range(0x21))
FORBIDDEN_DOMAIN_CODE_POINTS = set(C0_CONTROL_OR_SPACE) | set("#%/:<>?@[\\]^|\x7f")
def authority_start(text, base_scheme=None):
"""Return where an authority begins and whether it is special, or None.
Follows WHATWG URL parsing as src/domain/recordedTransportUrl.ts does:
special schemes (and scheme-relative references) skip every leading "/"
or "\\" before the authority, so `https://\\user:pass@host` still has
userinfo. File URLs never carry userinfo: an "@" in a file host makes
WHATWG reject the whole URL, so there are no credentials to remove.
"""
scheme = re.match(SCHEME_PREFIX, text, flags=re.I)
name = scheme.group(1).lower() if scheme is not None else None
if name == "file" or (name is None and base_scheme == "file"):
return None
if name is not None and name in SPECIAL_SCHEMES:
# A same-scheme Location without two slash-equivalent separators is
# relative to the recorded request, so its @ belongs to the path.
following = text[scheme.end():scheme.end() + 2]
if name == base_scheme and (len(following) < 2 or any(char not in "/\\" for char in following)):
return None
start = scheme.end()
else:
if scheme is not None:
if text[scheme.end():scheme.end() + 2] != "//":
return None
return scheme.end() + 2, False
# A non-special base treats backslashes as authority data and only
# recognizes two forward slashes as a network reference.
if base_scheme is not None and base_scheme not in SPECIAL_SCHEMES:
prefix = re.match(r"^[\x00-\x20]*//", text)
return None if prefix is None else (prefix.end(), False)
prefix = re.match(NETWORK_PREFIX, text)
if prefix is None:
return None
start = prefix.end()
while start < len(text) and text[start] in "/\\":
start += 1
return start, True
IPV4_DIGITS = {8: "01234567", 10: "0123456789", 16: "0123456789abcdefABCDEF"}
def ipv4_number(part):
"""WHATWG IPv4 number parser: decimal, 0x hex or 0-prefixed octal, else None."""
if part == "":
return None
radix, digits = 10, part
if part[:2] in ("0x", "0X"):
radix, digits = 16, part[2:]
elif len(part) > 1 and part[0] == "0":
radix, digits = 8, part[1:]
if digits == "":
return 0
if any(char not in IPV4_DIGITS[radix] for char in digits):
return None
# Bound decimal text before int(): Python limits decimal conversions to
# 4300 digits, and anything past 10 significant digits exceeds IPv4.
significant = digits.lstrip("0")
if radix == 10 and len(significant) > 10:
return 2**32
return int(digits, radix)
def domain_parses(domain):
"""Apply WHATWG's ends-in-a-number check and IPv4 parser to a domain."""
parts = domain.split(".")
if parts[-1] == "" and len(parts) > 1:
parts.pop()
last = parts[-1]
if not ((last != "" and last.isascii() and last.isdigit()) or ipv4_number(last) is not None):
return True
numbers = [ipv4_number(part) for part in parts]
if len(parts) > 4 or any(number is None for number in numbers):
return False
return all(number <= 255 for number in numbers[:-1]) and numbers[-1] < 256 ** (5 - len(numbers))
def special_host_parses(host_port):
"""Whether WHATWG could accept a special authority's host and port.
A rejected host fails the whole URL, so it has no credentials to remove.
This mirrors the forbidden-code-point, empty-host, IPv4, IPv6 and port
rules for ASCII hosts. UTS #46 mapping is not modeled: a non-ASCII host
may map to an accepted one (fullwidth digits, ideographic full stops,
ignored code points), so it is treated as accepted and still redacts.
"""
if host_port.startswith("["):
host_end = host_port.find("]") + 1
literal = host_port[1 : host_end - 1]
if host_end == 0 or "%" in literal:
return False
try:
ipaddress.IPv6Address(literal)
except ValueError:
return False
else:
colon = host_port.find(":")
host_end = len(host_port) if colon < 0 else colon
host = unquote_to_bytes(host_port[:host_end]).decode("utf-8", errors="replace")
# Mapping never removes ASCII, so these rejections hold either way.
if host == "" or any(char in FORBIDDEN_DOMAIN_CODE_POINTS for char in host):
return False
if host.isascii() and not domain_parses(host.lower()):
return False
port = host_port[host_end:]
if port == "":
return True
digits = port[1:]
if port[0] != ":" or not (digits == "" or (digits.isascii() and digits.isdigit())):
return False
# Bound the text first: int() refuses decimal strings over 4300 digits.
significant = digits.lstrip("0")
return len(significant) <= 5 and int(significant or "0") <= 65535
def without_userinfo(value, base_scheme=None):
"""Remove an authority's userinfo using the WHATWG authority boundary.
Special schemes and scheme-relative references also end the authority
at a backslash, so later path text containing "@" is not userinfo.
"""
binary = isinstance(value, bytes)
text = value.decode("latin-1") if binary else value
# WHATWG removes these controls before interpreting scheme/separators.
# Keep that parse view separate from the exact source spelling and bytes.
# It also drops trailing C0 controls and spaces; offsets are unchanged.
parsed_text = text.translate(URL_CONTROL_TRANSLATION).rstrip(C0_CONTROL_OR_SPACE)
located = authority_start(parsed_text, base_scheme)
if located is None:
return value
start, special = located
terminators = "/?#\\" if special else "/?#"
end = start
while end < len(parsed_text) and parsed_text[end] not in terminators:
end += 1
at = parsed_text.rfind("@", start, end)
if at < start or (special and not special_host_parses(parsed_text[at + 1 : end])):
return value
parsed_offset = 0
raw_start = 0
for raw_offset, char in enumerate(text):
if char in URL_IGNORABLE_CONTROLS:
continue
if parsed_offset == start:
raw_start = raw_offset
if parsed_offset == at:
safe = text[:raw_start] + text[raw_offset + 1 :]
return safe.encode("latin-1") if binary else safe
parsed_offset += 1
raise CaptureFailure("decoder", "Normalized URL authority could not be bound to its source.")
def project_record(state, secrets):
binaries, numbers, redactions = [], [], []
byte_secrets = []
@@ -58,16 +221,23 @@ def project_record(state, secrets):
return None
return text
def transport_value(value, path, transport_url):
def transport_value(value, path, transport_url, base_scheme):
if not (transport_url or re.fullmatch(r"/(?:backup/)*request/(?:path|authority)", path)):
return value, False
binary = isinstance(value, bytes)
pattern = rb"^([\x00-\x20]*(?:[a-z][a-z0-9+.-]*:)?//)[^/?#]*@" if binary else r"^([\x00-\x20]*(?:[a-z][a-z0-9+.-]*:)?//)[^/?#]*@"
replacement = rb"\1" if binary else r"\1"
separator = b"@" if binary else "@"
safe_url = re.sub(pattern, replacement, value, flags=re.I)
if path.endswith("/authority") and separator in safe_url:
safe_url = safe_url.rsplit(separator, 1)[1]
if transport_url:
safe_url = without_userinfo(value, base_scheme)
elif path.endswith("/authority"):
safe_url = value.rsplit(separator, 1)[-1]
else:
# Native request.path is origin-form unless it explicitly carries
# a scheme (absolute-form). Leading separators are path evidence,
# not a header URL reference's network authority.
text = value.decode("latin-1") if binary else value
safe_url = without_userinfo(value) if re.match(
SCHEME_PREFIX, text.translate(URL_CONTROL_TRANSLATION), flags=re.I
) else value
changed = safe_url != value
if changed:
redactions.append({"pointer": path, "reason": "transport-credential"})
@@ -80,7 +250,7 @@ def project_record(state, secrets):
del numbers[before[1]:]
del redactions[before[2]:]
def visit(value, path, depth=0, credential=False, transport_url=False):
def visit(value, path, depth=0, credential=False, transport_url=False, base_scheme=None):
if depth > 64:
raise CaptureFailure("input-limit", "Native capture exceeds the 64-level evidence nesting budget.", path)
if credential:
@@ -90,7 +260,7 @@ def project_record(state, secrets):
binaries.append({"pointer": path, "representation": "producer-bytes", "state": "redacted", "content_base64": None, "bytes": None, "sha256": None})
return None
if isinstance(value, bytes):
safe_bytes, credential_url = transport_value(value, path, transport_url)
safe_bytes, credential_url = transport_value(value, path, transport_url, base_scheme)
content_base64 = base64.b64encode(value).decode("ascii")
digest = hashlib.sha256(value).hexdigest()
hidden = any(secret in value or secret in safe_bytes for secret in byte_secrets) or any(secret in content_base64 or secret in digest or secret in str(len(value)) for secret in secrets)
@@ -107,7 +277,7 @@ def project_record(state, secrets):
return None
return safe_text
if isinstance(value, str):
safe_text, _ = transport_value(value, path, transport_url)
safe_text, _ = transport_value(value, path, transport_url, base_scheme)
return redact(safe_text, path, value)
if isinstance(value, bool) or value is None:
return value
@@ -125,10 +295,16 @@ def project_record(state, secrets):
for index, field in enumerate(value):
if not isinstance(field, (list, tuple)) or len(field) != 2 or not isinstance(field[0], bytes):
raise CaptureFailure("format", "Native header/trailer is not an ordered byte pair.", pointer(path, index))
result.append([visit(field[0], pointer(pointer(path, index), 0), depth + 2), visit(field[1], pointer(pointer(path, index), 1), depth + 2, field[0].lower() in CREDENTIALS, field[0].lower() in {b"location", b"referer", b"origin"})])
result.append([visit(field[0], pointer(pointer(path, index), 0), depth + 2), visit(field[1], pointer(pointer(path, index), 1), depth + 2, field[0].lower() in CREDENTIALS, field[0].lower() in {b"location", b"referer", b"origin"}, base_scheme if field[0].lower() == b"location" else None)])
return result
return [visit(item, pointer(path, index), depth + 1) for index, item in enumerate(value)]
return [visit(item, pointer(path, index), depth + 1, base_scheme=base_scheme) for index, item in enumerate(value)]
if isinstance(value, dict):
if path == "" or re.fullmatch(r"/backup(?:/backup)*", path):
request = value.get("request")
scheme = request.get("scheme") if isinstance(request, dict) else None
if isinstance(scheme, bytes):
scheme = scheme.decode("latin-1")
base_scheme = scheme.lower() if isinstance(scheme, str) and re.fullmatch(r"[a-z][a-z0-9+.-]*", scheme, flags=re.I) else None
result = {}
for key, item in value.items():
if not isinstance(key, str):
@@ -139,7 +315,7 @@ def project_record(state, secrets):
validate_omitted(item, pointer(path, key), depth + 1)
redactions.append({"pointer": path, "reason": "explicit-sensitive-value", "scope": "property-name"})
continue
result[key] = visit(item, pointer(path, key), depth + 1)
result[key] = visit(item, pointer(path, key), depth + 1, base_scheme=base_scheme)
return result
raise CaptureFailure("decoder", "Native parser returned an unexpected value type: " + type(value).__name__ + ".", path)
+265
View File
@@ -0,0 +1,265 @@
"""Source-owned tests for the mitmproxy adapter's record projection."""
import importlib.util
import os
import sys
import types
import unittest
from unittest import mock
def load_capture():
"""Load the adapter with fresh mitmproxy stand-ins scoped to its import."""
fake_mitmproxy = types.ModuleType("mitmproxy")
fake_mitmproxy.ctx = types.SimpleNamespace()
fake_mitmproxy.version = types.SimpleNamespace(VERSION="12.2.3")
fake_io = types.ModuleType("mitmproxy.io")
fake_io.tnetstring = types.ModuleType("mitmproxy.io.tnetstring")
stand_ins = {
"mitmproxy": fake_mitmproxy,
"mitmproxy.io": fake_io,
"mitmproxy.io.tnetstring": fake_io.tnetstring,
}
spec = importlib.util.spec_from_file_location(
"rea_mitmproxy_capture", os.path.join(os.path.dirname(__file__), "capture.py")
)
module = importlib.util.module_from_spec(spec)
with mock.patch.dict(sys.modules, stand_ins):
spec.loader.exec_module(module)
return module
capture = load_capture()
def location(value, scheme=None):
state = {"response": {"headers": [[b"Location", value]]}}
if scheme is not None:
state["request"] = {"scheme": scheme}
record = capture.project_record(state, [])
sidecar = next(
field for field in record["binary_fields"] if field["pointer"] == "/response/headers/0/1"
)
return record["reported"]["response"]["headers"][0][1], sidecar, record["redactions"]
class TransportUserinfoTests(unittest.TestCase):
def test_ignorable_controls_cannot_hide_authority_credentials(self):
for control in [b"\t", b"\n", b"\r"]:
for value in [b"https://" + control + b"/user:pass@host/path", b"ht" + control + b"tps://user:" + control + b"pass@host/path"]:
with self.subTest(value=value):
text, sidecar, redactions = location(value, b"https")
self.assertNotIn("user", text)
self.assertNotIn("pass", text)
self.assertEqual(sidecar["state"], "redacted")
self.assertEqual(len(redactions), 1)
def test_mixed_network_separators_remove_credentials(self):
for prefix in [b"\\\\", b"/\\", b"\\/"]:
with self.subTest(prefix=prefix):
text, sidecar, redactions = location(prefix + b"user:pass@host/path", b"https")
self.assertEqual(text, prefix.decode("ascii") + "host/path")
self.assertEqual(sidecar["state"], "redacted")
self.assertEqual(len(redactions), 1)
def test_non_special_base_keeps_backslashes_inside_network_authorities(self):
text, sidecar, redactions = location(b"//user\\name:pass@host/x", b"foo")
self.assertEqual(text, "//host/x")
self.assertEqual(sidecar["state"], "redacted")
self.assertEqual(len(redactions), 1)
text, sidecar, redactions = location(b"/\\invoice@customer", b"foo")
self.assertEqual(text, "/\\invoice@customer")
self.assertEqual(sidecar["state"], "retained")
self.assertEqual(redactions, [])
def test_native_request_paths_preserve_origin_form_and_redact_absolute_form(self):
for value in [b"/\\invoice@customer", b"//invoice@customer", b"\\\\invoice@customer"]:
with self.subTest(value=value):
record = capture.project_record({"request": {"path": value}}, [])
self.assertEqual(record["reported"]["request"]["path"], value.decode("ascii"))
self.assertEqual(record["binary_fields"][0]["state"], "retained")
self.assertEqual(record["redactions"], [])
record = capture.project_record({"request": {"path": b"https://user:pass@host/x"}}, [])
self.assertEqual(record["reported"]["request"]["path"], "https://host/x")
self.assertEqual(record["binary_fields"][0]["state"], "redacted")
def test_same_scheme_locations_preserve_relative_path_text_and_bytes(self):
for value in [b"https:invoice@customer", b"https:/invoice@customer", b"https:\\invoice@customer"]:
with self.subTest(value=value):
text, sidecar, redactions = location(value, b"https")
self.assertEqual(text, value.decode("ascii"))
self.assertEqual(sidecar["state"], "retained")
self.assertEqual(redactions, [])
def test_location_authorities_still_remove_credentials_with_a_base_scheme(self):
for value, scheme, expected in [
(b"https://user:pass@host/path", b"https", "https://host/path"),
(b"https:\\\\user:pass@host/path", b"https", "https:\\\\host/path"),
(b"https:user:pass@host/path", b"http", "https:host/path"),
]:
with self.subTest(value=value, scheme=scheme):
text, sidecar, redactions = location(value, scheme)
self.assertEqual(text, expected)
self.assertEqual(sidecar["state"], "redacted")
self.assertEqual(len(redactions), 1)
def test_backup_location_uses_its_own_request_scheme(self):
record = capture.project_record({
"request": {"scheme": b"http"},
"backup": {
"request": {"scheme": b"https"},
"response": {"headers": [[b"Location", b"https:invoice@customer"]]},
},
}, [])
self.assertEqual(record["reported"]["backup"]["response"]["headers"][0][1], "https:invoice@customer")
self.assertEqual(record["redactions"], [])
def test_backslash_ends_a_special_scheme_authority(self):
text, sidecar, redactions = location(b"https://example.test\\part@tail")
self.assertEqual(text, "https://example.test\\part@tail")
self.assertEqual(sidecar["state"], "retained")
self.assertIsNotNone(sidecar["content_base64"])
self.assertEqual(redactions, [])
def test_userinfo_before_a_backslash_is_still_removed(self):
text, sidecar, redactions = location(b"https://user:pw@example.test\\path")
self.assertEqual(text, "https://example.test\\path")
self.assertEqual(sidecar["state"], "redacted")
self.assertEqual(
redactions,
[{"pointer": "/response/headers/0/1", "reason": "transport-credential"}],
)
def test_ordinary_userinfo_is_removed(self):
text, _sidecar, _redactions = location(b"https://user:pw@example.test/a@b")
self.assertEqual(text, "https://example.test/a@b")
def test_empty_userinfo_marker_is_removed(self):
self.assertEqual(
capture.without_userinfo("https://@host"), "https://host"
)
def test_special_schemes_skip_leading_slashes_before_userinfo(self):
for value, expected in [
(b"https://\\user:pass@host/path", b"https://\\host/path"),
(b"https:\\\\user:pass@host/path", b"https:\\\\host/path"),
(b"https:user:pass@host", b"https:host"),
(b"//\\user:pass@host", b"//\\host"),
]:
with self.subTest(value=value):
self.assertEqual(capture.without_userinfo(value), expected)
text, sidecar, redactions = location(b"https://\\user:pass@host/path")
self.assertEqual(text, "https://\\host/path")
self.assertEqual(sidecar["state"], "redacted")
self.assertEqual(len(redactions), 1)
def test_file_base_keeps_paths_after_the_fixed_network_boundary(self):
for value in [b"///u@h", b"////u@h"]:
with self.subTest(value=value):
text, sidecar, redactions = location(value, b"file")
self.assertEqual(text, value.decode("ascii"))
self.assertEqual(sidecar["state"], "retained")
self.assertIsNotNone(sidecar["content_base64"])
self.assertEqual(redactions, [])
def test_file_urls_keep_path_text(self):
self.assertEqual(capture.without_userinfo("file:///a@b"), "file:///a@b")
def test_file_urls_reject_userinfo_and_keep_their_source(self):
# WHATWG rejects an "@" in a file host, so nothing parses as credentials.
self.assertEqual(
capture.without_userinfo("file://user:pass@host/x"), "file://user:pass@host/x"
)
for value in [b"\\\\user:pass@host/x", b"/\\user:pass@host/x", b"//user:pass@host/x"]:
with self.subTest(value=value):
text, sidecar, redactions = location(value, b"file")
self.assertEqual(text, value.decode("ascii"))
self.assertEqual(sidecar["state"], "retained")
self.assertEqual(redactions, [])
def test_rejected_special_hosts_keep_their_source(self):
# A rejected host or port fails the whole URL, so it has no userinfo.
for value in [
"https:user@%",
"https://user@",
"https://user:pass@h^st/x",
"https://user@ho st/x",
"https://user@host:99999/x",
"https://user@host:12a",
"https://user@[::1",
]:
with self.subTest(value=value):
self.assertEqual(capture.without_userinfo(value), value)
text, sidecar, redactions = location(b"https://user:pass@h^st/x", b"https")
self.assertEqual(text, "https://user:pass@h^st/x")
self.assertEqual(sidecar["state"], "retained")
self.assertEqual(redactions, [])
def test_accepted_special_hosts_still_remove_userinfo(self):
for value, expected in [
("https:user@%41", "https:%41"),
("https://user@[::1]:8443/x", "https://[::1]:8443/x"),
("https://user@host:/x", "https://host:/x"),
("wss://user:pass@host:65535", "wss://host:65535"),
("https://user@0x7f.1/", "https://0x7f.1/"),
("https://user@1.2.3.4./", "https://1.2.3.4./"),
("https://user@4294967295/", "https://4294967295/"),
("https://user@1.2.3.08a/", "https://1.2.3.08a/"),
("https://user@[::ffff:1.2.3.4]/", "https://[::ffff:1.2.3.4]/"),
]:
with self.subTest(value=value):
self.assertEqual(capture.without_userinfo(value), expected)
def test_unicode_hosts_that_may_map_to_accepted_hosts_remove_userinfo(self):
# UTS #46 maps these to 1.2.3.4; mapping is not modeled, so redact.
for host in ["%EF%BC%91.2.3.4", "1。2.3.4", "%C2%AD1.2.3.4", "1.2.3.4"]:
with self.subTest(host=host):
self.assertEqual(
capture.without_userinfo(f"https://user:pw@{host}/"), f"https://{host}/"
)
text, sidecar, redactions = location(b"https://user:pw@%EF%BC%91.2.3.4/", b"https")
self.assertEqual(text, "https://%EF%BC%91.2.3.4/")
self.assertEqual(sidecar["state"], "redacted")
self.assertEqual(len(redactions), 1)
# ASCII rejections still hold for non-ASCII hosts.
self.assertEqual(capture.without_userinfo("https://user@1^/"), "https://user@1^/")
def test_trailing_controls_and_spaces_do_not_hide_userinfo(self):
# WHATWG strips trailing C0 controls and spaces before parsing.
for suffix in [b" ", b"\x01", b"\x00 \x1f"]:
with self.subTest(suffix=suffix):
value = b"https://user:pw@example.test" + suffix
text, sidecar, redactions = location(value, b"https")
self.assertEqual(text, "https://example.test" + suffix.decode("latin-1"))
self.assertEqual(sidecar["state"], "redacted")
self.assertEqual(len(redactions), 1)
def test_rejected_ip_literals_keep_their_source(self):
for value in [
"https://user:pass@256.256.256.256/path",
"https://user@1.2.3.4.5/",
"https://user@1.2.3.09/",
"https://user@4294967296/",
"https://user@a.0x/",
"https://user@1..2/",
"https://user@1." + "9" * 5000 + "/",
"https://user@[1::2::3]/",
"https://user@[::1%25eth0]/",
]:
with self.subTest(value=value[:40]):
self.assertEqual(capture.without_userinfo(value), value)
def test_overlong_ports_are_compared_without_numeric_overflow(self):
# Python 3.11+ refuses int() on decimal strings over 4300 digits.
accepted = "https://user@host:" + "0" * 5000 + "80/x"
self.assertEqual(capture.without_userinfo(accepted), "https://host:" + "0" * 5000 + "80/x")
rejected = "https://user@host:" + "9" * 5000 + "/x"
self.assertEqual(capture.without_userinfo(rejected), rejected)
def test_non_special_schemes_keep_backslashes_in_the_authority(self):
self.assertEqual(capture.without_userinfo("foo://a\\b@host/x"), "foo://host/x")
self.assertEqual(capture.without_userinfo(b"//a\\b@host"), b"//a\\b@host")
if __name__ == "__main__":
unittest.main()
+88 -10
View File
@@ -555,6 +555,38 @@ def _failed_launch_description(process):
return None
def _retire_failed_launch(process, config, listener, stdout, stderr):
"""Reconcile an SBProcess returned alongside a failed Launch error."""
if not process.IsValid():
stdout.close()
stderr.close()
return False
pid = process.GetProcessID()
if pid > 0:
# Even an already-exited process needs its PID published so the host
# can distinguish a completed launch failure from an unknown target.
with open(config["pid_path"], "w") as handle:
handle.write(str(pid))
if process.GetState() != lldb.eStateExited:
stdout.mark_launched()
stderr.mark_launched()
if pid > 0:
# Hold a surviving target until the host has captured its start
# identity. If that proof is unavailable, Kill still addresses
# this exact SBProcess created by the current launch request.
_wait_for_identity_ack(config)
process.Kill()
_wait_for_exit(listener, process, 5)
exit_observed = process.GetState() == lldb.eStateExited
expected_exit = exit_observed
stdout.close(expected_exit=expected_exit)
stderr.close(expected_exit=expected_exit)
return exit_observed
def trace(debugger, config):
global _TRACE_CONTEXT
debugger.SetAsync(True)
@@ -567,18 +599,27 @@ def trace(debugger, config):
before_digest = _hash_file(config["executable"])
before_identity = _file_identity(config["executable"])
except OSError as error:
return {"status": "target-integrity-error", "error": str(error)}
return {
"status": "target-integrity-error",
"error": str(error),
"target_process_created": False,
}
if before_digest != config["expected_sha256"]:
return {
"status": "target-integrity-error",
"error": "selected executable digest changed before LLDB target creation",
"target_process_created": False,
}
error = lldb.SBError()
target = debugger.CreateTarget(
config["executable"], config["architecture"], None, False, error
)
if not target.IsValid():
return {"status": "target-error", "error": error.GetCString()}
return {
"status": "target-error",
"error": error.GetCString(),
"target_process_created": False,
}
module_path_before, module_file_identity_before, module_uuid_before = (
_module_identity(target)
)
@@ -589,6 +630,7 @@ def trace(debugger, config):
return {
"status": "target-integrity-error",
"error": "LLDB executable module resolved to a different pre-launch file identity",
"target_process_created": False,
}
created = [_create_breakpoints(target, spec) for spec in config["breakpoints"]]
breakpoint_index = {}
@@ -620,14 +662,38 @@ def trace(debugger, config):
)
process = target.Launch(_launch_info(target, config, listener), error)
if not error.Success() or not process.IsValid():
stdout.close()
stderr.close()
return {"status": "launch-error", "error": error.GetCString()}
exit_observed = _retire_failed_launch(
process, config, listener, stdout, stderr
)
return {
"status": "launch-error",
"error": error.GetCString(),
**(
{"target_process_created": True}
if process.IsValid()
else {}
),
**(
{"target_process_exit_observed": True}
if exit_observed
else {}
),
}
launch_failure = _failed_launch_description(process)
if launch_failure is not None:
stdout.close(expected_exit=True)
stderr.close(expected_exit=True)
return {"status": "launch-error", "error": launch_failure}
exit_observed = _retire_failed_launch(
process, config, listener, stdout, stderr
)
return {
"status": "launch-error",
"error": launch_failure,
"target_process_created": True,
**(
{"target_process_exit_observed": True}
if exit_observed
else {}
),
}
stdout.mark_launched()
stderr.mark_launched()
# Publish the PID before any post-launch file or module inspection. The
@@ -644,6 +710,7 @@ def trace(debugger, config):
return {
"status": "target-identity-unavailable",
"error": "host could not establish the launched process identity; target was stopped",
"target_process_created": True,
}
try:
after_digest = _hash_file(config["executable"])
@@ -652,7 +719,11 @@ def trace(debugger, config):
process.Kill()
stdout.close(expected_exit=process.GetState() == lldb.eStateExited)
stderr.close(expected_exit=process.GetState() == lldb.eStateExited)
return {"status": "target-integrity-error", "error": str(identity_error)}
return {
"status": "target-integrity-error",
"error": str(identity_error),
"target_process_created": True,
}
if after_digest != config["expected_sha256"] or after_identity != before_identity:
process.Kill()
_wait_for_exit(listener, process, 5)
@@ -661,6 +732,7 @@ def trace(debugger, config):
return {
"status": "target-integrity-error",
"error": "selected executable digest or file identity changed during LLDB launch",
"target_process_created": True,
}
module_path_after, module_file_identity_after, module_uuid_after = (
_module_identity(target)
@@ -676,6 +748,7 @@ def trace(debugger, config):
return {
"status": "target-integrity-error",
"error": "LLDB executable module resolved to a different file identity",
"target_process_created": True,
}
if (
module_uuid_before is not None
@@ -689,6 +762,7 @@ def trace(debugger, config):
return {
"status": "target-integrity-error",
"error": "LLDB executable module identity changed during launch",
"target_process_created": True,
}
context = {
"target": target,
@@ -748,7 +822,11 @@ def trace(debugger, config):
# exit code; preserve the debugger's concrete launch denial.
launch_failure = _failed_launch_description(process)
if launch_failure is not None:
return {"status": "launch-error", "error": launch_failure}
return {
"status": "launch-error",
"error": launch_failure,
"target_process_created": True,
}
launch_identity_stable = (
module_path_before is not None
and module_path_after == module_path_before
+169
View File
@@ -8,12 +8,15 @@ import sys
import tempfile
import types
import unittest
from unittest.mock import patch
fake_lldb = sys.modules.setdefault("lldb", types.ModuleType("lldb"))
fake_lldb.LLDB_INVALID_ADDRESS = -1
fake_lldb.eLaunchFlagDebug = 2
fake_lldb.eLaunchFlagStopAtEntry = 4
fake_lldb.eStateExited = 10
fake_lldb.SBEvent = lambda: object()
spec = importlib.util.spec_from_file_location(
"rea_lldb_tracer", os.path.join(os.path.dirname(__file__), "rea_lldb_tracer.py")
)
@@ -22,6 +25,172 @@ spec.loader.exec_module(tracer)
class NativeTraceBudgetTests(unittest.TestCase):
def test_failed_launch_does_not_infer_exit_from_a_failed_kill(self):
class Process:
def IsValid(self):
return True
def GetProcessID(self):
return 12345
def GetState(self):
return 6
def Kill(self):
return None
class Sink:
close_exit = None
def mark_launched(self):
pass
def close(self, expected_exit=False):
self.close_exit = expected_exit
with tempfile.TemporaryDirectory() as directory:
stdout, stderr = Sink(), Sink()
with patch.object(tracer, "_wait_for_identity_ack", return_value="unavailable"), patch.object(
tracer, "_wait_for_exit", return_value=None
):
exited = tracer._retire_failed_launch(
Process(), {"pid_path": os.path.join(directory, "pid")}, object(), stdout, stderr
)
self.assertFalse(exited)
self.assertFalse(stdout.close_exit)
self.assertFalse(stderr.close_exit)
def test_failed_launch_publishes_pid_and_retires_live_process(self):
class Process:
state = 6
killed = False
def IsValid(self):
return True
def GetProcessID(self):
return 12345
def GetState(self):
return self.state
def Kill(self):
self.killed = True
self.state = tracer.lldb.eStateExited
class Sink:
launched = False
close_exit = None
def mark_launched(self):
self.launched = True
def close(self, expected_exit=False):
self.close_exit = expected_exit
with tempfile.TemporaryDirectory() as directory:
pid_path = os.path.join(directory, "pid")
process = Process()
stdout, stderr = Sink(), Sink()
def acknowledge(config):
with open(config["pid_path"], "r") as pid_file:
self.assertEqual(pid_file.read(), "12345")
return "readable"
with patch.object(tracer, "_wait_for_identity_ack", acknowledge):
exited = tracer._retire_failed_launch(
process,
{"pid_path": pid_path},
object(),
stdout,
stderr,
)
self.assertTrue(exited)
self.assertTrue(process.killed)
self.assertTrue(stdout.launched)
self.assertTrue(stderr.launched)
self.assertTrue(stdout.close_exit)
self.assertTrue(stderr.close_exit)
def test_failed_launch_records_when_valid_process_is_already_exited(self):
class Process:
def IsValid(self):
return True
def GetProcessID(self):
return 12345
def GetState(self):
return tracer.lldb.eStateExited
def Kill(self):
raise AssertionError("an exited process must not be killed")
class Sink:
close_exit = None
def mark_launched(self):
raise AssertionError("an exited process has no output to drain")
def close(self, expected_exit=False):
self.close_exit = expected_exit
with tempfile.TemporaryDirectory() as directory:
pid_path = os.path.join(directory, "pid")
stdout, stderr = Sink(), Sink()
with patch.object(
tracer,
"_wait_for_identity_ack",
side_effect=AssertionError("exit is already observed"),
):
exited = tracer._retire_failed_launch(
Process(), {"pid_path": pid_path}, object(), stdout, stderr
)
with open(pid_path, "r") as pid_file:
self.assertEqual(pid_file.read(), "12345")
self.assertTrue(exited)
self.assertTrue(stdout.close_exit)
self.assertTrue(stderr.close_exit)
def test_pre_target_integrity_failure_reports_no_process_created(self):
original_debugger = getattr(tracer.lldb, "SBDebugger", None)
class DebuggerApi:
@staticmethod
def SetInternalVariable(*_arguments):
return None
class Debugger:
def SetAsync(self, _enabled):
return None
def GetInstanceName(self):
return "fixture"
def CreateTarget(self, *_arguments):
raise AssertionError("integrity failure precedes target creation")
tracer.lldb.SBDebugger = DebuggerApi
try:
with patch.object(tracer, "_hash_file", return_value="changed"), patch.object(
tracer, "_file_identity", return_value=(1, 2)
):
result = tracer.trace(
Debugger(),
{"executable": "fixture", "expected_sha256": "expected"},
)
finally:
if original_debugger is None:
del tracer.lldb.SBDebugger
else:
tracer.lldb.SBDebugger = original_debugger
self.assertEqual(result["status"], "target-integrity-error")
self.assertIs(result["target_process_created"], False)
def test_journal_checks_exact_byte_budget_before_writing(self):
record = {"kind": "other-stop", "reason": "signal"}
encoded_size = len((tracer.json.dumps(record) + "\n").encode("utf-8"))
+31 -10
View File
@@ -11,6 +11,12 @@ import sys
PROFILE = "pwntools@4.15.0;pyelftools@0.33;unicorn@2.1.2"
# Linux ET_CORE machine identifiers whose PRSTATUS layout is interpreted with
# unchanged pwntools 4.15.0 ctypes. Both share pr_cursig@12, pr_pid@32 and
# pr_reg@112; only the register window differs: amd64 is 27 x 8 bytes for a
# 336-byte descriptor, AArch64 is 34 x 8 bytes for a 392-byte descriptor.
CORE_MACHINES = (62, 183)
class CoreFailure(Exception):
def __init__(self, reason, message):
@@ -51,12 +57,27 @@ def inspect_core(path, cache):
# Other declared OSABIs must never select Linux note ctypes.
if content[7] not in (0, 3) or content[8] != 0:
raise CoreFailure("unsupported-target", "Initial recorded-core profile requires System V or GNU/Linux OSABI with ABI version zero; selected OSABI=" + str(content[7]) + ", ABI version=" + str(content[8]) + ".")
if int.from_bytes(content[16:18], "little") != 4 or int.from_bytes(content[18:20], "little") != 62:
raise CoreFailure("unsupported-target", "Initial recorded-core profile requires x86-64 ET_CORE.")
machine = int.from_bytes(content[18:20], "little")
if int.from_bytes(content[16:18], "little") != 4 or machine not in CORE_MACHINES:
raise CoreFailure("unsupported-target", "Initial recorded-core profile requires ET_CORE for x86-64 or AArch64; selected ET_TYPE=" + str(int.from_bytes(content[16:18], "little")) + ", e_machine=" + str(machine) + ".")
from elftools.common.exceptions import ELFError
from elftools.common.utils import struct_parse
from elftools.elf.elffile import ELFFile
from pwnlib.elf.datatypes import elf_prstatus_amd64, elf_siginfo_64, user_regs_struct_amd64
from pwnlib.elf.datatypes import (
elf_prstatus_aarch64,
elf_prstatus_amd64,
elf_siginfo_64,
user_regs_struct_aarch64,
user_regs_struct_amd64,
)
if machine == 62:
profile_label = "x86-64"
architecture = "x86_64-little-endian"
prstatus_type, registers_type = elf_prstatus_amd64, user_regs_struct_amd64
else:
profile_label = "AArch64"
architecture = "aarch64-little-endian"
prstatus_type, registers_type = elf_prstatus_aarch64, user_regs_struct_aarch64
try:
image = ELFFile(io.BytesIO(content))
header = image.header
@@ -107,17 +128,17 @@ def inspect_core(path, cache):
if owner != b"CORE\0":
continue
if note.n_type == "NT_PRSTATUS":
if len(descriptor) < ctypes.sizeof(elf_prstatus_amd64):
raise CoreFailure("format", "Linux amd64 NT_PRSTATUS descriptor is truncated.")
status = elf_prstatus_amd64.from_buffer_copy(descriptor)
if len(descriptor) < ctypes.sizeof(prstatus_type):
raise CoreFailure("format", "Linux " + profile_label + " NT_PRSTATUS descriptor is truncated.")
status = prstatus_type.from_buffer_copy(descriptor)
registers = []
for name, kind in user_regs_struct_amd64._fields_:
relative = elf_prstatus_amd64.pr_reg.offset + getattr(user_regs_struct_amd64, name).offset
for name, kind in registers_type._fields_:
relative = prstatus_type.pr_reg.offset + getattr(registers_type, name).offset
registers.append({"name": name, "value": hex(getattr(status.pr_reg, name)), "location": location(descriptor_start + relative, ctypes.sizeof(kind), end)})
threads.append({"note_index": note_index, "historical_pid": ctypes.c_int32(status.pr_pid).value, "recorded_current_signal": status.pr_cursig, "registers": registers})
elif note.n_type == "NT_SIGINFO":
if len(descriptor) < ctypes.sizeof(elf_siginfo_64):
raise CoreFailure("format", "Linux amd64 NT_SIGINFO descriptor is truncated.")
raise CoreFailure("format", "Linux " + profile_label + " NT_SIGINFO descriptor is truncated.")
signal = elf_siginfo_64.from_buffer_copy(descriptor)
number = ctypes.c_int32(signal.si_signo).value
code = ctypes.c_int32(signal.si_code).value
@@ -125,7 +146,7 @@ def inspect_core(path, cache):
signals.append({"note_index": note_index, "number": number, "code": code, "errno": ctypes.c_int32(signal.si_errno).value,
"fault_address": hex(signal.sigfault_addr) if known else None,
"fault_address_meaning": "recorded-sigsegv-address" if known else "unknown", "thread_association": "unknown"})
return {"format": "elf-core", "architecture": "x86_64-little-endian", "target_execution": "not-performed", "live_process_identity": "unknown",
return {"format": "elf-core", "architecture": architecture, "target_execution": "not-performed", "live_process_identity": "unknown",
"segments": segments, "notes": notes, "note_padding": padding, "threads": threads, "signals": signals,
"note_interpretation_completeness": "unknown",
"limitations": ["The initial Linux note profile accepts System V/unspecified or GNU/Linux OSABI with ABI version zero; an unspecified OSABI alone does not prove operating-system origin.",
+5
View File
@@ -6,6 +6,7 @@ export default defineConfig({
"Reverse engineer binaries, applications, and runtime behavior with one CLI and MCP server.",
lang: "en-US",
base: "/rea/",
srcExclude: ["readme/**"],
themeConfig: {
nav: [
{ text: "Get started", link: "/installation" },
@@ -28,6 +29,7 @@ export default defineConfig({
items: [
{ text: "Native binaries", link: "/native-investigation" },
{ text: "Go build metadata", link: "/go-binaries" },
{ text: "Rust dependencies", link: "/rust-binaries" },
{
text: "JavaScript and Electron",
link: "/javascript-artifact-reconstruction",
@@ -42,6 +44,7 @@ export default defineConfig({
link: "/web-network-captures",
},
{ text: "Electron observation", link: "/electron-observation" },
{ text: "Electron fuses", link: "/electron-fuses" },
{ text: "Process capture", link: "/process-capture" },
{ text: ".NET assemblies", link: "/managed-code-analysis" },
{ text: "Android applications", link: "/android-analysis" },
@@ -57,10 +60,12 @@ export default defineConfig({
collapsed: true,
items: [
{ text: "IDA", link: "/ida-provider" },
{ text: "Kuna", link: "/kuna-provider" },
{ text: "Ghidra on Windows", link: "/windows-ghidra-p0" },
{ text: "Ghidra DOS analysis", link: "/ghidra-dos" },
{ text: "Ghidra raw binary", link: "/raw-binary" },
{ text: "Ghidra NativeAOT", link: "/ghidra-nativeaot" },
{ text: "Ghidra PowerPC", link: "/ghidra-ppc" },
{ text: "Ghidra semantics", link: "/ghidra-provider" },
],
},
+58 -28
View File
@@ -26,6 +26,9 @@ Readiness is `rea inspect-adb-client`: it runs `adb version` and reports the
resolved binary, its version, and where it was selected from. A missing
binary reports `capability_unavailable` with the `REA_ADB_PATH` remediation
rather than attempting an installation.
The `binary_session` inventory uses the same bounded client-version probe; it
does not query devices, start the adb server, or establish that a requested
serial is reachable. Device state is checked by each device operation.
## Answer an analyst question
@@ -73,32 +76,44 @@ existing analysis families directly, for example
## Operations
| Tool | Kind | What it does |
| ----------------------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `inspect_adb_client` | read | Resolved binary, version, install path; never contacts the server |
| `list_adb_devices` | read | Serial, state, transport, model metadata, inferred emulator/physical kind with basis |
| `inspect_adb_device` | read | Fixed `getprop` whitelist: build, SDK, patch, fingerprint, product, ABIs, debuggable, `ro.kernel.qemu` |
| `inspect_adb_display` | read | `wm size` and `wm density` as declared |
| `list_adb_features` | read | `pm list features` (hardware/software features, hex GL versions) |
| `list_adb_services` | read | `service list` binder services and interfaces |
| `list_adb_processes` | read | `ps -A`: user, pid, ppid, rss, state, name |
| `inspect_adb_window` | read | `dumpsys window` focus declarations (what is on screen) |
| `read_adb_setting` | read | One `settings get` value; device-side null is reported as such |
| `read_adb_logcat` | read | Bounded `logcat -d -t` dump for one buffer, optional pid filter |
| `list_adb_packages` | read | `pm list packages -f` with scope; base APK device paths |
| `resolve_adb_packages` | read | Case-insensitive package-name substring search with exact-match flag |
| `inspect_adb_package` | read | `dumpsys package` projection: versions, install times, installer, flags, permission count |
| `list_adb_directory` | read | NUL-framed immediate children, including hidden entries: kinds, permissions, owner, group, sizes, symlink targets |
| `pull_adb_package` | read | Complete split APK set from `pm path`, each file digested |
| `pull_adb_file` | read | One absolute device path pulled and digested |
| `capture_adb_screen` | read | One `screencap -p` frame (exec-out, no device-side file) with dimensions |
| `collect_adb_bugreport` | read | Device-generated bugreport zip, digested |
| `push_adb_file` | **mutates device** | One local file to a device path (e.g. pushing tools into `/data/local/tmp`); refuses existing targets without explicit overwrite; verifies via the device's own sha256sum |
| `install_adb_package` | **mutates device** | Local APK via `adb install [-r]`; preserves the device's Success/Failure verdict |
| `uninstall_adb_package` | **mutates device** | Removes a package and its data for user 0 |
| `start_adb_app` | **mutates device** | Resolves the package's launcher activity (`cmd package resolve-activity --brief`) and starts it (`am start -n`) |
| `start_adb_activity` | **mutates device** | Starts an intent (`am start -a` with optional URI, component, and up to 16 typed extras); preserves the activity manager's verdict |
| `stop_adb_app` | **mutates device** | Force-stops every process of a package, ending a dynamic-analysis run |
| Tool | Kind | What it does |
| ----------------------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `inspect_adb_client` | read | Resolved binary, version, install path; never contacts the server |
| `list_adb_devices` | read | Serial, state, transport, model metadata, inferred emulator/physical kind with basis |
| `inspect_adb_device` | read | Fixed `getprop` whitelist: build, SDK, patch, fingerprint, product, ABIs, debuggable, `ro.kernel.qemu` |
| `inspect_adb_display` | read | `wm size` and `wm density` as declared |
| `list_adb_features` | read | `pm list features` (hardware/software features, hex GL versions) |
| `list_adb_services` | read | `service list` binder services and interfaces |
| `list_adb_processes` | read | `ps -A`: user, pid, ppid, rss, state, name |
| `inspect_adb_window` | read | `dumpsys window` focus declarations (what is on screen) |
| `read_adb_setting` | read | One `settings get` value; device-side null is reported as such |
| `read_adb_logcat` | read | Bounded `logcat -d -t` dump for one buffer, optional pid filter |
| `list_adb_packages` | read | `pm list packages -f` with scope; base APK device paths |
| `resolve_adb_packages` | read | Case-insensitive package-name substring search with exact-match flag |
| `inspect_adb_package` | read | `dumpsys package` projection: versions, install times, installer, flags, permission count |
| `list_adb_directory` | read | NUL-framed immediate children, including hidden entries: kinds, permissions, owner, group, sizes, symlink targets |
| `pull_adb_package` | read | Complete split APK set from `pm path`, each file digested |
| `pull_adb_file` | read | One absolute device path pulled and digested |
| `capture_adb_screen` | read | One `screencap -p` frame (exec-out, no device-side file) with dimensions |
| `collect_adb_bugreport` | read | Device-generated bugreport zip, digested |
| `push_adb_file` | **mutates device** | One local file to a device path (e.g. pushing tools into `/data/local/tmp`); checks for existing targets and refuses them without explicit overwrite (the check is not atomic); compares digests when device `sha256sum` is available |
| `install_adb_package` | **mutates device** | Local APK via `adb install [-r]`; preserves its Success/Failure verdict, records `replace_requested`, and leaves actual replacement `null` because prior package state is not observed |
| `uninstall_adb_package` | **mutates device** | Removes a package and its data for user 0 |
| `start_adb_app` | **mutates device** | Resolves the package's launcher activity (`cmd package resolve-activity --brief`) and starts it (`am start -n`) |
| `start_adb_activity` | **mutates device** | Starts an intent (`am start -a` with optional URI, component, and up to 16 typed extras); preserves the activity manager's verdict |
| `stop_adb_app` | **mutates device** | Force-stops every process of a package, ending a dynamic-analysis run |
For `install_adb_package`, `replace_requested` records whether `-r` was sent.
The `replaced` result is `null`: the successful install response does not say
whether the package was installed fresh or replaced, and REA does not query the
device first. The install verdict and host APK digest remain available.
Push and install consume a private snapshot of the selected local input. The
reported size and SHA-256 identify those consumed bytes; `local_path` and
`apk_path` retain the original input path, whose contents may change later.
Snapshot creation needs temporary disk space proportional to the input size.
Failed snapshot cleanup remains owned and is retried before later operations
and during shutdown.
## Interpret the result
@@ -118,6 +133,16 @@ existing analysis families directly, for example
while POSIX hosts retain valid POSIX filenames such as names with spaces or
colons. A package pull checks reported names against the destination
filesystem before pulling, and refuses names that would share a local path.
- **Package path refusals are diagnosable.** Refusals from `pull_adb_package`
return `unreadable_output` with `retryable: false`, keep the `pm path` output
in `details.captured_output`, and name the refusal in `details.reason`.
`unsafe_device_path` lists refused entries in
`details.diagnostics.rejected_paths` as `{device_path, reason}`;
`duplicate_local_destination` reports the shared `file_name` and both
`device_paths`; `local_destination_unavailable` reports the `device_path`,
`file_name`, and filesystem `error_code`. Diagnostics retain raw device
strings, while the caller-facing message escapes invisible and bidirectional
characters so they cannot hide text or reorder the path.
- **Splits are part of the answer.** `pm path` enumerates the base APK and
every installed split; pulls cover the whole set. System packages whose
APKs keep other names report the `unknown` role rather than guessing.
@@ -127,8 +152,10 @@ existing analysis families directly, for example
partial coverage instead of dropping rows.
- **Mutations are explicit.** `push_adb_file`, `install_adb_package`, and
`uninstall_adb_package` declare `mutatesTarget` and never run implicitly:
pushes refuse existing device files unless `overwrite` is set and verify
the transfer with the device's own `sha256sum`; installs preserve the
pushes check for existing device files before transfer and refuse them
unless `overwrite` is set. The check is not atomic, so a different process
can create the path before `adb push` writes it. When available, the device's
`sha256sum` verifies the transfer; installs preserve the
device's `Failure [...]` reason verbatim.
- **Producer identity stays attached.** Every result embeds the producing
adb client (`binary_path`, `path_source`, `version`, `installed_path`).
@@ -148,6 +175,9 @@ existing analysis families directly, for example
- Cancellation during a package pull preserves caller-selected output. The
error includes partial observations identifying completed artifacts and the
interrupted local path; remaining APKs were not attempted.
- Screen capture checks cancellation before publishing its exclusive output.
If cancellation arrives after publication, the error retains the committed
file's path, digest and dimensions; the file remains available as evidence.
- Bugreports collect into isolated staging and publish with exclusive creation;
filename collisions select the next numeric suffix. If publication fails, the
completed archive remains in staging. The error's partial observation gives
+77 -17
View File
@@ -9,6 +9,10 @@ This family is present on repository main and in npm 4.1.0. Check the
[released package boundary](installation.md#released-package-and-main) and the
connected server's tool list before selecting it.
On Linux, JADX launches an REA-owned process and requires a procps-compatible
`ps` on REA's `PATH`; see the shared
[Linux process prerequisite](installation.md#linux-owned-process-prerequisite).
## Optional package delivery
The MCP `inspect_android_package` request keeps complete output when `detail` is
@@ -33,10 +37,40 @@ unverified.
## Supply tools
Use a **full JDK 17 or newer** (including its compiler module) and
**jadx-headless-mcp 0.7.1**. REA runs its packaged Java metadata bridge in
source-file mode against the unmodified engine JAR. REA does not install Java,
an Android SDK, JADX, a device service or an emulator. Set the path in the shell
or MCP server environment:
[**jadx-headless-mcp 0.7.1**](https://github.com/1013503897/jadx-headless-mcp/releases/download/v0.7.1/jadx-headless-mcp-0.7.1-all.jar).
Verify the downloaded JAR's SHA-256 against
[REA's pinned release metadata](https://github.com/morluto/rea/blob/main/src/android/JadxRelease.ts)
before using it. The following download and verification commands work for npm
package consumers as well as repository checkouts; the repository-only
`npm run fixtures:android` helper is not included in the npm package.
On macOS or Linux, choose a destination and verify the pinned digest:
```sh
mkdir -p "$HOME/.local/share/rea/android" &&
cd "$HOME/.local/share/rea/android" &&
curl -fL -o jadx-headless-mcp-0.7.1-all.jar https://github.com/1013503897/jadx-headless-mcp/releases/download/v0.7.1/jadx-headless-mcp-0.7.1-all.jar &&
if command -v sha256sum >/dev/null; then
echo '6e5eacf500b64292bfb73c49797c1958f6ee44646e43e868039ae7feb573ff75 jadx-headless-mcp-0.7.1-all.jar' | sha256sum -c -
else
echo '6e5eacf500b64292bfb73c49797c1958f6ee44646e43e868039ae7feb573ff75 jadx-headless-mcp-0.7.1-all.jar' | shasum -a 256 -c -
fi &&
export REA_JADX_MCP_JAR="$PWD/jadx-headless-mcp-0.7.1-all.jar"
```
On Windows, run the equivalent in PowerShell:
```powershell
$jar = Join-Path $HOME '.local\share\rea\android\jadx-headless-mcp-0.7.1-all.jar'
New-Item -ItemType Directory -Force (Split-Path $jar) | Out-Null
Invoke-WebRequest -Uri 'https://github.com/1013503897/jadx-headless-mcp/releases/download/v0.7.1/jadx-headless-mcp-0.7.1-all.jar' -OutFile $jar
if ((Get-FileHash -Algorithm SHA256 $jar).Hash.ToLowerInvariant() -ne '6e5eacf500b64292bfb73c49797c1958f6ee44646e43e868039ae7feb573ff75') { throw 'JADX JAR SHA-256 does not match REA pinned metadata' }
$env:REA_JADX_MCP_JAR = $jar
```
REA runs its packaged Java metadata bridge in source-file mode against the
unmodified engine JAR. REA does not install Java, an Android SDK, JADX, a device
service or an emulator. Set the path in the shell or MCP server environment:
```sh
export REA_JADX_MCP_JAR=/absolute/path/jadx-headless-mcp-0.7.1-all.jar
@@ -86,6 +120,7 @@ rea inspect-android-method /path/Example.apk example.MainActivity onCreate
rea inspect-android-method /path/Example.apk example.MainActivity select --overload-index 1
rea trace-android-references /path/Example.apk example.MainActivity
rea trace-android-references /path/Example.apk example.MainActivity --method-name onCreate
rea trace-android-references /path/Example.apk example.MainActivity --method-name select --overload-index 1
```
Equivalent MCP methods are `inspect_android_package`, `search_android_classes`,
@@ -106,9 +141,10 @@ Every request includes `path`. Class/method operations use `class_name`,
```
Use `inspect_android_class` to choose an overload. REA rejects an ambiguous
method request rather than silently selecting the first match. The engine's
method reference operation cannot select overloads, so REA reports that boundary
as unsupported when multiple same-name methods exist.
method request rather than silently selecting the first match. Method reference
queries accept the same `overload_index`; the bridge resolves the selected
method node and the result reports its index and displayed signature. These
indices are tied to the current artifact and engine, not stable across builds.
The engine's smali fallback also joins same-name overloads; REA rejects that
fallback for an overloaded method instead of attributing all bodies to one
@@ -137,14 +173,28 @@ the same source byte limit and explicit truncation reporting.
full reported display types, fields and inner class names. Synthetic members
that code generation hides can appear in the inventory; it stays consistent
after method decompilation within the retained session.
Exact DEX descriptors are unknown. Overload indices are tied to this artifact
and engine, not stable identities across different builds.
Exact DEX prototypes are reported from frozen JADX method metadata as
`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.
- 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.
- Reference results are incoming static relationships between provider nodes.
Source lines and DEX instruction offsets remain unknown. They are not observed
runtime calls.
`reference_unit=using_node` means `total` counts containing JADX nodes, not
individual invoke instructions; two calls from one method can appear as one
entry. `coverage=complete` covers this node inventory only. For a method
reference whose using node has raw code, `direct_invokes` lists matching DEX
invoke instructions with their DEX entry, raw source method ID, code-unit
offset, method index and opcode. An empty array means no direct invoke was
matched in that node; `null` means raw code was unavailable or the using node
was not a method. This list does not establish complete call-site coverage
outside JADX's using-node inventory or through reflection and dynamic loading.
A virtual or interface invoke also does not establish its runtime implementation.
Source lines and the containing node's own `dex_offset` remain unknown.
Method-reference results also carry the selected `dex_descriptor`;
class-reference results report `null`.
These relationships are not observed runtime calls.
- Original requests, APK SHA-256, actual engine JAR SHA-256 and raw producer
responses remain in Evidence. Source revision is populated only for bytes
matching the audited release JAR.
@@ -210,9 +260,11 @@ REA never writes to the original APK or launches target code.
On Windows, stdin writes preserve stream backpressure and run outside the Node
event loop. Cancellation terminates the retained job, including descendants,
before workspace cleanup. An abrupt CLI/server process termination closes the
job and stops Java, but cannot run JavaScript workspace cleanup; temporary
workspace files can remain after that forced exit.
before workspace cleanup. On Windows, a detached REA helper owns the private
workspace's native directory handle. An abrupt CLI/server owner termination
closes the Java job and the helper's input pipe; the helper then removes the
workspace by handle. The real Windows lane checks both effects. If the helper
also dies or the host loses power before it finishes, private files can remain.
If Java cannot start the bridge, REA reports the selected executable and retained
startup diagnostics. Select a full JDK through `JAVA_HOME` or `PATH`, and use
@@ -275,12 +327,20 @@ The original APK and host Java configuration remain unchanged.
The same lane runs on Windows x64 with the bundled native controls and a full
JDK. It checks CLI/MCP results, real MCP cancellation and disconnect, and Java
exit after abrupt CLI owner termination. Windows forced termination is checked
separately from the POSIX CLI SIGTERM handler; it does not claim a structured
cancellation result or workspace removal after the owner is forcibly killed.
exit and workspace removal after abrupt CLI owner termination. Windows forced
termination is checked separately from the POSIX CLI SIGTERM handler. A
synthetic two-owner test confirms that one owner's exit does not remove the
other owner's workspace. The lane does not claim a structured cancellation
result or power-loss recovery.
The download script verifies fixed SHA-256 values, reuses matching files and
refuses to overwrite a different existing file. APK/JAR files live under ignored
`_reference/` and are excluded from package contents. The lane exercises actual
REA CLI and MCP results with the real engine, separately from synthetic protocol
regressions. It needs no Gradle build, emulator, Android SDK or Ghidra.
Direct invoke sites retain exact DEX code-unit offsets separately from each
containing node's unknown `dex_offset`. `direct_invokes_exhaustive: false`
means instruction rows were bounded by the local bridge frame budget; `null`
means the producer did not establish instruction coverage. Using-node totals
and coverage do not imply complete instruction evidence.
+2
View File
@@ -21,6 +21,8 @@ Readiness is `rea inspect-apktool-client`: it runs `apktool --version` and
reports the resolved launcher, its selection source, and its version. A
missing launcher reports `capability_unavailable` with the
`REA_APKTOOL_COMMAND` remediation rather than attempting an installation.
`binary_session` uses the same host probe for tool availability; it does not
validate a particular APK or guarantee that the selected input can be decoded.
On Windows, a `.bat` or `.cmd` launcher (such as Chocolatey's `apktool.bat`)
runs through an explicit `cmd.exe /d /s /c` call. That call has no AutoRun,
+13 -1
View File
@@ -38,6 +38,10 @@ graph TD
JS_CODEC["src/javascript/sourceMaps/<br/>pinned upstream map codec<br/>owned heap-limited Node process, complete point evidence"]
INSPECTOR["src/inspector/<br/>passive V8 Inspector discovery and capture<br/>script and target locations"]
DOTNET["src/dotnet/<br/>Managed static metadata provider<br/>PE/CLI metadata inventory, members, IL"]
OBJDUMP["src/objdump/<br/>GNU objdump command profile"]
RIZIN["src/rizin/<br/>Static command profile and persistent debugger sessions"]
GDB["src/gdb/<br/>Persistent GDB/MI sessions<br/>owned process lifecycle and target state"]
FIRMWARE["src/firmware/<br/>Binwalk / Unblob adapters<br/>owned processes, report normalization, private extraction"]
ANDROID["src/android/<br/>Static APK adapter<br/>owned SDK stdio, pinned headless JADX, normalization"]
REFERENCE["src/reference/<br/>Filesystem reference-source reader<br/>bounded traversal, validation, errors"]
@@ -50,7 +54,7 @@ graph TD
end
subgraph "Contracts Layer"
CONTRACTS["src/contracts/<br/>Caller-visible schemas for analysis, managed, browser, Electron, application, and session tools"]
CONTRACTS["src/contracts/<br/>Caller-visible schemas for analysis, reverse engineering, managed, browser, Electron, application, and session tools"]
ERRORS["src/contracts/errorSchemas.ts<br/>Shared analysis error schemas<br/>CLI diagnostics, generated error catalog"]
end
@@ -122,6 +126,14 @@ graph TD
INSPECTOR --> DOMAIN
APP --> DOTNET
DOTNET --> DOMAIN
COMPOSITION --> OBJDUMP
OBJDUMP --> DOMAIN
COMPOSITION --> RIZIN
RIZIN --> DOMAIN
CLI --> GDB
SERVER --> GDB
GDB --> PROCESS
GDB --> DOMAIN
HOPPER --> HOPPER_BRIDGE
GHIDRA --> GHIDRA_BRIDGE
HOPPER --> PROCESS
+22 -2
View File
@@ -98,8 +98,10 @@ section, symbol, mitigation or linkage views. The default `complete` detail and
CLI output are unchanged.
Complete results have a 32 MiB input, 64 MiB reply, 1 MiB combined diagnostics
and 30-second owned command deadline. The reply budget applies to the decoder
record; the CLI/MCP Evidence envelope adds copies and encoding overhead. The
and 30-second owned command deadline. `REA_PWNTOOLS_MAX_INPUT_BYTES` raises the
input bound alone, up to 1 GiB; the reply and deadline bounds are unchanged. The
reply budget applies to the decoder record; the CLI/MCP Evidence envelope adds
copies and encoding overhead. The
pinned MCP SDK defaults to a 10 MiB receive buffer. Large complete results need
a caller-configured `StdioClientTransport({ maxBufferSize: ... })` and a matching
server `REA_MCP_MAX_RESPONSE_BYTES`. See [MCP delivery budgets](mcp-contracts.md#tool-results).
@@ -145,6 +147,24 @@ received signal alone does not establish its exact cause. Dynamic tags require
a complete DT_NULL inside PT_DYNAMIC; interpreter names and ranges end at the
first NUL, with any padding still represented by the original segment range.
## Ghidra process failures
When output from the failing Ghidra startup or operation reports
`java.lang.OutOfMemoryError`, REA returns `resource_constraint`. Java heap or GC
overhead exhaustion includes heap-specific recovery advice. Metaspace and array
size failures do not recommend increasing the heap; native-thread exhaustion is
reported as a capacity constraint with unknown limits.
`configured_jvm_max_heap` records the explicit `-Xmx` value selected by REA's
launch builder. It is a configuration fact, not measured memory use or proof of
the effective JVM limit; JVM environment options may affect that limit.
When a Windows script wrapper selects the JVM, this value remains unknown.
Historical stdout and stderr remain available, but classification uses only the
failing operation's `failure_output`. Output draining waits at most one second,
subject to earlier cancellation or the startup deadline. `output_incomplete`
marks a snapshot taken before stream closure; cleanup ownership remains with
the session until release succeeds.
## Portable PE resources
`inspect_pe_resources` / `inspect-pe-resources` inspects an explicit PE32 or
+174 -15
View File
@@ -18,8 +18,19 @@ Inspect native SAZ or HAR 1.2 offline with REA, or connect Everywhere's separate
- `inspect_web_page` / `rea inspect-web-page` captures DOM structure, accessibility nodes, scripts, resources, safe response/DOM metadata, attach-window network and console metadata, WebSocket frame sizes, workers, quota, and optionally selected storage key names, redacted storage-content fingerprints, or script sources.
- `analyze_web_bundle` / `rea analyze-web-bundle` parses selected script artifacts without execution and derives chunk edges, route and endpoint candidates, vendor fingerprints, static WebMCP declarations, and optionally source-map/original-source evidence.
- `observe_web_session` / `rea observe-web-session` arms an observation window of the requested duration for an external user action and records ordered reload, SPA navigation, redirect, failure, lifecycle, and target-termination metadata.
- `discover_webmcp_tools` / `rea discover-webmcp-tools` uses the experimental CDP WebMCP domain to return every in-scope registration and its complete observed `input_schema` declaration and canonical `input_schema_sha256` digest inline. Declarations are untrusted JSON data, including declared types, required fields, enums, examples and reference URLs. REA does not resolve schema references. These fields replace `input_schema_shape`. Registrations with the same name and URL in distinct observed frames retain separate identities. `tool_key` includes the observed frame ID; it is not a stable identity across navigation or browser sessions. Same-frame registration updates replace that owner only, and removal leaves other owners intact. REA never exposes `WebMCP.invokeTool`; malformed protocol payloads fail explicitly instead of returning incomplete tool metadata.
- `discover_webmcp_tools` / `rea discover-webmcp-tools` uses the experimental CDP WebMCP domain to return every in-scope registration and its complete observed `input_schema` declaration and canonical `input_schema_sha256` digest inline. Declarations are untrusted JSON data, including declared types, required fields, enums, examples and reference URLs. REA does not resolve schema references. These fields replace `input_schema_shape`. Registrations with the same name and URL in distinct observed frames retain separate identities. `tool_key` includes the observed frame ID; it is not a stable identity across navigation or browser sessions. Same-frame registration updates replace that owner only, and removal leaves other owners intact. Declared `readOnly`, `untrustedContent`, `consequential`, and `autosubmit` annotation hints are reported per registration: `true`/`false` are declared values, `null` means the declaration did not provide the hint in that capture, and a missing `consequential` key means the hint was not recorded, either in a historical capture or because the observed annotation was invalid. Consult discovery completeness and exclusions to distinguish these cases. Malformed annotation values retain the registration with incomplete coverage and an `invalid_protocol_value` exclusion. Other malformed protocol payloads can fail discovery explicitly. REA never exposes `WebMCP.invokeTool`.
- `compare_web_captures` / `rea compare-web-captures` compares passive captures or exact-step-aligned browser scenarios. Scenario results commit reproducible literal normalization and expose alignment failures plus artifact-level action, screenshot, DOM, accessibility, URL, history, storage, and event differences. Missing or truncated evidence is never treated as equivalence.
Passive DOM comparison checks complete node inventories and complete frame
parent/child structure independently. Frame IDs and sibling enumeration order
are capture-local and do not establish a change; duplicate IDs, missing parents,
and cyclic ancestry remain unknown. A complete frame difference can still prove
change when DOM nodes are incomplete, and vice versa. Partial DOM or metadata
collection digests do not prove modification: a missing node or link can result
from a protocol omission. Rejected DOM facts also mark derived URL and agent-hint
metadata incomplete, while valid facts from other nodes and documents remain
available in the capture.
- `capture_web_screenshot` / `rea capture-web-screenshot` returns a content-addressed visible-viewport PNG.
- `compare_web_screenshots` / `rea compare-web-screenshots` performs bounded local PNG pixel comparison without OCR or external services.
- The CLI comparison commands accept each capture, screenshot artifact or normalization policy as inline JSON or a JSON file path. Use files for screenshot artifacts and scenario captures, whose embedded PNG bytes can exceed the host's command-line length limit.
@@ -55,14 +66,25 @@ Electron `file://` pages use a separate provider and target boundary; see [elect
## Provider authorities
CDP page capture, V8 Inspector observation, and Playwright scenarios stay
separate providers because their authorities differ: CDP and V8 attach to an
already-running target and never launch, drive, evaluate, or mutate it, while
Playwright providers own and drive the runtime (launch browsers, run actions,
capture step snapshots). Merging them would mix attach-only and owned-process
lifecycles in one contract. What they share instead is one exclusion
vocabulary (`BrowserExclusionReason`): every denial reason in every stack maps
onto it, even where a wire schema keeps a narrower historical bucket.
CDP page capture and V8 Inspector observation attach to an already-running
target without launching it, driving actions, or evaluating target code.
Playwright scenarios drive actions and capture step snapshots in either a
provider-owned browser or a caller-selected external browser. Separate providers
preserve these authority and lifecycle differences. They share one exclusion
vocabulary (`BrowserExclusionReason`): every denial reason maps onto it, even
where a wire schema keeps a narrower historical bucket.
CDP attachment is not a guarantee that the browser preserves all state.
Chromium can clear another client's `navigator.platform` emulation when REA's
page session disconnects, even if that session sent no emulation commands.
This affects page inspection, bundle capture, session observation, screenshots,
WebMCP discovery, and Electron page inspection; target listing alone does not
attach a page session. REA cannot read the other client's complete
user-agent/client-hint configuration and does not replay guessed settings.
Use a disposable browser when preserving externally configured emulation is
required, or have the owning client reapply its complete configuration afterward.
See [the reproduction and Chromium ownership limitation](https://github.com/morluto/rea/issues/4089).
Existing static application Evidence and passive web/Electron captures can be
combined later through
[JavaScript static/runtime reconciliation](javascript-runtime-reconciliation.md).
@@ -80,12 +102,74 @@ google-chrome \
REA does not launch, own, or terminate this browser. Use a dedicated profile and stop it yourself when the investigation is complete. Do not expose the debugging port on a non-loopback interface.
Other Chromium-family browsers, such as Chromium or Brave, work the same way
with the same two flags. On macOS, run the executable inside the application
bundle, for example
`"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"` or
`"/Applications/Brave Browser.app/Contents/MacOS/Brave Browser"`. Fedora installs
Chromium as `chromium-browser`.
REA needs the browser's HTTP discovery endpoint on a loopback port. A browser
driven over `--remote-debugging-pipe` or a private channel, for example by an
automation SDK, exposes no such endpoint, so `list_browser_targets` has nothing
to attach to. Connection failures report `endpoint_unreachable`; a reachable
HTTP service that returns a non-200 or non-JSON discovery response reports
`invalid_endpoint_response`.
Reproduce that flow in a Chromium-family browser started with
`--remote-debugging-port`.
In a minimal Linux container, headed Chromium under Xvfb can keep running while
its DevTools port never opens. Install the GLES, EGL, and Mesa DRI libraries
(on Fedora: `libglvnd-gles`, `mesa-libEGL`, and `mesa-dri-drivers`), then
restart the browser.
On Linux, a reachable but locked desktop keyring can stall Chrome before its
first navigation. For a dedicated investigation profile, add
`--password-store=basic` to bypass that keyring. Empty or placeholder frame URLs
then describe an unloaded page; REA continues to require an allowed, committed
URL. See [the startup investigation](https://github.com/morluto/rea/issues/1384#issuecomment-6093781686).
### Browsers without HTTP discovery
Some browsers expose a loopback browser WebSocket but no `/json/version` or
`/json/list`. Chrome 144 and later can enable remote debugging on a running
browser from `chrome://inspect/#remote-debugging`; this mode is reported to
serve only the browser socket. Since Chrome 136, `--remote-debugging-port` is
ignored for the default profile, so this is the remaining way to inspect a page
in an existing profile.
For such a browser, pass the browser socket instead of the HTTP endpoint:
```bash
rea list-browser-targets ws://127.0.0.1:9222/devtools/browser/<id>
```
Chrome writes the port and the `/devtools/browser/<id>` path to the
`DevToolsActivePort` file in its user data directory. REA does not read that
file for you.
- Only `list_browser_targets`, `inspect_web_page`, `analyze_web_bundle`,
`observe_web_session`, `discover_webmcp_tools`, and `capture_web_screenshot`
accept a browser socket. Website runtime attribution, Electron inspection,
and scenario attach mode still require HTTP discovery.
- Each call opens one WebSocket and uses it for discovery
(`Browser.getVersion`, `Target.getTargets`) and for the selected page.
Discovery must finish within the same 10-second deadline as HTTP discovery.
Chrome may ask you to approve each connection.
- The browser identifier controls access to the socket. Evidence records the
endpoint as `ws://127.0.0.1:9222/devtools/browser/[REDACTED]`.
- `browser.revision` is the socket's `Browser.getVersion` value, such as
`@e533e98…`. HTTP discovery reports `WebKit-Version`, such as
`537.36 (@e533e98…)`.
- `Target.getTargets` reports whether each target is attached. Chrome's
`/json/list` can omit it, so HTTP discovery may report `attached` as `null`.
- An existing profile can hold many unrelated pages. Target listing returns
each eligible page's URL and title, so pass `--allowed-origins` to list only
the origins you are investigating.
A browser driven only through `--remote-debugging-pipe` or a vendor's private
automation channel has neither endpoint, and REA cannot attach to it.
## Request boundary
CDP connections enforce the shared 64 MiB limit per WebSocket message. A larger
@@ -95,9 +179,13 @@ decoded strings or JSON objects.
Discovery supplies a literal loopback CDP endpoint and returns all eligible
page targets unless an exact HTTP(S) origin filter narrows the result. Follow-up
inspection requests select one target; their default scope is its current origin. `localhost`, private-LAN
addresses, HTTPS CDP endpoints, credentials, paths, queries, fragments, and
implicit ports are not valid CDP endpoints. Origin filters contain only scheme,
inspection requests select one target; their default scope is its current origin.
The endpoint is either `http://127.0.0.1:PORT` or a
[browser socket](#browsers-without-http-discovery),
`ws://127.0.0.1:PORT/devtools/browser/ID`; `[::1]` may replace `127.0.0.1`.
`localhost`, private-LAN addresses, HTTPS and `wss:` endpoints, credentials,
queries, fragments, implicit ports, and any other path are not valid CDP
endpoints. Origin filters contain only scheme,
host, and port; paths, credentials, queries, fragments, and wildcards are not
valid origins.
@@ -124,6 +212,36 @@ rea discover-webmcp-tools http://127.0.0.1:9222 TARGET_ID \
--json
```
### WebMCP discovery prerequisites
`discover_webmcp_tools` reports only what the attached browser's experimental
`WebMCP` CDP domain exposes. When a browser rejects the unknown domain, the
result reports `status: "unavailable"`. Whether a page can register tools at
all depends on several conditions that are not interchangeable:
- A site's own origin-trial token can enable the API for that origin
**provided the trial is still active, the browser version and platform
support it, and user settings permit it** — token presence alone is not a
guarantee. The trial is announced for Chrome 149 and later.
- The `WebMCPTesting` feature — `--enable-features=WebMCPTesting` at launch, or
`chrome://flags/#enable-webmcp-testing` in a normal profile — enables the
API page-side for that browser instance, independent of per-site tokens.
In one pilot run, a site whose registration code was present but whose
served token was scoped for third-party use registered 0 tools under a
default launch and 4 under the flag; that the token did not enroll the
first-party document is consistent with this but is an inference, not a
measured Chrome behavior.
In Chrome 154 specifically (one tested version), `WebMCP.enable` answered
under a default launch — the debug domain was not flag-gated in that run.
Other versions may differ, so an unattributed zero-tool result should be
read as registration state only after domain availability is confirmed.
Discovery is attach-only: the command needs a reachable loopback CDP endpoint
and a target ID from `list_browser_targets`, and it never navigates, reloads,
or evaluates the page. Observe the page in the state you want to measure;
registered tools reflect the document as loaded.
Accessibility names/descriptions, console primitive text, JSON body shapes, WebSocket shapes, script content, and storage key names are selected independently:
```bash
@@ -174,9 +292,36 @@ REA retains local observation data and removes only URL userinfo credentials, ex
- Accessibility structure and roles are retained by default, while names and descriptions require `include_accessibility_text: true`.
- Network observations retain method, status, MIME type, size, type, initiator stack location, and complete URLs with userinfo credentials removed. Redirects reported by CDP are attached as ordered `redirects` on the one request ID, with the prior request URL, CDP response URL, method/type, response status/MIME/encoded length, request timestamp, and the next `requestWillBeSent` event timestamp. CDP delivers `redirectResponse` with that next request event, so the latter is an event boundary rather than an exact response timestamp. Redirect response headers and bodies are discarded; hops outside the selected origins are discarded with the request chain. Headers are discarded after an allowlisted projection of length/encoding, structured CSP/Link/policy fields, and untrusted agent hints. Cookies and authorization headers are never retained.
- Request/response bodies are not requested or parsed by default. When selected, only allowed-origin JSON media types are read, converted immediately to complete value-free typed paths, types and observation counts, and discarded. Each path segment is either `{ "kind": "property", "name": "raw property name" }` or `{ "kind": "array-element" }`; array-element types include primitives and nested arrays. Literal `*` properties remain distinct from array elements. `Network.getResponseBody` is never sent unless JSON body shape capture is selected. Comparison uses body shapes only when both captures selected them, and compares request/response shapes only where both observations provide those facts. Unavailable or truncated selected coverage makes equivalence unknown; observed differences still report changed.
For normalized passive inputs with complete request inventories on both sides,
request additions and removals remain observable even when selected body shapes,
response metadata, or WebSocket coverage are unavailable. HTTP request and
WebSocket inventory coverage are evaluated separately; complete HTTP coverage
cannot prove a missing WebSocket connection. Limited request inventory still
prevents request-absence claims. This comparison rule does not make live CDP's
attach-limited inventory complete or establish body-shape equality.
An unavailable HTTP resource type also leaves request identity incomplete:
observations with the same method and URL remain possible matches across types,
and identical unknown types cannot establish complete equality. Ambiguous type
groups cannot prove membership or content changes either: a missing type may
redistribute repeated requests between buckets without changing the requests.
Their comparison remains unknown, while independently identified groups retain
proven changes. Different methods/URLs and complete empty peer inventories still
support absence claims.
Relevant dropped-event counts also limit inventory completeness even when no
section is listed as truncated: HTTP uses `network_requests`, while WebSocket
uses `websocket_connections` and `websocket_frames`. Drops in other event kinds
and the aggregate total do not by themselves limit HTTP inventory. Proven
modifications remain reportable despite incomplete inventory.
Repeated identical HTTP content samples establish multiplicity changes only
with complete request inventories; limited inventories retain known content
comparisons without interpreting duplicate sample counts as changes.
WebSocket stream inventories and connection counts likewise require complete
WebSocket coverage. Redirect resource types use only shared known metadata:
an omitted type leaves equality unknown rather than proving a modification.
- Console observations with a stack source retain call type, argument types, timestamp, and source location. When console text capture is selected, already-delivered primitive values are retained verbatim; objects, getters, and remote properties are never expanded.
- WebSocket observations retain direction, opcode, and payload byte length. When shape capture is selected, text frames are classified as text or complete value-free JSON shape; binary bytes, hashes, prefixes, and raw frames are never retained.
- Storage observations always redact values. Key names, IndexedDB names, and cache names require `include_storage_keys`; stable content fingerprints require the additional `include_storage_fingerprints` selection. Cookie fingerprints cover cookies applicable to the current main-frame URL, including its path, rather than every cookie path on that origin. Other storage remains scoped to the origin. Cache bodies above 64 KiB and partial IndexedDB remote objects make fingerprint coverage incomplete, so identical observations remain `unknown`.
- Unavailable or malformed storage inventories mark `storage_keys` unavailable, even without fingerprints. Comparisons suppress inferred key additions and removals while preserving modifications proven by complete fingerprints on both sides. Previously exported captures are not retroactively corrected; recapture them to obtain these coverage markers.
- Script metadata is included only when CDP supplies a URL on an allowed origin. Stable keys exclude transient CDP script IDs, and exact transient raw URLs are used only during script/resource reconciliation. URL-less scripts are excluded because their origin cannot be established. When CDP supplies an execution-context association, the accepted script retains its authorized frame ID for later attribution. Script metadata admission bounds retained identity and source-map strings to 8 MiB and 50,000 records. When a source-map URL cannot fit, the script identity remains and source-map coverage reports `resource_budget_exhausted`; rejected script observations are counted without retaining their IDs. Source content is omitted unless explicitly requested and is returned inline as a self-verifying artifact with its SHA-256 digest.
Frames, resources, scripts, events, and workers outside the selected origin scope are excluded. An explicit `allowed_origins` list can include additional origins; excluded target details are counted without being exposed.
@@ -197,8 +342,22 @@ validation and resource bounds. Omitted script metadata makes its affected
section explicitly truncated.
Disallowed-origin entries are filtered before retention.
CDP discovery, connection, and commands wait for the peer or caller cancellation;
a stalled peer can leave an operation waiting. WebSocket messages remain subject
Resource-tree inventories distinguish a valid empty `resources` array from a
missing or malformed collection. Invalid resource entries are omitted with
incomplete coverage, while valid resources from that frame and other frames
remain available. Comparing an incomplete resource inventory cannot prove
addition, removal, or equality; a complete empty inventory can prove removal.
Resource records require a nonempty type and a string MIME type; absent or
malformed required fields are recorded as omissions instead of invented defaults.
An explicitly empty MIME type is valid. The optional content size may be absent;
an invalid supplied size remains unknown with incomplete resource coverage while
the resource's other facts are retained. Zero and nonnegative fractional sizes
are preserved.
CDP discovery has a 10-second deadline. Each WebSocket handshake and command
response has its own 20-second deadline, separate from the caller-selected
observation window. A stalled handshake or command reports `provider_timeout`;
caller cancellation still reports cancellation. WebSocket messages remain subject
to the [64 MiB limit](#request-boundary). REA does not impose a message-count or
pending-command quota. On cancellation, it closes only its own WebSocket,
waiting up to one second before terminating it. Malformed unsolicited events
@@ -207,7 +366,7 @@ commands fail closed. A correlated reply's session ID must match the command's
selected session. An optional method is unavailable only when the browser
rejects it as unknown; other command rejections retain their reported failure.
Network and console coverage starts only after REA attaches and enables the relevant CDP domains. `prior_activity_available` is always `false`; absence from these arrays is not evidence that an event never occurred. If CDP reuses a request ID without supplying redirect metadata, REA retains the predecessor observation and marks network coverage incomplete.
Network and console coverage starts only after REA attaches and enables the relevant CDP domains. `prior_activity_available` is always `false`; absence from these arrays is not evidence that an event never occurred. Scripts are different: scripts the page parsed before REA attached are still reported under `scripts`. A page that loaded before attach can therefore list its scripts while `network.requests` lacks the requests that fetched them. If CDP reuses a request ID without supplying redirect metadata, REA retains the predecessor observation and marks network coverage incomplete.
Source maps are not fetched by inspection. Bundle analysis fetches them only when `fetch_source_maps` is selected, with exact-origin redirect checks and no credentials, cookies, or referrer. Each fetch operation has a 30-second deadline and a 64 MiB aggregate streamed-response budget across redirects. A deadline or byte-limit failure remains explicitly unavailable and makes bundle completeness partial. Source-map decoding also bounds raw resolved identities to 64 MiB and aggregate inline evidence to 32 MiB. Failed maps preserve their failure context when it fits; if another context cannot fit, collection stops with the full requested count, the returned processed count, and an explicit unknown-results limitation. Retained mappings and original sources are returned inline. See [source-location limits](web-source-location.md) for the structural decoder bounds. Original source artifacts with `.ts`, `.tsx`, `.mts`, or `.cts` suffixes carry `text/typescript`; other source names retain `text/javascript`.
+106 -20
View File
@@ -15,11 +15,17 @@ there is no separate origin allowlist or request-interception layer. Environment
settings (including service workers blocked by default), empty storage, and a
final sanitized URL capture are supplied by default. Set
`environment.service_workers` to `allow` when the application requires them.
For attached browsers, `block` rejects captures when the selected page or its
frames are controlled by a service worker or already have a registration,
including after navigation. Existing caller registrations are never removed.
Navigation can run destination code before a newly discovered worker is rejected;
that rejection does not roll back browser effects. REA removes its owned
future-document scripts, restores live frame methods, then disconnects.
Add secret declarations, storage seeds, and additional artifact or event
capture only when needed. Action, secret, and storage counts are not capped.
Duration, action, and navigation
timeouts remain fixed provider-owned liveness limits; the request does not
accept a caller-controlled `limits` object.
Duration, action, and navigation timeouts remain fixed provider-owned liveness
limits. `capture.limits` controls retained event and network-content budgets;
it does not change those liveness deadlines.
## Minimal launch example
@@ -138,6 +144,24 @@ distinction: it closes and deletes provider-owned profiles, but only disconnects
from an external CDP browser. Real-browser verification checks both outcomes and
confirms that an attached external browser remains alive.
Keeping that browser alive does not guarantee its emulation state survives.
Chromium can clear caller-owned `navigator.platform` emulation when a page CDP
session disconnects, including sessions that sent no emulation commands. REA
cannot snapshot the other client's complete user-agent/client-hint settings and
does not replay a partial reconstruction. Use launch mode to keep this effect
inside a provider-owned browser, or have the external owning client reapply its
complete configuration after capture. Connected captures report this limitation;
see [issue #4089](https://github.com/morluto/rea/issues/4089).
`environment.locale` sets Intl formatting in both modes. Launch also sets
`navigator.language`, `navigator.languages`, and the request `Accept-Language`.
Connect preserves the caller's page and request languages; every attached
capture identifies this locale limitation, including retained partial
observations. Use launch mode when the application must select the requested
language through navigator values or request headers. Artifact completeness
covers the selected observations, not equivalence of launch and connect
language environments.
HTTP(S) URLs may include ordinary query values and fragments directly. Use
ordered structured query entries when a value needs to reference a declared
environment-backed secret. Form, storage, and cookie values are either literal
@@ -173,12 +197,44 @@ declared action. Each step reports action status, elapsed time, sanitized URLs,
event bounds, and independently typed capture states for screenshot, DOM,
accessibility, URL, history, and storage. Console, page-error, network,
WebSocket, frame, worker, popup, and cancelled-download events are attributed to
steps and all observed events and requested artifacts are returned inline.
There are no event, frame, worker, popup, WebSocket, DOM-node,
accessibility-node, storage-entry, screenshot-count, or cumulative
metadata-byte caps. Requested artifacts and observed events are returned
inline without application-defined size ceilings. Other missing sections
remain explicit and make the capture ineligible for equality claims.
steps. Retained events and requested artifacts are returned inline; omitted
events are counted by kind and identify affected network transactions.
Event and selected network-content retention have caller-selected limits under
`capture.limits`. Omitted values use the defaults below; every supplied value
must be a positive safe integer, and callers can raise or lower a default.
Byte limits are conservative allocation-cost estimates for retained event
values, including UTF-16 storage and JSON expansion, rather than serialized
bytes or a promise about exact process memory. `events.omissions.by_kind`
reports counts and reasons for omitted event kinds. Its
`network_transactions` rows identify affected transaction IDs and omitted
event kinds; `network_transaction_range` identifies the contiguous IDs beyond
the transaction limit. Omission metadata is bounded by the selected
transaction limit. Other missing sections remain explicit and make the capture
ineligible for equality claims.
The default limits are 10,000 events, 4 MiB estimated allocation for one
event, 16 MiB estimated allocation across retained events, 1,024 network
transactions, 64 KiB of body bytes per transaction phase, 1 MiB estimated
allocation for body redaction/base64, 1 MiB for retained header values, and
1 MiB for network metadata. The same limits are included in the capture
Evidence. Playwright materializes response bodies before REA can inspect their
byte length; `max_network_body_bytes` bounds subsequent retained copies and
does not bound the browser API read itself. A body that exceeds its selected
limit has an explicit `body-size-limit` or `body-allocation-limit` unavailable
state with the observed and allowed values.
Additional implementation bounds are 16 simultaneous network-content reads,
a 5-second deadline for each provider API read, and 2,048 live event-listener
owners. These are not caller-selectable. The concurrency bound queues admitted
content jobs and does not itself omit observations; a provider read timeout
produces a typed unavailable state, while exceeding listener capacity produces
event-kind omission counts. A preflight avoids body redaction and base64 copies
when the currently available event budget already rejects the body.
Concurrent reads may each pass that check before their events are retained;
their retained-copy work is bounded by 16 times the selected per-body
allocation limit. The Playwright response Buffer itself is materialized before
REA can apply that limit. Requested artifacts and retained events are returned
inline within the declared limits.
Attach-mode captures also declare the unavoidable pre-attach event gap.
Selected event families are observed on the root page and discovered descendant
@@ -249,8 +305,12 @@ Each observed request object receives a capture-scoped `transaction_id`.
Requests sharing a URL remain distinct. Redirects link to the preceding
transaction only when it was observed; a null predecessor does not establish
that no earlier hop existed. Request events require both transaction fields;
an absent ID is malformed capture data, while a null redirect predecessor is
an explicit unknown.
an absent ID is malformed capture data. `redirected_from_transaction_state`
records `root` when Playwright observed no predecessor and `unknown` when a
predecessor exists but was outside the capture. A linked predecessor is carried
by `redirected_from_transaction_id` with no state field. Older captures without
the state field retain the conservative meaning that a null predecessor is
unknown.
`scenario.network_content` records the resolved content selection. Comparing
captures with different selections reports network evidence as unknown with a
@@ -278,14 +338,21 @@ retained representation after redaction.
Only completed responses are read. At the end of the final step, observation
stops; requests still in flight emit `request-unfinished`, and their observed
responses have explicitly unavailable bodies. This cutoff does not wait for
long-lived streaming connections. Already-started content reads settle before
browser cleanup, with a provider-owned five-second timeout per read and
cancellation support. Failed or unexposed reads retain per-content states and make selected
event coverage incomplete. Body events can arrive during later steps or final
drain; event bounds describe receipt during the step, while the source event
reference establishes the transaction phase. Comparison attributes content to
its source event's step and excludes the sequence reference from normalization.
responses have explicitly unavailable bodies. Already-admitted content reads
drain before browser cleanup. At most 16 reads run at once; queued work is
bounded by the selected network-transaction limit and by successful admission
of its request or response event. Each provider API read has its own five-second
timeout once it starts, so queue wait adds to total drain and cleanup time; this
is not a five-second total capture cutoff. Cancellation interrupts active reads
and queued jobs produce cancelled states without calling their provider APIs.
This drain does not wait for long-lived unfinished streaming connections and
does not refetch unfinished response bodies. Failed or unexposed reads retain
per-content states and make selected event coverage incomplete. Body events can
arrive during later steps or final drain; event bounds describe receipt during
the step, while the source event reference establishes the transaction phase.
Comparison attributes content, like every other event of its transaction, to
the step that sent the request (the receipt step when that request was not
observed) and excludes the sequence reference from normalization.
Playwright scenario capture does not supply initiator stacks; passive CDP
observation retains its own initiator evidence.
@@ -300,7 +367,22 @@ an unchanged claim; observed differences may still prove a changed result.
The comparison records its full normalization commitment and SHA-256 digest.
Built-in rules exclude step elapsed time and event sequence/index fields,
compare screenshots by content digest, and compare DOM/accessibility captures
by normalized text. Optional caller rules are bounded exact-literal
by normalized text. Network events are grouped by transaction without their
capture-local transaction IDs, attributed to the step that sent the request,
and compared as an unordered collection after caller rules apply, because
concurrent responses arrive in any order and can land in a later step; a
redirect retains the complete predecessor ancestry, including each hop's own
request, response outcome, and selected network content. Each transaction's
capture-local IDs, sequence/index fields, and content source-sequence anchor
are removed. Caller rules normalize retained strings before hashing. The
`preserve_redirect_transaction_ancestry` built-in rule fingerprints each hop
as canonical SHA-256 of `{ events, redirected_from }`: `events` is the sorted
list of its normalized event SHA-256 digests, and `redirected_from` is its
predecessor fingerprint or null for a root. Selected transaction fingerprints
are sorted for the step's network comparison. Temporary numeric graph indices
are lookup references only and do not enter the final digest. Missing or cyclic
predecessor evidence yields unknown comparability instead of an unchanged claim.
Other events keep receipt order. Optional caller rules are bounded exact-literal
replacements over named artifact kinds. Rules and artifact-kind lists are
sorted canonically before application, making the same policy reproducible
regardless of input order. Rules operate only on already-redacted durable
@@ -318,3 +400,7 @@ selection. Captures with different selections report event evidence as unknown
with a recovery instruction, even when no selected family emitted an event.
Captures written before this field existed omit it, so their event evidence is
also unknown.
Network normalization also commits the removal of capture-local transaction IDs,
attribution to the requesting step, and unordered transaction comparison. These
built-in rule identities participate in the normalization digest.
+120 -13
View File
@@ -83,8 +83,21 @@ The shared CLI/MCP workflow reports compiler version, modules, replacements,
build settings, original bytes and SHA-256. See [Go binaries](go-binaries.md)
for supported containers and absent or malformed metadata.
Configure [Hopper or Ghidra](installation.md#hopper), or the
[IDA adapter](ida-provider.md), before analyzing a native target. Substitute
Inspect an embedded cargo-auditable Rust dependency list without installing
Rust or configuring a disassembler:
```bash
rea inspect-rust-binary /absolute/path/to/program --json
```
The same workflow reports package names, versions, sources, the root package,
dependency edges, and the section's file range. A missing `.dep-v0` section is
null metadata, not proof that the binary has no dependencies. See
[Rust binaries](rust-binaries.md).
Configure [Hopper or Ghidra](installation.md#hopper), the
[IDA adapter](ida-provider.md), or [Kuna](kuna-provider.md) before analyzing a
native target. Substitute
your target, search text and function name or address in these examples:
```bash
@@ -140,9 +153,13 @@ overrides it. In MCP, pass `provider_id` to `open_binary`:
The selected provider remains bound to the session until an explicit switch
or close. Provider failures are returned with their original reason. For an
`ambiguous` selection error, choose from `details.candidate_ids`; for
`provider_unavailable`, run `rea doctor --provider ID --json` to diagnose the
selected engine. See [task readiness](installation.md#check-readiness-for-your-task)
and [provider selection](mcp-contracts.md#identity-and-discovery).
`provider_unavailable`, follow `message`, `remediation.action`, and the original
reasons in `details.rejections`. For Hopper, Ghidra, IDA, and JADX, run
`rea doctor --provider ID --json` to diagnose the selected engine. Other adapters,
such as `pwntools-elf`, report their configuration requirements directly and are
not accepted by `doctor --provider`. See
[task readiness](installation.md#check-readiness-for-your-task) and
[provider selection](mcp-contracts.md#identity-and-discovery).
The session reports work still in progress through `analysis_activity`.
A client timeout can end its wait while the provider continues analyzing.
@@ -151,6 +168,12 @@ be verified. See [MCP contracts](mcp-contracts.md) for session lifecycle and
[Ghidra first-query deadlines](mcp-contracts.md#ghidra-first-query-deadlines-and-recovery)
for import, client timeouts and recovery.
If one-shot analysis fails and closing its session also fails, the original
error details remain inline and `details.cleanup_error` describes the later
close failure. Each diagnostic retains its own reported resources; the later
failure does not establish whether an earlier resource was released. Captured
output and partial observations remain in the original details.
## Save and reuse analysis snapshots
A snapshot retains successful analysis results for later queries. REA reuses
@@ -174,7 +197,9 @@ rea analyze /absolute/path/to/program --provider ghidra --snapshot /absolute/pat
rea analyze /absolute/path/to/program --provider ghidra --snapshot /absolute/path/to/analysis/program.json
```
An exact CLI cache hit is read before starting a provider process.
An exact CLI cache hit is normally read before starting a provider process.
On Windows, Ghidra first admits the selected source through its native snapshot
owner, so replay can start that provider.
In MCP, `open_binary` accepts `snapshot_path` to import a snapshot atomically
for its matching target; an MCP provider may still start before returning a
cached result. `close_binary` accepts `snapshot_path` and optional
@@ -212,8 +237,36 @@ Import an older source tree to compare with the current artifact:
```bash
rea import-reference-source /absolute/path/to/source
rea import-reference-source /absolute/path/to/source --detail summary --outline-depth 2 --json
```
The default (`--detail full`) returns the complete `HistoricalSourceGraph`.
Choose `--detail summary` for a `HistoricalSourceSummary` of that same committed
inventory: its `HistoricalSourceManifest` contains the full graph and root
digests, with provenance, VCS facts, languages, manifest paths, entry and
relationship counts, parse-failure counts, file/directory states, exclusions
and limitations. The summary is a presentation view, not a graph accepted by
source-to-bundle comparison; keep the full output for that workflow.
`--outline-depth` requires `--detail summary` and accepts a nonnegative safe
integer. Depth 1 shows root children, depth 2 (the default) also shows their
children, and depth 0 omits all outline entries. Paths retain canonical Unicode
code-point order. `outline.omitted_entry_count` counts only inventoried entries
hidden by the chosen depth; it does not count unexamined children of excluded or
unreadable directories, or change `inventory_state`. Exclusions and source
limitations remain separate from this presentation omission.
`file_sizes.known_bytes` is the exact sum of known inventoried file sizes as
decimal text, so large sums cannot lose integer precision. Unknown sizes count
in `file_sizes.unknown_file_count`; excluded or unexamined paths are not included
in that total. A zero known-byte sum therefore does not establish an empty source
tree. Full output retains individual file hashes, relationships and parse-failure
diagnostics. Summary mode performs the same import and cleanup, then projects
the committed result without rereading source files. It reduces the selected
output, not the amount of source examined. A shallow tree or many exclusions can
still produce a large summary; depth 0 removes the outline but preserves the
other facts. No fixed output-size or runtime improvement is promised.
The import records hashes and metadata for the supplied files, separately
from observations of the current app. For a directory inside a Git checkout,
it records the HEAD of the nearest enclosing repository; whether the working
@@ -221,7 +274,12 @@ tree matches that commit remains unknown. It honors the source tree's `.gitignor
and applies default exclusions for common generated and dependency paths such
as `node_modules/`, `dist/`, and `*.log`. Pattern-based exclusions record the
matched pattern and whether it came from project, default, caller, or sensitive
path policy. Set `REA_REFERENCE_SECRET_PATTERNS_JSON` to a JSON array of ignore
path policy. Links into excluded paths retain their internal target metadata;
the exclusion explains why the target has no inventory entry, and the inventory
remains partial. Exclusions record `entry_kind` when the importer observed it;
older saved exclusions without that field have unknown kind. An excluded
ancestor proves an internal descendant only when its kind is `directory`.
Set `REA_REFERENCE_SECRET_PATTERNS_JSON` to a JSON array of ignore
patterns when you want to mark selected paths as sensitive; those patterns are
reported separately as configured-secret exclusions.
@@ -245,6 +303,7 @@ Choose the guide for the target and intended interaction:
- [Browser observation](browser-observation.md): inspect a selected page through CDP.
- [Browser scenarios](browser-scenario-contract.md): run declared interactions and capture their results.
- [Electron observation](electron-observation.md): inspect an Electron renderer or capture an application scenario.
- [Electron fuses](electron-fuses.md): read packaged fuse bytes before expecting `--inspect` to reach an Electron main process.
- [Node/Electron Inspector](javascript-runtime-observation.md): record script locations and execution contexts.
- [Process capture](process-capture.md): run an executable and compare terminal, exit and filesystem observations.
@@ -253,17 +312,65 @@ user permissions; consult the chosen guide for host requirements and effects.
## Output and exit status
`rea mcp doctor --json` checks production MCP startup and reports child logs in
`diagnostics`. It drains stderr while retaining at most its first 64 KiB of raw
bytes as text, omitting a final UTF-8 code point if it crosses that byte limit.
`stderr_bytes` counts all observed bytes, including omitted output;
`stderr_truncated` indicates that output exceeded the retention limit. The same
diagnostics appear inside the report when `--full-output` is selected. Log
truncation alone does not change the diagnostic health or exit status.
The default terminal format is TOON. Use `--json` when saving results for a
JSON consumer. `--json` is indented on a terminal and compact when piped or
JSON consumer. Valued output flags (`--format`, `--filter-output`,
`--token-limit`, `--token-offset`) accept `--flag value` or `--flag=value`; a
missing value fails with `MISSING_OUTPUT_VALUE` before the command runs. `--json` is indented on a terminal and compact when piped or
redirected, which keeps large results smaller for agents and scripts; pipe
through `jq .` for indented files. Output selection and formatting do not change
operation status.
| Status | Meaning |
| --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| `0` | The operation completed. Its result may include partial evidence, warnings or unresolved questions. |
| `1` | The operation could not complete. Structured output identifies invalid input, permissions, cancellation, timeouts or another failure when available. |
| `128 + N` | Signal `N` ended the process, where the shell or runtime preserves the conventional signal-derived status. |
For `cutter-command`, `--json-output` requests command-specific JSON from
Cutter when available. This is separate from `--format json`, which selects the
CLI result representation; the global `--json` flag selects that same CLI
representation.
Cutter still accepts deprecated `--json=true` and `--json=false` compatibility
values. When both forms are present, `--json-output` takes precedence in either
argument order. Bare `--json` retains its global meaning of selecting JSON CLI
output formatting.
| Status | Meaning |
| --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `0` | The operation completed. Its result may include partial evidence, warnings or unresolved questions. |
| `1` | The operation could not complete. Structured output identifies invalid input, permissions, cancellation, timeouts or another failure when available. |
| `128 + N` | Signal-derived status. An unhandled signal may have ended the process, with the shell or runtime preserving the conventional status; a command may instead have handled the signal, waited for its operation and cleanup to settle, and assigned the same status (`130` for SIGINT, `143` for SIGTERM). In the handled case, it may also have printed a structured `cancelled` result. |
A `cancelled` result does not by itself determine the exit status; it depends on
the command's signal handling. Commands routed through the shared
`withCommandCancellation` handler in the CLI set status `130` for SIGINT or
`143` for SIGTERM after their operation settles. Current source registrations
use that handler for process capture and comparison (`capture-process`,
`compare-process-captures`), JavaScript application analysis (`analyze` when it
selects the JavaScript lane, and `analyze-javascript-application`), selected
browser, web and Electron commands (including `analyze-web-bundle`,
`observe-web-execution`, `capture-browser-scenario` and
`capture-electron-scenario`), and the platform and provider command families
for Android/ADB, Apktool/JEB/Flutter, firmware, PCAP and signal analysis,
WASM/EVM, Go/Rust and binary diagnostics, Frida instrumentation, the Cutter
command, objdump, Rizin and GDB. In these paths, a returned structured
`cancelled` result can be printed before the wrapper assigns the
signal-derived status. This describes the current source routing; it is not a
claim that every command or provider runtime has been independently exercised.
When the reader of piped output closes early, as `rea ... | head` does, the
CLI stops writing, prints no error and exits `141` (`128 + 13`, the status of a
SIGPIPE-terminated writer). The operation itself may have completed; only its
output was declined.
`inspect-artifact` also returns a structured cancelled result, but its direct
analysis cancellation path does not set a signal-derived exit status, so it
exits `1`. Setup has its own contract: cancellation exits `0`. Treat the
structured result and process status as separate signals when scripting
cancellation.
`setup --dry-run` returns `planned` and exits `0`; a cancelled setup also exits
`0`. Setup returns `1` for `needs_confirmation` or `needs_human`.
+161 -5
View File
@@ -33,6 +33,71 @@ schema constraints; REA still validates complete canonical inputs. Full-profile
`gpt-5.4` metadata is also verified locally. Live model API acceptance and
Windows remain unverified; see [native client verification](testing.md).
## Claude Desktop
Setup registers `mcpServers.rea` in `claude_desktop_config.json`: under
`~/Library/Application Support/Claude` on macOS, under
`$XDG_CONFIG_HOME/Claude` (default `~/.config/Claude`) on Linux, and on Windows
in the file the installed app reads:
- Without Claude Desktop's MSIX package storage, setup uses
`%APPDATA%\Claude\claude_desktop_config.json`.
- The MSIX build (package family `Claude_pzs8sxrjxfjjc`, installed under
`C:\Program Files\WindowsApps\Claude_<version>_x64__pzs8sxrjxfjjc`)
virtualizes AppData writes. Once it has saved its configuration, it reads
the package-private copy
`%LOCALAPPDATA%\Packages\Claude_pzs8sxrjxfjjc\LocalCache\Roaming\Claude\claude_desktop_config.json`
and ignores the `%APPDATA%` file. Setup, doctor and uninstall use that
private copy when it exists. Before the package has saved one, they use the
`%APPDATA%` file it still reads; with neither file, setup creates the
private copy.
- If `%LOCALAPPDATA%\Packages` holds a `Claude_<publisher ID>` package from
another publisher, or cannot be read, REA cannot establish which file Claude
Desktop reads. Setup, doctor and uninstall report that instead of falling
back to `%APPDATA%`. Doctor then reports `claude_desktop` as invalid, and
uninstall stops before removing anything for any client.
Quit Claude Desktop fully before approving setup; closing its window is not
enough. A running Claude Desktop can rewrite its configuration from memory and
drop the registration. This was observed with the Windows MSIX build. Start
Claude Desktop again after setup. If the package saves its private copy after
setup planned to write the `%APPDATA%` file, setup makes no change and asks you
to rerun it.
A process that the MSIX app started, such as a terminal in Claude Desktop's
Code tab, sees the package's private files at their `%APPDATA%` paths even
though it reports no package identity. Setup compares one of those files at
both paths. When it finds that redirected view, the app is running, so setup
and uninstall refuse to change Claude Desktop's configuration; rerun them from
a terminal that Claude Desktop did not start. If the package has no private
file to compare, setup and uninstall cannot rule that out and also refuse; open
Claude Desktop once if it has never run, quit it fully, then rerun. Doctor
reads the same file from either kind of process.
The same process also writes everything under `%APPDATA%` and
`%LOCALAPPDATA%` into the package's private storage, where other apps never
read it. From that redirected view, setup and uninstall refuse to change other
clients' files there (VS Code, Trae, Devin and Hermes) and skill directories
there. `npm install -g` run from such a process installs into the package's
`LocalCache\Roaming\npm`, which other processes cannot see at the `%APPDATA%`
path. Setup refuses to register a launcher stored in any MSIX package's
private storage. Install REA from an ordinary terminal and rerun setup there.
Uninstall removes REA's entry from both the private copy and the `%APPDATA%`
file, including registrations that earlier releases wrote to `%APPDATA%`.
REA recognizes only the MSIX package. A non-MSIX Claude Desktop installed
beside it, which would read the `%APPDATA%` file, is not detected.
The package identity, both file locations, the redirected view, and these
setup and doctor results were observed on Windows 11 with the MSIX build
2.31226.1.0, using read-only `doctor` and `setup --dry-run` runs both inside
and outside the app. On that host, setup run from an ordinary terminal with
the app quit wrote the package-private copy; after the app restarted, the
entry was still present, doctor reported it aligned, and Claude Desktop's MCP
log showed REA starting and answering `tools/list`. Uninstall against the real
app is unverified.
## Claude Code
Claude Code normally defers MCP tool definitions through tool search. It
@@ -64,6 +129,51 @@ and Claude Code reports that autocompact is thrashing. To avoid this:
The sizes above come from REA's own `tools/list`. A live Claude Code session
against DeepSeek's API is unverified.
For a client whose stdio reader rejects the complete catalog frame, add
`"REA_MCP_OUTPUT_SCHEMA_PROFILE": "off"` to REA's `env` object and restart the
client. This omits only advertised output schemas; REA still validates complete
structured results against its canonical contracts. See [MCP output schema
profiles](mcp-contracts.md#output-schemas).
## Kimi Code
Setup registers `rea` in `~/.kimi-code/mcp.json`, or
`$KIMI_CODE_HOME/mcp.json` when set, while preserving unrelated settings and
creating a `.rea.backup` before updating an existing file. Kimi Code infers
stdio from the `command` entry, so setup does not add a `type` field. New
registrations include `startupTimeoutMs = 30000` and a `toolTimeoutMs` derived
from REA's known provider allowance plus 120 seconds of operation
headroom. The default is 450000 ms, covering the default 330-second Ghidra
startup; an explicit Ghidra startup timeout of 2700000 ms produces a 2820000 ms
tool timeout. The startup allowance follows an explicitly selected Ghidra or
Hopper provider, and otherwise uses the larger known Ghidra/Hopper deadline.
An explicit `REA_JEB_MCP_TIMEOUT_MS` request budget also contributes to that
maximum. For `ida` or `auto` selection, a valid IDA registration's parsed
`timeoutMs` also contributes to that maximum; its default is 300000 ms.
Setup forwards these explicitly selected REA deadlines and the input schema
profile into the registration; unrelated process values are omitted.
This headroom is a practical default, not a guarantee for every analysis
duration. Setup preserves positive timeout values already in the `rea` entry.
You can edit `startupTimeoutMs` and `toolTimeoutMs` there; doctor keeps positive
caller-selected values aligned and reports a non-blocking advisory when a tool
timeout is shorter than the known provider allowance.
Setup selects the compact input-schema advertisement for new or refreshed Kimi
registrations unless their stored configuration or setup environment explicitly
selects `REA_MCP_INPUT_SCHEMA_PROFILE=full` or `compact`. This keeps REA's
canonical tool validation unchanged while avoiding a Moonshot Kimi Code 2.2.0
failure observed with the full catalog: its request was rejected because a
single tool schema exceeded the provider's size limit. The limit is
provider-specific and not an MCP protocol limit. `rea doctor --client kimi`
reports an older registration with no explicit profile as stale. To restore the
compact profile, run `rea setup --client kimi` or add
`REA_MCP_INPUT_SCHEMA_PROFILE=compact` to the registration's `env`, then restart
Kimi Code. An explicit `full` profile remains respected and can still exceed
Moonshot's limit.
Kimi Code also discovers personal skills in `~/.agents/skills` and project-local
`.agents/skills`, so no separate skill root is required; setup still installs
and verifies the matching skill files through the normal shared-skill flow.
Start a new Kimi Code session to pick up MCP and skill changes.
## Pi
For [Pi](https://github.com/earendil-works/pi/blob/main/packages/coding-agent/docs/mcp.md),
@@ -124,8 +234,20 @@ standalone CLI check.
## Codex and Devin
Codex treats an unset or empty `CODEX_HOME` as `~/.codex`. Devin honors
`XDG_CONFIG_HOME` for its `devin/mcp_config.json` on non-Windows systems.
Codex treats an unset or empty `CODEX_HOME` as `~/.codex`. Its MCP server
registration supports `startup_timeout_sec` for connection initialization and
`tool_timeout_sec` for each tool call. New REA registrations use a 30-second
startup timeout and a tool timeout derived from the known provider
allowance plus 120 seconds of operation headroom. This yields 450 seconds for
the default Ghidra allowance; an explicit Ghidra startup timeout of 2700
seconds yields 2820 seconds. Setup preserves positive caller-selected timeout
values in the REA entry, and doctor reports a non-blocking advisory when a tool
timeout is shorter than the known provider allowance. An explicitly selected
JEB request budget also contributes to the allowance. A configured IDA
registration's parsed `timeoutMs` contributes for `ida` or `auto` selection.
These values are client-side deadlines, so increase them in `~/.codex/config.toml` if a
workload needs more time. Devin honors `XDG_CONFIG_HOME` for its
`devin/mcp_config.json` on non-Windows systems.
## OMP
@@ -165,6 +287,14 @@ For [Qoder](https://docs.qoder.com/cli/mcp-reference), setup writes
from `~/.qoder/skills` (following `QODER_CONFIG_DIR` when set), so setup
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.
## Trae
For [Trae](https://docs.trae.ai/ide/add-mcp-servers), setup writes
@@ -180,6 +310,21 @@ The user-level MCP paths follow the editor's VS Code layout. Trae's official
documentation describes the raw JSON editor without specifying those paths;
loading this registration in Trae remains unverified.
## WorkBuddy
For [WorkBuddy](https://www.workbuddy.cn/docs/workbuddy/From-Beginner-to-Expert-Guide/Function-Description/MCP-Guide), setup writes `mcpServers.rea` into the
global `~/.workbuddy/mcp.json` (a standard `mcpServers` JSON file at the user
home root on every platform). Entries carry `command`, `args`, and `env`;
stdio is the default transport. WorkBuddy discovers personal skills from
`~/.workbuddy/skills` (`%userprofile%\.workbuddy\skills` on Windows), so
setup installs its skill copy there.
The global MCP path is a plain JSON file; setup preserves every other server
and top-level key when it upserts `rea`. WorkBuddy also reads a project-level
`<project>/.workbuddy/mcp.json`, which setup does not touch: it registers the
user-level file only. Loading this registration and real REA tool calls
through WorkBuddy are verified.
## ZCode
For [ZCode](https://zcode.z.ai/en/docs/mcp-services), setup registers `rea` in the nested `mcp.servers` table of the
@@ -194,7 +339,11 @@ the first native server into an empty table, it copies the fallback's
`mcpServers` entries into the native table so they keep loading, and leaves
the fallback file untouched. Existing REA environment overrides are preserved
when refreshing its command. A malformed or unreadable fallback file fails setup instead of
being silently suppressed; repair or remove it and rerun. ZCode disables a
being silently suppressed; repair or remove it and rerun. Uninstall removes an
REA-owned `rea` entry from both the native table and the fallback, backing up
each changed file, so an emptied native table cannot load a leftover fallback
registration; other fallback servers, settings and a foreign `rea` entry are
kept. ZCode disables a
server with `enable: false` on its entry; doctor reports such a registration
as stale, and rerunning setup removes the flag to repair it. Native configuration
uses strict JSON; setup refuses comments, trailing commas and a BOM rather than
@@ -209,8 +358,15 @@ replaces) the REA entry in the home patch `$DSH_HOME/cordis.patch.yml`
patches and comments. `rea doctor --client dsh` checks that same entry id.
`rea uninstall` removes it by scanning supported clients; uninstall takes no
`--client` flag. The home patch applies to every Harness profile, so the
registration is written once and no per-profile edit is needed. The written
entry is:
registration is written once and no per-profile edit is needed.
Setup, doctor and uninstall resolve `DSH_HOME` and `DSH_AGENTS_HOME` as Harness
does: unset, empty or whitespace-only values use `~/.dsh` and `~/.agents`,
respectively. Bare `~`, `~/` and `~\` expand to the user home; other relative
values resolve against the command's working directory. Nonblank values retain
their original spaces, and the agents root is independent of the Harness home.
Run REA and Harness from the same working directory when using relative overrides.
The written entry is:
```yaml
- insert:
+55 -7
View File
@@ -6,6 +6,28 @@ and provider lanes. Each record below states its host, client version, model
fixture, and remaining coverage; a native-client run with a loopback model does
not establish live model-service acceptance.
## Published Pi code-mode renderer limit
A separate probe of the published `@earendil-works/pi-codemode` 1.1.0 and 1.2.0
declaration renderers rendered the full `compare_web_captures` input as
`args: unknown`: its generated TypeScript type exceeds Pi's 16,000-character
default. REA keeps the full advertisement's fields by inlining references when
the shared form would exceed Pi's 32-reference expansion limit; retaining that
shared form would leave later fields unknown instead. The existing
`REA_MCP_INPUT_SCHEMA_PROFILE=compact` profile lets callers stay below
its character limit by advertising reduced nested validation structure, while
REA continues to validate calls against the complete canonical input schema.
Compact applies to the server's advertised inputs and is not an automatic
full-profile fallback; see the [compact input schema profile](mcp-contracts.md#compact-input-schema-profile)
for details of its reduced forms.
Custom renderer hosts can raise `inputMaxChars` through `renderToolSignature` or
`renderToolSample`; `renderDeclarations` does not expose that override. The
probe exercised the published renderer code, not a native Pi host or model, and
does not establish broader Pi compatibility. See the pinned upstream
[`declarations.ts`](https://github.com/earendil-works/pi/blob/b45e0ebcc5ae2fc42a6f2f23104d10e656e6bedb/packages/codemode/src/declarations.ts)
and [renderer usage notes](https://github.com/earendil-works/pi/blob/b45e0ebcc5ae2fc42a6f2f23104d10e656e6bedb/packages/codemode/README.md#declarations-for-the-model).
## End-to-end, integration and golden evidence
The optional `verify:qwen-client`, `verify:pi-client`, and `verify:hermes-client`
@@ -18,14 +40,26 @@ pass `-- chat` to check plain chat with REA enabled. Qwen also reads the full
result that its client offloads, and Pi exercises default codemode execution.
Use `REA_VERIFY_RUNTIME_ROOT` to select a production-only installed REA package.
Receipts retain client versions, result digests, host coverage and owned-process
cleanup. These POSIX lanes use a deterministic loopback model, so they do not
prove live model-provider or native Windows compatibility. Qwen, Pi and Hermes
cleanup. These lanes use a deterministic loopback model and do not prove live
model-provider compatibility. Pi and Hermes remain POSIX-only. Qwen, Pi and Hermes
bind both `HOME` and `USERPROFILE` to the disposable account. Qwen and Pi discover
the shared personal skill through their default search paths without an explicit
skill setting; Hermes discovers it through the selected `HERMES_HOME`.
Set `REA_VERIFY_HERMES_STICKY_PROFILE=1` to exercise native Hermes selection of
a named sticky profile with `HERMES_HOME` still pointing to its root.
The Qwen lane also supports native Windows (verified with Qwen Code 0.25.1 and
Node 24.19.0 for both `chat` and `call`). With Node 24.18.0, the same `call`
journey reached full Evidence readback but the client aborted during exit with
`UV_HANDLE_CLOSING`; that run remains a failure, not a compatibility claim.
Select the npm install's
`node_modules/@qwen-code/qwen-code/cli-entry.js` with `REA_VERIFY_QWEN_COMMAND`,
because `execFile` cannot launch the `qwen.cmd` shim directly. A selected `.js`,
`.mjs` or `.cjs` entry point runs under the verifier's Node. Windows path checks
use native separators; persisted output is read through Qwen's real shell tool
with a shell-independent encoded Node payload. Windows receipts report process
lineage as unavailable when the verifier has no retained native Job Object lease.
`verify:gemini-client` requires an installed Gemini CLI (verified with
`@google/gemini-cli@0.63.0` on POSIX and native Windows); select it with
`REA_VERIFY_GEMINI_COMMAND`. A selected `.js`, `.mjs` or `.cjs` entry point runs
@@ -365,6 +399,11 @@ Such a stall leaves the initial page unloaded with empty or placeholder CDP URLs
it does not establish permission to capture that page. See
[the keyring diagnosis](https://github.com/morluto/rea/issues/1384#issuecomment-6093781686).
On Windows, browser verifier lanes that launch an owned fixture require a
standard, non-elevated caller token. They inspect the verifier caller before
allocating fixture resources and refuse elevated or unobservable tokens; they do
not change caller authority or claim to inspect the browser process token.
`verify:browser` also captures a source-owned noise canvas as a real PNG above
8 MiB through the CLI and stdio MCP, with complete byte/digest parity and real PNG
decoding. Its SDK client explicitly permits the larger inline JSON response;
@@ -446,7 +485,7 @@ artifact path. Its `expectedFirstTool` is `null`, and
`targetClarificationPassed` grades that limited routing outcome independently
of analysis prose heuristics. Analyzing a nearby example cannot pass this case.
The packaged desktop and parser-comparison scenarios now assess a closed set of
The packaged desktop, parser-comparison, and managed scenarios assess a closed set of
known fixture claims. Each final answer must be a strict JSON manifest containing
exactly the requested claim IDs, values, Evidence IDs, and Evidence authority and
confidence. Golden values are not included in the prompt. The evaluator checks
@@ -463,15 +502,24 @@ analyses and use the requested module and export selectors. Boundary tests packa
the same source-owned desktop fixture and compare the same source-owned parser
files through actual MCP tool results, then verify that fabricated answers fail.
The managed rubric checks the exact public top-level type set and the CLI entry
point's declaring type and method name. The exact type-set claim requires an
unfiltered member inventory: `selection` must be null and the Evidence parameters
must omit `type_names`. A selected inventory can still ground the entry-point
claim. Both inspections must describe the freshly built DLL's SHA-256. Entry-point
resolution additionally requires complete artifact and member metadata, matching
non-null module MVIDs, and a unique method at the header's metadata token. These are
static metadata claims, not runtime behavior;
compiler-generated member counts, token values, and MVIDs are not golden answers.
Per-scenario `factualCorrectness` is `passed`, `failed`, or `not_assessed` within
the `configured_fixture_claims` scope. A pass establishes only the configured
claims, not unrestricted factual correctness. Native, managed, browser,
claims, not unrestricted factual correctness. Native, browser,
navigation-context, and address-context scenarios have no factual rubric and
remain `not_assessed`; the managed workflow requires both artifact and member
inspection to answer its type and entry-point question.
remain `not_assessed`.
The closed factual scenarios request complete producer Evidence on the initial
analysis so their source selectors can authenticate all configured facts.
inspection or analysis so their source selectors can authenticate all configured facts.
Summary/page/item workflows are exercised separately by `javascript-module-view`;
its heuristic gate does not establish full-producer factual correctness.
+87
View File
@@ -0,0 +1,87 @@
# Electron fuse state
`inspect_electron_fuses` / `inspect-electron-fuses` answers whether a packaged
Electron app will honor `--inspect` and `--inspect-brk`, `ELECTRON_RUN_AS_NODE`,
embedded ASAR integrity validation, and loading application code only from
`app.asar`. It reads the fuse wire stored in the Electron binary. It does not
launch the app, modify fuse bytes, or use the network.
```bash
rea inspect-electron-fuses /Applications/Example.app --json
```
The CLI resolves a relative path against the caller's working directory. MCP
requires an absolute filesystem path and preserves that exact selection:
```json
{
"name": "inspect_electron_fuses",
"arguments": { "path": "/Applications/Example.app" }
}
```
On POSIX hosts, the CLI preserves path components so symlinks are followed
before parent (`..`) steps. Bundle selections may end in `.` or parent steps
through real directories; a terminal parent step through a directory symlink
is rejected. Windows CLI paths retain native drive and separator resolution.
Check this before `observe_javascript_runtime` with `electron-main`. When
`EnableNodeCliInspectArguments` is disabled or removed, Electron ignores
Inspector flags, and a later target listing can simply show no main-process
target.
## What is read
On macOS, pass the `.app` bundle or the Electron Framework binary. Bundle and
framework selection first tries the top-level `Electron Framework` binary,
then `Versions/Current/Electron Framework`, then
`Versions/A/Electron Framework`. Symlinks are followed only while they stay
inside that framework.
On Windows and Linux, pass the main Electron executable. The wire lives in
that executable rather than in `app.asar`.
The inline Evidence result contains:
- the selected path, the resolved regular file, its SHA-256, size, and container
format (`mach-o`, `mach-o-universal`, `pe`, or `elf`);
- one slice entry for a thin Mach-O, PE, or ELF file, and one entry per slice of
a universal Mach-O, with file-absolute `sentinel_offset` values;
- `wire_version`, `declared_length`, and ordered fuses `{ index, name, state, raw_byte }`;
- `sentinel_count`, including zero and more than one;
- `runtime_implications`, each marked `relationship: "derivation"` and tied to
the fuse index it came from.
Version 1 names, in index order, are `RunAsNode`, `EnableCookieEncryption`,
`EnableNodeOptionsEnvironmentVariable`, `EnableNodeCliInspectArguments`,
`EnableEmbeddedAsarIntegrityValidation`, `OnlyLoadAppFromAsar`,
`LoadBrowserProcessSpecificV8Snapshot`, `GrantFileProtocolExtraPrivileges`,
and `WasmTrapHandlers`. An index past that table, or any index on another wire
version, keeps `name: null` and the raw byte.
Wire bytes are `0x30` disabled, `0x31` enabled, and `0x72` removed. Any other
byte is `unknown`. A removed fuse is not enabled: Inspector flags and
`ELECTRON_RUN_AS_NODE` are not honored, and application code is not restricted
to `app.asar`. On macOS and Windows, a removed or disabled
`EnableEmbeddedAsarIntegrityValidation` byte means embedded ASAR integrity is
not enforced, and an enabled byte means it is enforced. Electron compiles that
validation only on macOS and Windows. On a Linux ELF executable the observed
byte is preserved and the enforcement outcome is `unknown`.
## Unknown and multiple sentinels
No sentinel means the fuse state is unknown. It does not mean the fuses are
disabled. A wire that continues past the slice is reported as truncated, with
only the fuse bytes that were present. Two or more sentinels in one slice are
all reported. REA does not pick one.
Fuse bytes do not prove that the application enables Inspector some other way,
for example through `app.commandLine` or a remote-debugging switch in
application code.
The selected file must be a regular file, or a bundle that resolves to one.
Symlinks are followed only inside `Electron Framework.framework`. The reader
admits at most 1 GiB, searches with 256 KiB windows, and keeps at most 32
sentinels per slice. A real nine-fuse, one-slice result is about 1,200 CLI
tokens; a two-slice result is about 1,300 tokens. Exact size varies with path
lengths and limitations. This inspection creates no temporary files and does
not modify the selected bytes.
+16
View File
@@ -2,11 +2,21 @@
REA can attach to an Electron/Chromium CDP endpoint and inspect existing `file://` renderer pages without evaluating JavaScript or invoking Electron APIs. The request supplies the endpoint directly; REA accepts only literal-port loopback HTTP endpoints. Selecting an endpoint exposes every Electron target it serves, including local file paths and page metadata.
Disconnecting the inspection's page session can clear caller-owned
`navigator.platform` emulation inside Chromium. REA cannot restore another
client's complete user-agent/client-hint configuration. Use a disposable target
or let the owning client reapply its configuration; see the shared
[CDP state limitation](browser-observation.md#provider-authorities).
This passive runtime surface is distinct from the target-free static
[`analyze_javascript_application`](javascript-artifact-reconstruction.md)
workflow. Static analysis reads a local ASAR or extracted directory without
executing application code. Passive observation inspects an existing Electron
page. The results keep static inferences and runtime observations distinct.
Packaged fuse state is a separate static read:
[`inspect_electron_fuses`](electron-fuses.md) reports whether this binary will
honor Inspector flags, `ELECTRON_RUN_AS_NODE`, embedded ASAR integrity, and
`app.asar`-only loading.
Use the separate
[`reconcile_javascript_runtime`](javascript-runtime-reconciliation.md) workflow
when both Evidence sets already exist.
@@ -112,6 +122,12 @@ If cleanup cannot be verified, the error retains collected observations in
`details.partial_observation` and marks their application cleanup `unverified`.
Cleanup uncertainty does not authorize signaling unrelated processes.
### Target fuse prerequisites
Active scenario capture requires that the target Electron executable honors Node CLI inspector flags (`--inspect` or `--remote-debugging-port`) so that Playwright can establish its DevTools protocol session.
When an Electron executable explicitly disables the `EnableNodeCliInspectArguments` fuse (or removes the capability), `capture-electron-scenario` fails immediately before process launch with an `AnalysisUnsupportedTargetError` ("unsupported_target") rather than waiting for the startup deadline. For targets with disabled inspector fuses, use passive CDP inspection via `inspect_electron_page` or run against an unrestricted development binary with the extracted `app.asar`.
The real verifier exercises compiled CLI and stdio MCP discovery, page/script
inspection, static/runtime reconciliation, active observation, and CLI SIGTERM
cleanup before checking the IPC, renderer crash/reload, utility/child-process,
+24
View File
@@ -39,6 +39,18 @@ license](https://github.com/morluto/rea/blob/main/third_party/evmole/README.md)
- Upstream also returns terminal metadata during selector recovery. Its reported
values remain in `raw_result`; integer precision and non-text-key coverage are
unknown in the JS binding. Full bytecode retains the original encoded metadata.
- `bytecode.metadata` independently interprets a plausible terminal CBOR root
map from the selected bytes. Integers use decimal strings; byte strings use hex,
and scalar keys retain their types. The recorded offset/length addresses the
original bytecode, so summary delivery does not copy an opaque CBOR payload.
Nested containers have partial scalar coverage; unsupported or malformed
candidates report `not-decoded` with a diagnostic. Decoding is bounded to 64
container levels and the terminal payload's encoded size.
- An unavailable trailer does not prove metadata absence or Solidity origin.
Compiler-version bytes/text are metadata declarations, not verified compiler,
source-file or deployed-contract identity. Historical omission parses as a null
unknown facet without changing the original Evidence record. Metadata/source
URLs are not fetched.
- Selectors, argument strings and state mutability are **inferences**. Function
names/signatures are not looked up. Optimized code may produce different
mutability candidates; recovered types are not a verified source ABI.
@@ -125,3 +137,15 @@ The real verification lane traces representative raw/hex CLI workflows and check
that only declared Node/prlimit launchers and process ownership inspection run.
Socket traces permit local stdio socket pairs and reject attempted network requests.
These observations verify those fixtures; they are not a general network sandbox.
For the independent metadata parser, `npm run verify:solidity:metadata` uses an
existing caller-selected solc 0.8.30 module:
```sh
REA_VERIFY_SOLC_MODULE=/absolute/path/to/solc/index.js npm run verify:solidity:metadata
```
It compiles the owned fixture with unoptimized, viaIR, content-hash-free and
CBOR-free settings, verifies the observed trailer facts and reports local decode
timings. No contract is executed. This lane does not establish the complete
Linux EVM provider's CLI/MCP, resource-limit or cleanup behavior.
+27
View File
@@ -9,6 +9,10 @@ No device, root privilege, mount, emulation or execution of extracted programs
is needed. Arbitrary firmware bytes do not pass native executable admission;
select a returned ELF separately for supported Ghidra analysis.
On Linux, Binwalk and Unblob run as REA-owned processes and require a
procps-compatible `ps` on REA's `PATH`; see the shared
[Linux process prerequisite](installation.md#linux-owned-process-prerequisite).
## Caller-supplied tools
Provide existing tools using absolute executable paths, scoped to your shell:
@@ -103,6 +107,23 @@ build's source revision, so that field remains unknown.
engine finished its configured extraction without those reported conditions;
default skip rules and format coverage still apply. It does not establish
complete firmware reconstruction.
- `extraction_gaps` makes those conditions recoverable: a row identifies an
unextracted region or a failed task/stage. A chunk retains its identity and
byte range, plus its original-file range when it belongs to the selected input.
A multi-file stage retains its reported ID and handler with unknown byte ranges;
a standalone task failure keeps unavailable stage identity null. Rows retain exact dependency names from
the producer's missing-dependency reports; an accepted report with no names
remains a failure with unknown dependencies; no executable identity or specific installation step is inferred. Each row's `continuation_id`
references one instruction in `extraction_continuations`, so many regions
share recovery prose without losing any region identity. Instructions use
already configured capabilities where one exists. Every extraction rerun
requires a fresh absent `output_directory`, preserving the prior evidence.
Nested unknown chunks use `inspect_firmware_regions` on the actual published
child rather than repeating the same extraction with unchanged handlers.
Inspection requires separately configured Binwalk (`REA_BINWALK_COMMAND`);
without it, the inspection step is unavailable and the region remains unexamined. A listed gap does not
mean the target is corrupt or that a retained region is empty; REA installs
nothing.
- The destination must be absent, absolute, with an existing parent. Publication
uses exclusive, symlink-resistant writes and durable digest readback. Existing
destinations are never overwritten. This is not an atomic directory rename:
@@ -167,6 +188,12 @@ records gzip offset/length and extracted file hashes. Real verification checks
CLI/MCP, full-file and selected-range extraction, unknown chunks, depth limits,
content parity and unchanged source bytes. No binary fixture is committed.
The optional missing-extractor lane asserts dependency names and the owning
handler in `extraction_gaps`. Those assertions must be run against the pinned
Unblob release to establish real-provider coverage; synthetic producer fixtures
alone do not verify Unblob's report nesting or stage identities. The gap ownership
claims remain unverified against real Unblob until that lane runs.
Additional lanes are explicit, so the base lane stays small:
```sh
+15 -2
View File
@@ -60,6 +60,10 @@ server; older servers keep the complete request after schema discovery.
[Dart 2.14 layout](https://github.com/dart-lang/sdk/blob/2.14.0/runtime/vm/snapshot.h).
Kind numbers vary across Dart releases; other values are preserved with
partial coverage, without guessing their interpretation.
Address resolution examines every overlapping `PT_LOAD` interpretation,
including partial overlaps. Equivalent mappings are coalesced; conflicting
mappings leave affected symbols unresolved and qualify coverage. Dynamic
hash, symbol and string tables use the same mapping checks.
A payload without exported snapshot symbols is refused rather
than guessed at.
- **The snapshot hash** from the header at the start of each exported data
@@ -133,8 +137,11 @@ server; older servers keep the complete request after schema discovery.
`resource_constraint`.
- **Engine notes.** Engine libraries are spooled into the owned root. ELF
headers, note tables and note payloads share an 8 MiB metadata budget;
up to 64 distinct build IDs are retained. Exceeding either bound preserves
the candidates already examined and reports partial coverage.
up to 64 distinct build IDs and 1024 distinct nonempty note ranges are retained.
The range cap bounds bookkeeping and separate positional reads even for tiny
overlapping declarations. Exact duplicates share one read and empty ranges
consume no inventory slots. Exceeding a bound preserves the candidates examined
from retained ranges and reports partial coverage with `build_id: null`.
- **Metadata and candidate capacity.** Both operations refuse duplicate
relevant library paths. Inventory is limited to 32 ABIs and 1024 bytes per
relevant library path, independently of compressed size. Identification
@@ -174,3 +181,9 @@ engine build-ids, no labeled Dart version (as expected for a release
engine), and the Android clang toolchain line; a plain aapt2-built APK
reports not-detected with complete coverage. The suite uses self-contained
synthetic ELF images and scanner payloads; it does not read private local files.
The Gallery workflow and all 147 focused Flutter tests also pass on macOS Sonoma
14.8.9 x86-64 and Windows 11 x64 build 26300.9457 guests with Node 24.18.0,
including the conflicting-load and note-range-limit CLI/MCP regressions. Windows
verification runs under a non-administrator account. Verifiers use a shared
temporary parent so each CLI/MCP process can own and release its own snapshots.
+2 -2
View File
@@ -6,8 +6,8 @@ For remote devices, pass a host with an optional port (`host[:port]`; bracket IP
MCP exposes `list_frida_devices`, `list_frida_processes`, `start_frida_session`, `load_frida_script`, `resume_frida_session`, `unload_frida_script`, `frida_session_status`, and `close_frida_session`. A spawned process starts paused so scripts can load first. Closing unloads session scripts and detaches. If a spawn remains paused, REA resumes it only after script cleanup or session detach confirms the instrumentation is released; if both cleanup paths fail, the target stays paused and the cleanup error is retained. REA never kills the target. MCP sessions end with the server process. The one-shot CLI command `instrument-with-frida` accepts one inline script or script file, observes messages for a bounded duration, and returns Evidence with the source hash and captured messages.
Failed startup cleanup retains acquired Frida handles, including a paused spawn that has not attached, for later cleanup retries. Its remote connection stays leased until session detach and target resume complete. CLI commands await provider cleanup before returning; MCP retains these resources for cleanup when its server closes. A failed cleanup remains reported rather than treating an unregistered session as fully released.
Failed startup cleanup retains acquired Frida handles, including a paused spawn that has not attached, for later cleanup retries. Its remote connection stays leased until session detach and target resume complete. CLI commands await provider cleanup before returning; MCP retains these resources for cleanup when its server closes. Server shutdown stops admitting new Frida operations and waits for admitted startup, discovery, and queued work before releasing their resources. A failed cleanup remains reported and can be retried by closing the server again, rather than treating an unregistered session as fully released.
Frida scripts execute inside the selected target with that process's memory, filesystem, and network authority. Message capture is bounded to 16 MiB and reports truncation. Script source is not stored in Evidence; its selected path and SHA-256 digest are. Evidence retains non-authentication target selection such as device or endpoint, mode, program or PID, and spawn arguments. Authentication values remain excluded. Results report the observed session and messages, while unsupported platform metadata remains unknown. A request cancelled before a Frida operation starts has no provider effects. If cancellation arrives during a native Frida call, REA waits for that call to return, then closes any session it created and returns the observed session as a partial observation. Cancellation Evidence separately reports whether a target resume was observed, whether instrumentation cleanup completed, and that final target liveness remains unknown. REA does not kill the target. Remote discovery retains its observations and reports a separate cleanup error if the temporary connection cannot be removed.
Frida scripts execute inside the selected target with that process's memory, filesystem, and network authority. Message capture is bounded to 16 MiB and reports truncation. Script source is not stored in Evidence; its selected path and SHA-256 digest are. Evidence retains non-authentication target selection such as device or endpoint, mode, program or PID, and spawn arguments. Authentication values remain excluded. Results report the observed session and messages, while unsupported platform metadata remains unknown. A request cancelled before a Frida operation starts has no provider effects. If cancellation arrives during a native Frida call, REA waits for that call to return, then closes any session it created and returns the observed session as a partial observation. A cancelled script load on an existing session releases only its new script before queued operations can run; the session and previously loaded scripts remain available. Once the provider commits a successful load, later request cancellation does not unload that script; use `unload_frida_script` to release it. If script cleanup fails, the script remains owned and a paused target cannot resume until the pending instrumentation is released. Queued script loads, resumes, and unloads check cancellation before dispatching native effects. Cancellation Evidence separately reports whether a target resume was observed, whether instrumentation cleanup completed, and that final target liveness remains unknown. REA does not kill the target. Remote discovery retains its observations and reports a separate cleanup error if the temporary connection cannot be removed.
The [Frida Core API](https://frida.re/docs/frida-core-api/) documents device management, sessions, and script loading; the [JavaScript API](https://frida.re/docs/javascript-api/) documents target-side instrumentation. [Luma](https://luma.frida.re/) is Frida's standalone GUI; REA does not automate or control it. `npm run verify:frida` requires a Linux host with the optional Frida binding and uses a short-lived local Node.js fixture created by the verifier. Add `-- --address HOST:PORT --pid PID` to run the separate remote attach lane against a caller-selected target. Mock tests do not establish real device/server compatibility.
+54 -14
View File
@@ -22,6 +22,28 @@ lane. MIPS16 shared images have the separate bounded profile below; other shared
or compressed images remain unsupported. Their family may be identified, but provider selection returns the
specific unsupported constraint rather than guessing a compatible profile.
A second admission covers recordless legacy firmware images
([#1620](https://github.com/morluto/rea/issues/1620)): ELF32 `ET_EXEC` headers
declaring `EF_MIPS_ARCH_32` or `EF_MIPS_ARCH_32R2` with an unspecified ABI
field, `EF_MIPS_ABI2` clear, and an _inspected_ absence of any ABI flags
record (a target whose record was merely not observed stays refused). For
these images o32 is an analysis assumption derived from the ELF32/ET_EXEC
structure, recorded as `mips_abi_basis` in the profile and stated as a
limitation in Evidence, never a declared fact; floating-point ABI semantics
stay unverified. Ghidra's own header auto-selection picks a 64-bit language
for exactly these files, so the lane pins `-processor MIPS:<LE|BE>:32:default`
through a `provider-pinned-v1` profile selection and checks the
bridge-reported `language_id` after import. A caller-selected
`REA_GHIDRA_LANGUAGE_ID`/`REA_GHIDRA_COMPILER_SPEC_ID` still wins over the
pin; combining the pinned profile with an environment override is a typed
refusal. A header that declares an ABI field but carries no record is still
refused. Two further boundaries apply: an inspection that could not examine
every ABI declaration source (for example a carved ELF whose section table
extends past the captured bytes without a program-header record) does not
establish the recordless admission, and an `EF_MIPS_ARCH_32` header that
_declares_ an ABI is refused rather than relabeled, because the declared-ABI
lane is verified for `EF_MIPS_ARCH_32R2` headers only.
Explicit `EF_MIPS_FP64` and `EF_MIPS_NAN2008` declarations are refused. Their
absence is insufficient: Clang can emit identical `e_flags` for FP32, FP64 and
soft-float targets. Target resolution also reads the 24-byte ABI flags record
@@ -30,22 +52,40 @@ section names or the record being inside the initial 4 KiB header probe.
Program and section declarations must agree; duplicate, conflicting, truncated
or out-of-file records are rejected. Stripped section tables and section-only
records are supported. Extended table numbering is explicitly unsupported.
For carved ELFs whose section table extends beyond the captured file, REA
examines every complete captured section header and records the unavailable
headers as a limitation. A valid program-header ABI record can still establish
the supported declaration. Available section declarations must agree with it;
their duplicate, conflicting or unreadable records remain errors. A missing
program-header record does not make ABI declarations in uncaptured sections
absent or supply a default ABI.
GNU `SHT_GNU_ATTRIBUTES` sections are located by numeric section type; section
names never control detection. REA reads at most 4096 payload bytes and parses
format version `A`, the `gnu` vendor's file-scope `Tag_GNU_MIPS_ABI_FP` (4) and
`Tag_GNU_MIPS_ABI_MSA` (8) only. Unknown vendors, scopes or tags, repeated
declarations, malformed lengths, and out-of-file or oversized payloads remain
refusals. A nonzero FP tag must match the separate ABI flags `fp_abi` record;
value 0 (`ANY`) declares no requirement. The [GNU Object Attributes v1
format](https://sourceware.org/binutils/docs/as/Object-Attributes-v1.html) and
[GNU MIPS tag definitions](https://sourceware.org/binutils/docs/as/GNU-Object-Attributes.html)
are the format references. The MSA tag describes the vector
argument ABI, while the ABI flags ASE field describes instruction-set
extensions; REA does not treat those as interchangeable. The 128-bit MSA
argument ABI and unknown nonzero MSA values remain unsupported. Without a
separate ABI flags record, FP attribute values are admitted only when absent,
`ANY` (0), double precision (1), or FPXX (5), the values covered by the
recordless profile. Parsed values are retained in the MIPS analysis profile.
For carved ELFs whose section table extends
beyond the captured file, REA examines every complete captured section header
and records the unavailable headers as a limitation. The provider refuses an
incomplete ABI declaration inspection even when a program-header ABI record is
valid, because an unavailable section header could carry an unsupported or
conflicting declaration. A missing program-header record does not make ABI
declarations in uncaptured sections absent or supply a default ABI.
The field layout follows
[`Elf_MIPS_ABIFlags_v0`](https://github.com/bminor/glibc/blob/master/elf/elf.h).
Provider admission requires record version 0, MIPS32r2, 32-bit GPRs, no CPR2,
no ISA extension, ASE or unknown general flags, and one of two floating-point
declarations: 32-bit CPR1 with a double-precision or FPXX ABI, or no CPR1 with
the soft-float ABI. Other combinations, including single-float, FP64 and
unspecified ABIs, are refused. Missing records remain unknown rather than
defaulting to a compatible ABI, and this lane refuses them. Unknown record
Provider admission on the declared-ABI lane above requires record version 0,
MIPS32r2, 32-bit GPRs, no CPR2, no ISA extension, ASE or unknown general
flags, and one of two floating-point declarations: 32-bit CPR1 with a
double-precision or FPXX ABI, or no CPR1 with the soft-float ABI. Other
combinations, including single-float, FP64 and unspecified ABIs, are refused.
Missing records remain unknown rather than defaulting to a compatible ABI, and
the declared-ABI lane refuses them; the recordless legacy admission above is
the only path that admits a recordless image. Unknown record
values are retained for diagnosis but do not gain provider support. These checks
bind the declared interpretation; the fixtures do not establish full
floating-point behavioral conformance.
@@ -111,7 +151,7 @@ MIPS header and ABI record facts are committed in the analysis profile so
interpretation changes invalidate profile-bound snapshots. The support-lane
identity is versioned independently of file identity. Evidence, lifecycle
outputs and saved snapshots preserve the `mips` family. Hopper and IDA adapters
do not implicitly gain MIPS support. Windows Ghidra P0 remains PE x86/x86-64 only.
do not implicitly gain MIPS support. Windows Ghidra P0 does not admit MIPS targets.
### Soft-float prototype
+31 -7
View File
@@ -17,10 +17,14 @@ submodule and is a separate, more capable workflow.
The parser currently handles x64 PE32+ and RTR 9.1, including bounded
DEHYDRATED_DATA processing, MethodTable relationships, virtual slots, and
frozen strings. Other RTR versions and PE architectures are reported as
unsupported or partial. The matching .NET 8.0.22 fixture is source-built and
the parser's Windows-host Ghidra workflow has not yet had a real Windows-host
run. This does not establish Windows Ghidra import or decompiler behavior.
Windows Ghidra P0 has no database mutation authority. A PE/CLI or ReadyToRun
unsupported or partial. The matching .NET 8.0.22 fixture is source-built.
On a Windows host, Windows Ghidra P0 runs the parser without a JAR. Metadata,
MethodTable relationships, virtual slots and frozen strings come from the
parser report, and `analyze_function` decompiles slot targets. Tools that read
the Ghidra database do not see rehydrated bytes (see below), because only the
optional adapter writes them into the database and Windows Ghidra P0 has no
database mutation authority. A PE/CLI or ReadyToRun
assembly should use `inspect_managed_artifact`; it is not NativeAOT.
## Build and configure
@@ -57,18 +61,30 @@ returns `observations.metadata_recovery` inline from the captured source snapsho
including the RTR header, MethodTable addresses, frozen strings, derived overlay
identity, and examined/recovered counts. Its enclosing `unsupported` load-image
status means independent PE load-image verification was not performed; it does
not mean the metadata parser failed or the executable failed to load.
not mean the metadata parser failed or the executable failed to load. Without
the optional adapter, each recovered type's `type` is `null`, because no Ghidra
DataType exists; select it by `address`.
Pass a returned MethodTable address to `inspect_native_data_type`. A direct PE
query also derives the snapshot metadata from the authenticated Ghidra session
handshake, so it does not require a preceding load-image call. In a persistent
MCP session, load-image followed by type inspection reuses that same derivation.
The bridge resolves the caller's address spelling once; metadata lookup uses
its canonical default-space identity. Bare hexadecimal, prefixed hexadecimal,
and encoded default-space selectors therefore select the same MethodTable.
Other address spaces and unresolved identities are not matched by offset alone.
Metadata already supplied by the engine is retained without reading or deriving
an additional source snapshot.
The database result remains `unavailable` when Ghidra has no DataType at that
address; its `metadata_recovery.source` is `read-only-derived-overlay` and
carries related type, interface, slot, and target observations. Generated
address identities do not claim original managed names. The parser does not
merge frozen literals into Ghidra's database string inventory. Follow slot
targets with `analyze_function` and ordinary cross-references.
merge frozen literals into Ghidra's database string inventory: read them from
`metadata_recovery.frozen_strings`, not `search_strings`. Slot targets come
from the parser report; on Windows P0 without the adapter, a `read_bytes` of
a slot in the rehydrated region returns no bytes. The Linux adapter route
verifies a complete slot read and its target. Follow slot targets with `analyze_function` and ordinary
cross-references.
The built-in CLI path uses a MethodTable address returned by the parser or
another observation:
@@ -129,3 +145,11 @@ compiler/linker symbols and raw directory bytes provide the oracle. Stripped
fixtures exercise heuristic detection. Tests do not execute the resulting
NativeAOT binaries. Fixture generation and adapter compilation are optional and
never prerequisites of ordinary native analysis or setup.
The real MCP lane runs only on a Linux x64 host with the adapter. The
Windows-host behavior above was checked manually over MCP on Windows 11 x64
(non-elevated, Ghidra 12.1.4, JDK 21, no JAR) with the source-built win-x64
symbols fixture. The format, RTR version and frozen strings matched the fixture
oracle; the derived MethodTable, its base type, its interface and a virtual
slot reaching the overridden method matched the linker map; and pseudocode was
readable. No CI lane repeats this check.
+90
View File
@@ -0,0 +1,90 @@
# PowerPC ELF analysis through Ghidra
This lane extends native ELF analysis to 32-bit big-endian PowerPC; it does
not execute PowerPC code. It is the first milestone of
[#1741](https://github.com/morluto/rea/issues/1741). GameCube/Wii DOL
executables, PPC64 and other PowerPC variants are separate follow-ups.
## Admission boundary
ELF `EM_PPC` (20) is retained as the provider-neutral `powerpc` family. The
target also retains class, byte order, `e_type` and raw `e_flags`. Recognition
does not mean every provider or PowerPC variant is supported. `EM_PPC64` (21)
is not recognized as this family.
The Ghidra lane admits ELF32 `ET_EXEC`, big endian. ELF64, little-endian,
relocatable and shared objects are refused with a specific unsupported
constraint. `EF_PPC_EMB` declares an embedded variant and is refused; the
remaining `e_flags` bits only declare relocation conventions and do not change
instruction decoding.
## Bound language
Ghidra's ELF loader selects `PowerPC:BE:32:e500:default` for a plain `EM_PPC`
header with `e_flags` 0. e500 is the embedded Book-E variant whose SPE unit
replaces the classic floating-point unit, so it misinterprets ordinary
750-family code. Nothing in the header distinguishes the two.
The lane therefore imports with `PowerPC:BE:32:default` and compiler spec
`default`, commits that choice in the analysis profile as
`language_selection: "provider-pinned-v1"`, and checks the language Ghidra
reports after import. A different language set through
`REA_GHIDRA_LANGUAGE_ID` (and optionally `REA_GHIDRA_COMPILER_SPEC_ID`)
replaces the lane default and is admitted against the installed language
catalog as usual; for example `PowerPC:BE:32:e500` for genuine e500 code.
PowerPC header facts are committed in the analysis profile so interpretation
changes invalidate profile-bound snapshots. The support-lane identity is
versioned independently of file identity. Evidence, lifecycle outputs and saved
snapshots preserve the `powerpc` family. Hopper and IDA adapters do not
implicitly gain PowerPC support. Windows Ghidra P0 remains PE x86/x86-64 only.
## Use
With a caller-installed Ghidra and compatible JDK on a supported Linux/macOS host:
```bash
rea analyze /absolute/path/to/ppc.elf --provider ghidra --json
rea function /absolute/path/to/ppc.elf function_name --provider ghidra --json
```
MCP uses the existing tools, including `open_binary` with an absolute path and
`provider_id: "ghidra"`. No PowerPC-specific tool catalog or emulator is
introduced.
## Verification
The source-owned freestanding fixture is `tests/conformance/c/ppc.c`. The
optional cross-target lane requires a caller-supplied Clang with the PowerPC
target, LLD, GNU `readelf`, Ghidra and its compatible JDK; ordinary host-native
checks do not acquire these tools. `REA_PPC_CLANG` selects Clang and
`REA_PPC_READELF` selects GNU readelf (for example
`/opt/homebrew/opt/binutils/bin/readelf` on macOS). These are test-time
prerequisites, not REA runtime providers.
```bash
GHIDRA_INSTALL_DIR=/absolute/path/to/ghidra npm run verify:ghidra:ppc
```
The lane compiles the fixture for a 750-family core (`-mcpu=750`) and never
executes it. GNU readelf independently supplies header facts and symbol
addresses, compared with the production REA target resolver before provider
startup. Its version, bounded command output and selected tool path are
retained in the verification report. The parser deliberately recognizes only
this fixture's GNU output, rejecting missing, ambiguous or unexpected fields
rather than substituting defaults.
Existing public MCP operations check Ghidra's **observed** language and
compiler specification and loaded-file hashes; an e500 import fails the lane.
The source-owned `ppc-probe.S` supplies fixed `addi` and conditional-branch
encodings: verification checks bytes, length, immediate and direct destination,
independent of decompiler variable names. Global and string bytes are checked at
independently reported symbol addresses.
CLI/MCP discovery and decompilation, direct calls, target/profile/Evidence
identity, snapshot round-trip and final process cleanup remain covered.
Pseudocode presence is a liveness check, not proof of semantic equivalence.
Instruction decoding and reader agreement do not establish runtime or complete
floating-point semantics. Parser/profile tests are narrower checks, not
substitutes for this real-provider run. Report the actual lanes run and leave
unexecuted verification pending.
+149 -10
View File
@@ -7,6 +7,8 @@ Executable bytes remain unchanged.
Use [installation](installation.md#ghidra) for supported hosts, prerequisites,
configuration, startup deadlines, resource controls, and cleanup recovery.
[Linux Ghidra launches](installation.md#linux-owned-process-prerequisite) also
require a procps-compatible `ps` on REA's `PATH` for process ownership checks.
[Windows P0](windows-ghidra-p0.md) has its own admission and lifecycle boundary;
[NativeAOT observations](ghidra-nativeaot.md) include a built-in read-only PE
snapshot parser and a separate optional annotation extension. Check the
@@ -41,8 +43,108 @@ does not prove text is absent. Both list and search Evidence carry this
limitation. When an address is known, use `read_bytes` and `xrefs` to inspect
bytes and references.
## PE debug-symbol coverage
The post-analysis handshake reports Ghidra's PDB Program Information attributes:
the referenced file, GUID, hexadecimal age, legacy signature, version, and
`PDB Loaded` state. Absent attributes remain null. PE Evidence carries a
limitation when a PDB reference is present but Ghidra reports it as not loaded,
or its load state is unknown. A bridge without this report leaves PDB coverage
unknown. A reported loaded state is a Ghidra observation, not an independent
PDB identity or complete symbol-coverage attestation, and retains an inline
coverage caveat. Handshake PDB strings share a 4096 UTF-16-unit serialization
budget (at most 24 KiB after JSON escaping); whole attributes that do not fit
are omitted with an explicit resource limitation, rather than truncated.
Original names and types may be missing even when auto-analysis completes.
An empty name search does not establish that a function is absent; use
`list_procedures` and address-based analysis to inspect recovered functions.
REA imports a private executable copy and does not admit a caller-selected
PDB companion. This reporting does not search symbol servers, copy a sibling
PDB, or change analyzer settings.
## Loaded addresses and file offsets
`rea analyze <path> --provider ghidra --json` and MCP `binary_overview` report
the observed loaded `image_base` in `normalized_result`. They retain each
segment's reported `image_base` and `address_space`. A provider that does not
report a base, or whose segments disagree, omits the overview field, following
the existing contract on main. REA does not substitute zero or the first
segment's address. All reported bases and address spaces remain visible in the
complete segment inventory, including conflicting observations.
`function` and most MCP procedure selectors use procedure names or
provider analysis addresses. Ghidra `decompile` / MCP `procedure_pseudo_code`
also accept source-file byte offsets. Existing symbols and analysis addresses
take precedence; a `0x`-prefixed number that resolves to no procedure falls back
to its observed source mapping. Use `--file-offset=0x...` on the CLI or
`procedure: "file-offset:0x..."` through MCP to select file coordinates explicitly.
Multiple mapping candidates fail as ambiguous rather than selecting a bank or
address space. A successful conversion retains the file offset, mapped address
and procedure entry in raw Evidence and `locations`.
If an offset maps to loaded target bytes but no procedure starts at or contains
the mapped address, the failure preserves both the original file offset and
analysis address. This differs from an unmapped offset and does not recommend
retrying the same offset syntax.
If the function resolves but the backend fails or cancels decompilation, the
failure retains the selector, mapped address and procedure entry without
changing the backend failure code.
Both mapping directions require a complete original `FileBytes` source whose
SHA-256 matches the admitted target. Auxiliary files and partial source objects
remain visible in the load-image inventory but do not establish offsets in the
original target. Ghidra's object-local offsets are not silently treated as
original-file coordinates. Original-source identity is checked once per source
object per session; analysis-memory edits do not change the original bytes.
ELF virtual addresses and file offsets are
different coordinates. Ghidra can rebase a PIE ELF during import: in the
source-owned verification fixture, ELF entry `0x1013` becomes Ghidra procedure
`0x101013` with image base `0x100000`. That example is not a general conversion
rule.
Use `list_procedures` to select an observed procedure address. To relate file
bytes to loaded memory, call MCP `inspect_native_load_image` or:
```sh
rea search <path> . --kind procedures --mode regex --provider ghidra --json
rea inspect-native-load-image <path> --provider ghidra --json
rea decompile <path> <observed-procedure-address> --provider ghidra --json
rea decompile <path> --file-offset=0x1013 --provider ghidra --json
rea address-to-file-offset <path> <observed-procedure-address> --provider ghidra --json
```
The load-image result preserves source-file indexes, file offsets, address
spaces, and inclusive mapping ranges. Follow the mapping for the selected
source file and region; adding `image_base` to an arbitrary file offset is not
valid. For a linear byte-addressed, file-backed mapping, a file offset `F` in
`[mapping.file_offset, mapping.file_offset + mapping.length)` maps to
`mapping.start + (F - mapping.file_offset)` in the reported `address_space`.
If several mappings cover the same file offset, keep those candidates distinct
and select the procedure from the observed inventory. Uninitialized regions may
have no file backing. ELF mappings are observed
without independent format-specific load-image verification, as the result's
status and limitations state.
An unsuccessful numeric procedure lookup reports the current Ghidra image base
and gives the CLI commands above alongside the corresponding MCP operations.
Repeat any target-format, architecture, and entry-point options used to open the
target. Other procedure operations keep their analysis-address semantics;
file-offset fallback applies only to decompilation.
Overview workflow revision 3 binds this output to snapshot identity. Older
serialized overview Evidence remains readable; it is not replayed as a current
overview merely because its source artifact is unchanged.
## Admitted function-analysis semantics
Concurrent callers share one startup while retaining independent cancellation.
Only the final departing caller abandons startup and waits for its cleanup.
New attempts wait for that cleanup; failed cleanup retains its original error
and owned directories until a successful close. Closing the provider reports a
provider-state failure to live callers rather than cancelling their signals.
| Concern | Ghidra contract |
| ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Decompiler lifetime | One persistent `DecompInterface` is opened for the imported Program and disposed during bridge shutdown. Native decompilation has no fixed per-function deadline. External functions or functions without bodies return `null`; cancellation and native failure remain distinct. |
@@ -57,14 +159,18 @@ bytes and references.
Function extents and instruction completeness refer to Ghidra's analyzed body.
An inferred no-return call can terminate that body before subsequent bytes.
Pseudocode, assembly, procedure info, instruction windows, and dossiers report
observed terminal call sites in Evidence limitations, including resolved callee
names, addresses, and FunctionManager flags when available. Instruction windows
and dossiers also retain these warnings in their result limitations. An
unresolved callee or unestablished terminal-flow basis remains explicit. These
observations qualify the analysis; they do not prove that a no-return flag is
incorrect or recover excluded code. Analysis profiles version this observation
contract so older snapshots cannot replay unqualified results.
Pseudocode, assembly, procedure info, instruction windows, incoming and outgoing
references, callee and caller reads, and dossiers report observed terminal call sites in Evidence limitations,
including resolved callee names, addresses, and FunctionManager flags when
available. On x86-32, the same views also report the exact Borland/Delphi-style
FS exception-registration pattern when its handler address falls outside the
function body. This is a static registration observation; it does not establish
handler execution or change the body. Instruction windows and dossiers also
retain these warnings in their result limitations. An unresolved callee or
unestablished terminal-flow basis remains explicit. These observations qualify
the analysis; they do not prove that a no-return flag is incorrect or recover
excluded code. Analysis profiles version this observation contract so older
snapshots cannot replay unqualified results.
On non-Windows hosts, the ephemeral import repair clears no-return flags on the
known returning external ABI names `malloc`, `__tls_get_addr`, `memcpy`, `memset`,
@@ -93,8 +199,12 @@ these tools before compilation. Keeping this matrix separate lets Linux
host/provider acceptance run with only the host compiler.
`npm run verify:ghidra:windows` checks source-owned native x86-64 PE applications;
`-- --x86` selects x86 and `-- --dll` selects DLLs, and the flags can be combined.
These lanes check all 25 read-only operations, target/snapshot/import SHA-256 linkage,
`-- --x86`, `-- --arm`, or `-- --arm64` selects another target architecture;
`-- --dll` selects DLLs. Choose one architecture and optionally combine it with
`--dll`. These static fixtures contain source-owned encoded instructions; they
are not compiler-produced programs. See the [Windows verification scope](windows-ghidra-p0.md#native-authority-and-target-identity)
for real-provider coverage and exclusions.
These lanes check all 26 read-only operations, target/snapshot/import SHA-256 linkage,
and project, endpoint, process, and runtime cleanup. The independent native
lane checks DACLs, handle admission, cancellation, and Job Object lifecycle.
`npm run verify:ghidra:windows:package` packs and installs REA into an isolated
@@ -103,3 +213,32 @@ It accepts the same architecture and DLL flags and verifies the selected PE role
in the analysis profile.
All lanes require matching native controls; package startup alone establishes
only package compatibility.
## Masked byte search
`search_bytes` (`rea search-bytes PATH '48 8B ?? ?? 89' --provider ghidra`)
searches initialized analyzed memory, including loader fixups. Supply hexadecimal
byte pairs, with `??` for any byte. An optional equal-length hexadecimal `mask`
selects bits: `pattern: "a0"`, `mask: "f0"` matches bytes from `a0` through `af`.
Wildcards remain unconstrained even with an explicit mask. At least one bit
must be constrained.
Optional `start` and `end` addresses are inclusive and select one address space.
A match must end at or before `end`, so `end` set inside a match excludes it.
Without bounds, loaded memory blocks are eligible, in Ghidra address order.
Blocks that are not loaded (file parts such as `.symtab`) and overlay blocks are
listed in `searched.skipped_not_loaded` and are not searched; pass `start` or
`end` in their address space to search them.
Overlapping matches are included; matches never cross block boundaries.
Each match reports its address, segment, unique source file offset (or null),
and containing procedure (or null).
Pages default to 100 matches and accept 1 to 1000. Pass `next_cursor` as `cursor`
with the same target, profile, pattern, mask and bounds to continue. `complete`
means no further matches remain. `searched.blocks` and `searched.bytes` describe
the inclusive memory ranges examined for this page, including lookahead for the
next match. Coverage can overlap between pages; do not sum it as unique memory.
`searched.skipped_uninitialized` lists eligible uninitialized blocks on every
page. These blocks are never searched. Cancellation returns a typed error,
discards the active Ghidra database, and does not return a successful partial page.
Hopper byte search remains unavailable until its implementation is verified.
+2 -2
View File
@@ -11,8 +11,8 @@ ELF `EM_RISCV` (243) is retained as the provider-neutral `riscv` family. The
target also retains the raw ELF declarations that decide a lane: class, byte
order, `e_type`, and `e_flags`. Recognition does not mean every provider or
RISC-V variant is supported. Existing PE, managed and Mach-O architecture sets
are unchanged, and caller-supplied headerless raw-binary entry still accepts only
its existing four families.
are unchanged. Headerless inputs use the separate [raw-binary admission
boundary](raw-binary.md); raw RISC-V images are not supported.
The Ghidra lane admits little-endian ELF32 and ELF64 `ET_EXEC` images that
declare `EF_RISCV_FLOAT_ABI_DOUBLE` in `e_flags`, with no `EF_RISCV_RVE`,
+21 -4
View File
@@ -39,8 +39,8 @@ container format, architecture, word size and byte order. A recognized
- `module.dependencies`, with the same fields and their original order;
- `module.settings`, retaining ordered key/value entries and duplicate keys;
- `module.unparsed_lines`, `module.unparsed_line_bytes_base64` and
`module.complete`, so unfamiliar, malformed, conflicting or undecodable records
do not disappear behind a complete result.
`module.complete`, which says whether every present module-text line was
parsed. It does not establish that the dependency inventory is complete.
Missing checksums are `null`; empty fields and development/local replacement
versions remain unchanged. Build settings such as `GOOS`, `GOARCH` or `vcs.revision`
@@ -50,6 +50,13 @@ base64 while other decodable module records are still reported; the reader does
not insert replacement characters or reject the entire artifact for those
strings.
When no main-module (`mod`) record was decoded, `module.main` is `null` and
dependency coverage is unknown. Successfully decoded dependency records remain
visible. GOPATH-mode builds may contain only a package `path` and build settings;
their empty dependency array does not establish the absence of dependencies.
The result's limitations state this explicitly. A module-mode build with a main
module record and no dependency records has an observed empty dependency list.
## Supported boundary
The built-in TypeScript reader supports ELF, PE and thin Mach-O images with Go's
@@ -58,6 +65,12 @@ original stable file snapshot, including pointer-backed strings. It does not
scan arbitrary byte strings as compiler metadata or interpret a fat Mach-O
container as one selected architecture.
For ELF images without a named build-info section, discovery checks each
nonempty writable, non-executable load segment in program-header order. This
includes sectionless PIE images with metadata in a later data segment. Named
sections retain priority, and alignment, file mapping and record validation
remain required.
Historical moduleless Go binaries can store their empty module-string header in
declared zero-initialized virtual memory rather than file-backed bytes. The reader
recognizes this only when the complete optional module header lies in that memory
@@ -78,9 +91,13 @@ host permission denial, changed artifacts and cancellation retain distinct
errors. The selected file is never modified; this inspection creates no
temporary files, starts no subprocesses and accesses no network endpoint.
Resource guards bound the selected file to 256 MiB, aggregate embedded strings
Resource guards bound the selected file and cumulative header searches to 256 MiB
each, aggregate embedded strings
to 1 MiB, structural table decoding to 16 MiB and the complete normalized metadata
report to 8 MiB. Oversized results fail without partial success. MCP also applies
report to 8 MiB. Identical search spans with the same virtual alignment are
searched once; partially overlapping spans consume the cumulative search budget
and fail with a CPU resource constraint when it is exhausted. Oversized results
fail without partial success. MCP also applies
its [response transport budget](mcp-contracts.md), which counts both text and
structured content; a report within the metadata limit can still require export
from retained Evidence. These limits are not operating-system memory or CPU
+183
View File
@@ -0,0 +1,183 @@
# Verify real HTTP/3 MITM captures
This optional Linux laboratory checks native mitmproxy captures through REA's
public CLI and MCP. It changes no runtime contracts or adapters and establishes
only the specific producer, mode and clients exercised here.
## Prepare a disposable guest
The exercised image is [Omarchy 4.0.4, build 440](https://nightly.omarchy.org/stable/4.0.4/440/omarchy-4.0.4.qcow2),
SHA-256 `43412b896fe3d730e373a51a8acf99343d7c3e6dee78aa9f9bd7fca181127264`.
Download it without installing guest packages on the host, check its digest,
and use a **new qcow2 overlay** with that file as a read-only backing image.
The matching [boot artifacts](https://nightly.omarchy.org/stable/4.0.4/440/unpacked/)
include the kernel, initrd, cmdline and upstream vm-boot recipe. Use a fresh SSH
key, loopback forwarding and guest disk; do not reuse another investigation's
writable overlay or host trust configuration. Shut the guest down before
switching overlays. Preserve the image and boot artifact hashes with evidence.
This sealed guest already supplies cURL 8.22.0 with OpenSSL 3.6.4, ngtcp2 1.25.0,
nghttp3 1.18.0 and nghttp2 1.70.0. **No guest rolling package upgrade is needed.**
Run `curl --version` and verify HTTP2 and HTTP3 features before starting. A stock
cURL from another distribution may omit HTTP/3. For a different guest, building
cURL requires a compatible TLS library, ngtcp2 and nghttp3; follow the
[official build instructions](https://curl.se/docs/http3.html), install to a guest
private prefix, and record exact source revisions and build inputs. That alternative
has not been exercised by this laboratory; it is not the pinned reproduction path.
Use a fresh VM overlay over a sealed Linux image. Use user-mode networking with
SSH forwarding bound to host loopback; keep host directories unmounted. Install
and build dependencies inside the guest, then remove its default route before
generating traffic. Host trust, routes and installed packages stay unchanged.
Record the image digest, REA source revision and these dependency versions:
- REA's `.nvmrc`, `package.json#packageManager` and locked npm dependencies.
- Official Linux x86_64 mitmproxy **12.2.3** standalone archive. SHA-256:
`2e95286b618fa6fd33e5e62a78c2e5112571d85f42ec2bac29b97ee242bdb5c5`.
- A private Python virtual environment using the complete pinned graph in
`scripts/fixtures/http3-lab-requirements.txt`. Save `pip freeze` and build/download logs.
- OpenSSL and cURL advertising HTTP3 and HTTP2 in `curl --version`.
The verified guest used Python 3.14.7, cURL 8.22.0, ngtcp2 1.25.0 and nghttp3
1.18.0. Other dependency profiles need verification. Back up the guest's
`/etc/hosts` before adding `127.0.0.1 lab.test`. Reserve guest TCP/UDP loopback
ports 18443 and 19443. These changes belong only to the disposable guest.
From the guest REA checkout, use its pinned Node/npm toolchain:
```sh
npm ci
npm run build:cached
python3 -m venv /absolute/lab/venv
/absolute/lab/venv/bin/pip install --no-deps -r scripts/fixtures/http3-lab-requirements.txt
/absolute/lab/venv/bin/pip check
/absolute/lab/venv/bin/pip freeze > /absolute/lab/python-requirements.txt
```
Before removing the guest default route, download the official mitmproxy archive
and verify it, then extract to the guest's private tools directory:
```sh
mkdir -p /absolute/lab/tools
curl --fail --location https://downloads.mitmproxy.org/12.2.3/mitmproxy-12.2.3-linux-x86_64.tar.gz \
--output /absolute/lab/mitmproxy.tar.gz
printf '%s %s\n' 2e95286b618fa6fd33e5e62a78c2e5112571d85f42ec2bac29b97ee242bdb5c5 \
/absolute/lab/mitmproxy.tar.gz | sha256sum --check --strict
tar -xzf /absolute/lab/mitmproxy.tar.gz -C /absolute/lab/tools
/absolute/lab/tools/mitmdump --version
```
Pin the REA revision with `git rev-parse HEAD` or archive SHA-256. Build using
the exact `.nvmrc` and npm version; do not copy a different checkout's `dist`.
For stronger offline dependency reproduction, preserve downloaded Python
archives, their digests and built wheels along with the image and toolchain.
The requirement versions pin the runtime graph, not every transient build tool;
source builds and alternate interpreters are not guaranteed byte-identical.
## Run the narrow profile
Select absolute executables and a new evidence directory. The producer refuses
to reuse a directory. No certificates are installed in a system trust store;
clients and proxy receive explicit paths to freshly generated test CAs.
```sh
REA_MITMDUMP_COMMAND=/absolute/lab/tools/mitmdump \
REA_HTTP3_PYTHON_COMMAND=/absolute/lab/venv/bin/python \
npm run verify:web:http3 -- /absolute/evidence/run-1
```
The producer sends three concurrent binary POSTs on one HTTP/3 connection to the
same URL through mitmproxy reverse mode to an aioquic HTTP/3 origin. The origin
waits for all complete requests, then dispatches responses in reverse order of
completion at the origin. QUIC stream send/receive orders need not coincide. A cURL HTTP/3-only
request checks a second client implementation. Mitmproxy's real save addon
writes `normal.mitm`; no native flows are synthesized.
A second proxy run disables HTTP/3. cURL `--http3` falls back to HTTP/2, reaching
an HTTP/1.1 TLS test origin. `fallback.mitm` must retain those lower protocols,
so a successful HTTP request cannot masquerade as an HTTP/3 success.
## Acceptance and evidence
Each execution creates `verifications/<run-UUID>/` with its own diagnostics.
`verification-latest.json` belongs to the most recently registered attempt and
changes atomically from `running` to `passed` or `failed`. Registration and
the ownership check/replacement use the same SQLite transaction lock. An older
attempt finishing later keeps its own reports without replacing the marker.
The `.verification-lock.sqlite` pathname must remain in place while attempts
may run; SQLite releases the lock on connection close or process death.
Lock acquisition waits for the current owner without a timeout or lease
stealing. The verifier has per-command deadlines, but no overall run deadline;
a live stalled lock owner can therefore delay publication until it exits.
Only a successful attempt writes its own `verification.json`, after
all cleanup and input-integrity checks. `result.json` also records failures and
the original error separately from cleanup errors. Older successes remain in
identified history; a legacy root `verification.json` is archived before a rerun.
A killed verifier may leave `running`, which must never be interpreted as success.
Capture inputs live in `captures/`. To inspect them again without new traffic:
```sh
REA_MITMDUMP_COMMAND=/absolute/lab/tools/mitmdump \
npm run verify:web:http3 -- /absolute/evidence/run-1 --inspect-existing
```
Acceptance for the current run:
- Every positive native record reports HTTP/3, UDP, ALPN h3 and successful TLS
timestamps on both legs. Connection IDs, producer TLS labels, exact TLS setup
timestamps and connection errors match the UUID-matched producer observation
on each leg. Endpoint handshakes independently observe QUIC v1.
- Native flow UUIDs remain distinct. Three endpoint streams share a connection
on each leg. Origin dispatch reverses completed receipt. Client and capture
ordering is observed, while association is verified independent of their order.
- CLI and MCP agree. Request/response bytes, lengths, SHA-256, ordered duplicate
headers, method, scheme, authority and path match independent observations.
Readable header names and values, request URL fields and ALPN also match;
non-UTF-8 header bytes retain a null display value rather than replacement text.
- Actual MCP advertised schemas validate, Evidence preserves source digests and
provider provenance, and the complete returned Evidence matches the retained MCP session records.
- The fallback capture reports client HTTP/2 over TCP and origin HTTP/1.1, with
cURL independently reporting version 2. Input files remain unchanged.
- Every cleanup action is attempted on success and failure, and final lineage
contains no live descendants. Recorded producer groups are inspected separately
after token-verified cleanup, including after their launcher has exited.
Retain captures, endpoint/producer JSONL, proxy diagnostics, cURL output, manifest,
CLI/MCP output, advertised schema and session bundle. Export public certificates
and dependency/source provenance separately. Do not commit evidence, private
keys, dependencies or VM disks. Shut down the guest after exporting evidence.
The reverse proxy can preserve authority `lab.test:18443` while recording origin
port 19443. Mitmproxy's serialized flow/connection UUIDs are not stream IDs.
Endpoint stream IDs are scoped observations; this test invents no cross-leg
mapping. Mitmproxy 12.2.3's `QUICv1` label alone does not prove negotiation: the
fixture checks the negotiated `_quic._version` in pinned aioquic 1.2.0.
Coverage is native capture, Linux reverse mode, QUIC v1 and these clients.
Empty/absent/incomplete/redacted body semantics, cancellations, resets,
certificate failures, malformed captures, HAR and qlog are separate increments.
Local/WireGuard, browsers, QUIC v2, migration, 0-RTT, mTLS, replay, sustained load
and loss tolerance remain unverified by this profile. Passing it does not
establish general HTTP/3 support in REA.
Official references: [mitmproxy protocol support](https://github.com/mitmproxy/mitmproxy/blob/v12.2.3/docs/src/content/concepts/protocols.md),
[HTTP/3 implementation](https://github.com/mitmproxy/mitmproxy/blob/v12.2.3/mitmproxy/proxy/layers/http/_http3.py),
[serialized HTTP state](https://github.com/mitmproxy/mitmproxy/blob/v12.2.3/mitmproxy/http.py),
[aioquic configuration](https://github.com/aiortc/aioquic/blob/1.2.0/src/aioquic/quic/configuration.py).
## Verifier regressions
After building REA, run these inside the same isolated guest:
```sh
node --test scripts/lib/http3-verifier-lifecycle.test.mjs scripts/lib/http3-capture-assertions.test.mjs
/absolute/lab/venv/bin/python scripts/fixtures/http3-lab.test.py
```
These cover different QUIC stream completion orders, stale success reports,
multiple failed closes, separately owned process groups and partially acquired
resources, plus producer TLS fact mismatches on both legs and altered readable
fields with unchanged binary evidence. They test verifier
semantics; they do not add HTTP/3 provider coverage.
+18 -3
View File
@@ -4,6 +4,17 @@ REA adapts an existing IDA MCP server into its native analysis tools. Existing I
Choose one lifecycle mode in an upstream MCP JSON registration. Set `REA_IDA_MCP_CONFIG` to that file, then select `ida` once through `REA_ANALYSIS_PROVIDER`, the CLI's `--provider ida`, or MCP's `open_binary.provider_id`. No provider startup occurs during discovery.
## Supported upstream versions
Each mode accepts one upstream tool profile, checked against `tools/list` before any analysis runs. The attached profile requires the legacy GUI tool names; upstream 2.0.0 renamed those tools, and REA has not established an equivalent attached-mode lifecycle and ownership contract for that catalog. It therefore fails closed with the missing tool names in the error diagnostics.
The headless profile is defined by the database-supervisor API, not by an upstream version string. The upstream source snapshot [`c133c385`](https://github.com/mrexodia/ida-pro-mcp/tree/c133c3853faa111a9b00ee615c013b720d0c4acd) includes `idb_close` (introduced by [upstream PR #495](https://github.com/mrexodia/ida-pro-mcp/pull/495)) alongside `idb_open` and `idb_list`. Earlier builds may report the same `2.0.0` version while lacking that tool, so REA checks the live catalog rather than treating a version number or branch name as a compatibility guarantee.
| Mode | Upstream profile | Known working |
| -------- | ------------------- | ------------------------------------------------------ |
| attached | legacy GUI tools | 1.4.0 |
| headless | database supervisor | builds serving `idb_open`, `idb_list`, and `idb_close` |
## Analyze an already-open GUI target
The attached profile supports upstream **1.4.0 legacy tools**, including `get_metadata`, `list_functions`, and `decompile_function`. Copy the upstream registration's `command`, `args`, and optional `env` into a JSON file. An existing `mcpServers` object with an `ida-pro-mcp` entry is also accepted. For example:
@@ -88,6 +99,8 @@ REA copies the admitted binary into a private temporary directory, checks its di
One REA session owns one database. Stdio registrations default `IDA_MCP_MAX_WORKERS` to `1`; the explicit upstream `--max-workers 1` also bounds a supervisor. Database size and engine memory depend on the target and upstream; REA does not impose an engine memory limit. The original binary is never executed or overwritten. Optional `workspaceRoot` selects an existing absolute temporary-directory parent on the REA host. Ensure it has space for the input copy and IDA working files.
When a completed `idb_open` response reports success or is otherwise ambiguous, but no active owned private worker can be verified, REA retains the private workspace even if `idb_list` appears empty. A later close can retry verification if the original owned worker becomes discoverable. A completed explicit `idb_open` rejection retains the existing no-worker cleanup path. This is a local admission/ownership safeguard and **does not** provide a process-termination receipt or change the legacy reaped-worker protocol.
Headless cleanup uses the upstream database lifecycle. Abruptly terminating REA, its proxy, or the supervisor does not establish worker release. Prefer `close_binary` for MCP sessions; after an interrupted process, inspect upstream session inventory and retained workspace diagnostics before removing working files. This adapter does not claim Windows Job Object containment for the upstream detached workers.
## Connect an agent once
@@ -118,17 +131,19 @@ Use a package version that actually includes this adapter; repository main can b
{"name":"close_binary","arguments":{}}
```
Opening an MCP target starts its selected provider session; headless opening waits for auto-analysis. Set the agent's MCP request deadline long enough for the target. The registration's optional `timeoutMs` defaults to 300000 for upstream requests. A freshly opened headless worker can briefly report `status: "busy"` from `server_health`; REA re-reads the health every 100 ms until the worker answers, the caller cancels, or `timeoutMs` has elapsed since the first read. Only expiry while the worker is still busy produces a retryable `provider_timeout`; no further read is started once the deadline has passed. That error names the busy tool of the last observation, or `unknown` when the worker reported none or a value that is not tool-name-shaped. Cancellation during the wait produces `cancelled`. Any other unexpected health payload, including a busy payload with an invalid shape, remains `unreadable_output`. Cancellation drains the current upstream request before cleanup; it does not force-stop GUI analysis. An open request that times out without a completed response retains the private workspace because worker inventory alone cannot prove the request has stopped. `binary_session` reports actual tool availability. Repeated doctor/setup calls are unnecessary when the registration already works.
Opening an MCP target starts its selected provider session; headless opening waits for auto-analysis. Set the agent's MCP request deadline long enough for the target. The registration's optional `timeoutMs` defaults to 300000 for upstream requests. An upstream SDK request that reaches this limit returns `provider_timeout` with its operation and actual request budget. A freshly opened headless worker can briefly report `status: "busy"` from `server_health`; REA re-reads health every 100 ms until the worker answers, the caller cancels, or `timeoutMs` has elapsed since the first read. Each health request is capped by the time remaining in that overall wait, so a stalled read cannot extend the readiness deadline by another full request timeout. Expiry while the worker is still busy produces a retryable `provider_timeout`; no further read is started after the deadline. That error names the busy tool from the last observation, or `unknown` when the worker reported none or a value that is not tool-name-shaped. If a health request itself consumes the remaining wait budget, the same last-busy timeout is returned with the stalled request's captured output. Cancellation during the wait produces `cancelled`. Any other unexpected health payload, including a busy payload with an invalid shape, remains `unreadable_output`. Cancellation drains the current upstream request before cleanup; it does not force-stop GUI analysis. An open request that times out without a completed response retains the private workspace because worker inventory alone cannot prove the request has stopped. `binary_session` reports actual tool availability. Repeated doctor/setup calls are unnecessary when the registration already works.
For guided registration, set `REA_IDA_MCP_CONFIG` before `rea setup --client <client>`. Setup includes that file reference in its reviewed plan and preserves it after approval; it does not copy upstream credentials into the agent configuration. `rea doctor --provider ida --json` validates the registration without starting IDA and explicitly leaves runtime compatibility and target binding for analysis.
For guided registration, set `REA_IDA_MCP_CONFIG` before `rea setup --client <client>`. Setup includes that file reference in its reviewed plan and preserves it after approval; it does not copy upstream credentials into the agent configuration. For `ida` or `auto` selection, Codex/Kimi setup and doctor use the effective client registration's parsed `timeoutMs` (default 300000 ms) when recommending the agent's per-tool deadline. Existing positive client deadlines remain caller choices. `rea doctor --provider ida --json` validates the registration without starting IDA and explicitly leaves runtime compatibility and target binding for analysis.
## Coverage and evidence
All profiles support procedure/string inventory and literal or regex search, procedure resolution, pseudocode, assembly, raw function instructions, resolved callees, incoming address references, and a function dossier. Inventories and modern pseudocode/assembly follow upstream pagination. Upstream limits on modern xrefs and callees are retained as explicit truncation limitations. In the modern headless profile, targets that are not proven x86-only use processor-neutral `callgraph` edges instead of the x86 instruction checks in upstream `callees` (mrexodia/ida-pro-mcp#549). REA requests depth zero: the pinned upstream emits the root's outgoing edges without scanning child functions, then REA batches function-name lookup for those addresses. The graph covers recognized function destinations only; imports and other non-function targets remain unknown. A server without `callgraph` returns an empty relationship list with an explicit coverage limitation. Graph and path workflows retain those limitations alongside observed edges. A failed or inconsistent graph reply remains an error. A transport-level truncated preview is rejected rather than reported as complete evidence; the error preserves upstream retrieval guidance.
All profiles expose `list_documents` as the single target admitted for the session. Attached mode verifies its digest against IDA metadata before returning it; headless mode binds the digest-checked private input. `inspect_address_context`, `trace_feature`, and `binary_overview` can therefore resolve that identity without upstream document-listing tools. Segment enumeration is optional: when the selected profile cannot provide it, `binary_overview` still returns document and procedure/string counts and omits `segments` and `segment_count`; omission means unobserved, not empty. The modern headless profile also supports `resolve_containing_procedure`. An address resolves through upstream `lookup_funcs` to the canonical entry of the function that contains it; the body stays unavailable like other IDA identities. When no function contains it, REA reads the database-scoped minimal `survey_binary` and reports `not_in_procedure` for an address inside one of its segments (ends are exclusive) and `outside_segments` otherwise. A missing, truncated or inconsistent survey fails the read instead of guessing a reason. `trace_feature` marks a trace truncated, with a residual unknown for that address, when upstream caps the references it returned. Attached profiles do not declare address containment resolution.
The legacy profile additionally supports direct callers. Its producer may report call-site or interior addresses; REA resolves the reported function symbol to its canonical entry. The modern and native profiles do not prove direct callers from its code/data xref labels, so that capability is unavailable and the dossier marks it unknown.
Complete body ranges, typed reference edges, CFG, referenced data, source locals, comment kinds, and database revision are unavailable. Empty unsupported dossier facets mean **unknown**, not observed absence. `binary_overview`, whole-symbol inventory, mutation, debugging, arbitrary Python forwarding, and requested GUI navigation are outside this adapter's coverage. Check session availability before composing a workflow that needs those facets.
Complete body ranges, typed reference edges, CFG, referenced data, source locals, comment kinds, and database revision are unavailable. Empty unsupported dossier facets mean **unknown**, not observed absence. Whole-symbol inventory, mutation, debugging, arbitrary Python forwarding, and requested GUI navigation are outside this adapter's coverage. Check session availability before composing a workflow that needs those facets.
`provider.version` identifies the REA adapter; the profile explicitly records `version_scope: "rea-ida-adapter"`. The MCP handshake's `serverInfo` is separate and does not establish IDA or Python distribution versions. Raw observations preserve producer metadata and output. Attached hashes identify the recorded original input; headless identity uses the digest-verified private copy and observed worker input path. Neither proves source-code recovery or an unmodified analysis database.
+3 -3
View File
@@ -49,10 +49,10 @@ For runtime instrumentation, see the [Frida guide](./frida-instrumentation.md).
| Task | Guides |
| --------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Inspect native functions, references, or types | [Native binaries](native-investigation.md), [Go metadata](go-binaries.md), [offline ELF layout](binary-diagnostics.md), [recorded crashes](recorded-crashes.md) |
| Select a native provider or import format | [Ghidra semantics](ghidra-provider.md), [Windows](windows-ghidra-p0.md), [DOS](ghidra-dos.md), [raw bytes](raw-binary.md), [MIPS](ghidra-mips.md), [PSP](ghidra-psp.md), [NativeAOT](ghidra-nativeaot.md), [IDA](ida-provider.md), [JEB](jeb-analysis.md) |
| Inspect native functions, references, or types | [Native binaries](native-investigation.md), [Go metadata](go-binaries.md), [Rust dependencies](rust-binaries.md), [offline ELF layout](binary-diagnostics.md), [recorded crashes](recorded-crashes.md) |
| Select a native provider or import format | [Ghidra semantics](ghidra-provider.md), [Windows](windows-ghidra-p0.md), [DOS](ghidra-dos.md), [raw bytes](raw-binary.md), [MIPS](ghidra-mips.md), [PSP](ghidra-psp.md), [NativeAOT](ghidra-nativeaot.md), [IDA](ida-provider.md), [Kuna](kuna-provider.md), [JEB](jeb-analysis.md) |
| Analyze JavaScript or Electron artifacts | [Static analysis](javascript-artifact-reconstruction.md), [tracing and comparisons](javascript-application-workflows.md), [application graph](javascript-application-graph.md), [source recovery](javascript-recovery.md) |
| Compare static JavaScript with runtime observations | [Runtime attribution](javascript-runtime-observation.md), [reconciliation](javascript-runtime-reconciliation.md), [Electron observation](electron-observation.md) |
| Compare static JavaScript with runtime observations | [Runtime attribution](javascript-runtime-observation.md), [reconciliation](javascript-runtime-reconciliation.md), [Electron observation](electron-observation.md), [Electron fuses](electron-fuses.md) |
| Inspect assemblies, packages, or firmware | [.NET](managed-code-analysis.md), [Android](android-analysis.md), [Apktool resources](apktool-resource-analysis.md), [ADB acquisition](adb-device-analysis.md), [Apple](apple-application-analysis.md), [Harmony](harmony-analysis.md), [firmware](firmware-analysis.md), [WASM](wasm-artifacts.md), [EVM](evm-bytecode.md) |
| Observe or compare a browser scenario | [CDP observation](browser-observation.md), [scenarios](browser-scenario-contract.md), [runtime evidence](web-runtime.md), [historical captures](web-network-captures.md) |
| Trace captured website source | [Source locations](web-source-location.md), [module traces](website-module-trace.md), [script export](website-script-export.md) |
+361 -39
View File
@@ -3,13 +3,13 @@
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), or the [IDA guide](ida-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) |
| 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
@@ -20,7 +20,9 @@ npx rea-agents setup
Choose the agents to configure, review the proposed paths and changes, and
approve the plan. npm's package-download approval and REA's setup approval are
separate. Setup asks before changing agent configuration or installing Hopper.
Restart the agents named in the completion message.
Restart the agents named in the completion message. Quit Claude Desktop fully
before approving, because a running app can overwrite the registration; on
Windows, see [where Claude Desktop reads its configuration](client-configuration.md#claude-desktop).
For the latest published release, use `npx rea-agents@latest setup`.
[Package-runner and global-installation options](#package-runners-and-global-installation)
@@ -62,11 +64,21 @@ preserves a verified `.rea.backup.<sha256>.recovery` copy of the current bytes.
Both snapshots remain available; a failed rollback reports the current recovery
copy's path and the restore failure.
Setup also retains valid environment overrides in REA's existing server entry,
such as `REA_MCP_INPUT_SCHEMA_PROFILE` and `REA_MCP_MAX_RESPONSE_BYTES`.
such as `REA_MCP_INPUT_SCHEMA_PROFILE`, `REA_MCP_OUTPUT_SCHEMA_PROFILE`, and
`REA_MCP_MAX_RESPONSE_BYTES`.
Detected provider settings replace only matching environment keys.
Setup and doctor report a malformed REA environment map or invalid runtime
setting before setup writes files. Repair the named setting in the client
configuration; setup does not silently discard the rest of the map.
This includes malformed loader/exclusion JSON arrays and a Ghidra compiler
specification without its required language identifier. Valid overrides keep
their original strings, including empty Hopper arguments. DeepSeek Harness
`!!js` environment expressions remain unevaluated and preserved; registration
alignment does not verify the runtime values those expressions will produce.
For layered OpenCode files, each file's values are checked before merging, while
setting dependencies are checked against the effective merged environment.
Setup preserves each layer's overrides instead of copying inherited settings
into the file it updates.
After setup, REA reports which agents, analysis tools, and workflow files passed
its final checks. Restart any agent named in the completion message, then begin
@@ -110,6 +122,16 @@ See [client configuration](client-configuration.md) for client-specific roots,
profiles, and environment overrides. A mixed selection plans each distinct
destination. Selecting the skill without a client uses `~/.agents/skills`.
Setup leaves existing skill copies in other locations untouched.
Setup refuses symbolic links at the selected skill directory or its managed
`references` directory, matching uninstall and update's ownership rules. Use a
regular skill directory, or select `--skill=false` to configure the client
registration without installing the bundled skill.
The final selected directories are checked before approval and again before
setup changes any client or Hopper installation. Unselected client destinations
do not block other setup choices. Doctor reports linked or non-directory skill
paths as `unmanaged`, names the affected paths, and provides those alternatives;
an unreadable skill identity remains `unknown`. Uninstall retains and reports
files it cannot safely remove, including their ownership manifest.
`rea setup --skill-client claude_code --yes --json` refreshes and verifies the
selected skill destination independently of MCP registration selection. The
@@ -125,7 +147,154 @@ identity remains available at `identity.catalog`.
Setup pins package-runner MCP registrations to the exact installed REA version,
installs the matching skill and on-demand references in the same plan, and adds
`startup_timeout_sec = 30` for Codex and Grok Build.
`startup_timeout_sec = 30` for Codex and Grok Build. Codex also receives a
`tool_timeout_sec` derived from known provider startup plus operation headroom.
## Limit REA to one project or agent profile
Setup registers REA for the whole client. For Claude Code it writes
`mcpServers.rea` in `~/.claude.json` and installs the bundled skill under
`~/.claude/skills`, so every project that Claude Code opens can call REA. To
offer REA's tools only where they're wanted, use one of the routes below.
Both routes control which sessions discover REA's MCP server and skill. They
leave shell commands, filesystem access and network access available. An agent
under the same OS account can run `rea` from `PATH` or `npx` and read other
profiles and REA output. For clean-room separation, use an implementation
environment with filesystem and network restrictions that prevent reading or
fetching REA, the target and analysis output, including through shared
installations and package registries. A separate account or container must
enforce those restrictions.
For Claude Code, check existing default-profile state before choosing either
route. Setup is additive: a previous user-level REA registration and personal
skill remain active in other projects. Back up the default configuration and
REA skill first, then, with `CLAUDE_CONFIG_DIR` unset, remove the REA user
registration with `claude mcp remove --scope user rea` and remove the known
REA-installed `~/.claude/skills/reverse-engineer-anything` directory. Preserve
unrelated registrations and skills. `rea uninstall` applies to other clients
as well, so it is broader than this Claude-only migration.
**A separate Claude Code profile.** Claude Code keeps settings, skills and session
history under `CLAUDE_CONFIG_DIR`. Setup follows the same
variable: it writes the registration to `$CLAUDE_CONFIG_DIR/.claude.json` and
the skill to `$CLAUDE_CONFIG_DIR/skills`.
For REA setup, an empty `CLAUDE_CONFIG_DIR` is treated as unset: registration
goes to `~/.claude.json` and the skill goes to `~/.claude/skills`.
```bash
CLAUDE_CONFIG_DIR=~/.claude-rea npx rea-agents setup --client claude_code
CLAUDE_CONFIG_DIR=~/.claude-rea claude
```
A new profile may need authentication. Console sign-ins without an API key are
stored outside `CLAUDE_CONFIG_DIR`; see
[Claude Code authentication](https://code.claude.com/docs/en/authentication#log-in-with-multiple-accounts).
Sessions started without the variable use the default profile's registration,
skill and history.
Inline assignments apply only to that command. Keep the same prefix for every
profile check, setup rerun, update, or removal; bare commands use the current
environment and can otherwise inspect or recreate default-profile state:
```bash
CLAUDE_CONFIG_DIR=~/.claude-rea claude mcp list
CLAUDE_CONFIG_DIR=~/.claude-rea npx rea-agents doctor --client claude_code --skill --json
CLAUDE_CONFIG_DIR=~/.claude-rea npx -y rea-agents@latest setup --client claude_code --dry-run
```
After reviewing an update plan, rerun that same prefixed setup command without
`--dry-run` and approve its exact changes. Profile removal likewise needs the
prefix on `claude mcp remove --scope user rea` and any `rea uninstall` command;
keep the broader client scope of uninstall in mind. Restart the selected
profile after changing its pinned registration.
`claude mcp remove` removes only the registration. To remove REA from this
profile without running the broader uninstall flow, first back up the profile's
REA-managed skill directory, then remove
`$CLAUDE_CONFIG_DIR/skills/reverse-engineer-anything` as well. Keep other skills
in the profile intact.
The commands above use POSIX shell assignment syntax. In native Windows
PowerShell, select the profile for the whole setup/launch/check session and
restore the previous selection afterward:
```powershell
$previousClaudeConfigDir = $env:CLAUDE_CONFIG_DIR
try {
$env:CLAUDE_CONFIG_DIR = Join-Path $HOME ".claude-rea"
npx rea-agents setup --client claude_code
claude mcp list
npx rea-agents doctor --client claude_code --skill --json
npx -y rea-agents@latest setup --client claude_code --dry-run
# After reviewing the plan, repeat setup without --dry-run and approve it.
claude
# Profile removal, when intended, also belongs inside this selected scope:
# claude mcp remove --scope user rea
} finally {
if ($null -eq $previousClaudeConfigDir) {
Remove-Item Env:CLAUDE_CONFIG_DIR -ErrorAction SilentlyContinue
} else {
$env:CLAUDE_CONFIG_DIR = $previousClaudeConfigDir
}
}
```
Use PowerShell for these Windows examples rather than pasting POSIX assignments
into cmd.exe. Keep profile updates, readiness checks and removal inside the
same selected environment; the `finally` block restores the previous profile
or unsets the variable when it was absent.
**One project.** Install without configuring Claude Code: use the curl
installer's `--no-setup`, or run setup only for other clients. Then register
REA from the project directory at Claude Code's local scope.
This route uses the default Claude profile. Keep `CLAUDE_CONFIG_DIR` unset for
the entire local lifecycle: registration, backup, listing, version replacement
and removal. In POSIX shells run `unset CLAUDE_CONFIG_DIR`; in PowerShell run
`Remove-Item Env:CLAUDE_CONFIG_DIR -ErrorAction SilentlyContinue` before these
commands. Record an existing selection first if you intend to restore it after
finishing. The configuration path below is the default profile's
`~/.claude.json`; a different active profile uses its own config root instead.
Register with the variable unset:
<!-- x-release-please-start-version -->
```bash
claude mcp add --scope local rea -- npx -y rea-agents@6.5.0 mcp
```
On native Windows, use the same project directory and wrap the package-runner
shim so the stdio client can launch it:
```powershell
claude mcp add --scope local rea -- cmd.exe /d /c npx -y rea-agents@6.5.0 mcp
```
<!-- x-release-please-end -->
Local scope stores the entry under that directory's path in `~/.claude.json`,
so other projects don't load it. This route doesn't install the bundled skill.
Avoid `--scope project`, which writes `.mcp.json` into the repository where it
can be committed or copied. `rea doctor` and `rea uninstall` read only the
user-level entry, so they don't report or remove a local-scope registration;
remove it with `claude mcp remove --scope local rea` from the same directory.
`rea update` doesn't refresh it either, so it stays on the version it names.
To move it to a newer release, first back up the project entry in
`~/.claude.json` and record its environment and other metadata. In particular,
preserve caller-selected overrides such as `REA_MCP_INPUT_SCHEMA_PROFILE` and
`REA_MCP_MAX_RESPONSE_BYTES`. Run `claude mcp remove --scope local rea` from the
same directory, then recreate it with the platform-appropriate command above
and the new version. Reapply every recorded environment value with Claude
Code's `mcp add --env NAME=value` options and restore any other retained entry
metadata before restarting the project session. Removal and re-addition do not
preserve these values automatically. Running setup for Claude Code instead
would add a user-level entry for every project.
Check the separate profile with the prefixed `claude mcp list` above and compare
with the default profile with `CLAUDE_CONFIG_DIR` unset. For local scope, run
`claude mcp list` from the selected project and again from another project.
## Check readiness for your task
@@ -134,6 +303,15 @@ configuration without changing them. Without options, it audits every detected
registration, the installed skill and optional analysis engines; its overall
`healthy` value can be false while your chosen workflow works.
Doctor's `providerInspections` are host-level engine and registration checks,
not a readiness row for every advertised MCP adapter surface. `tools/list`
remains the complete stable catalog. Call `binary_session` with `{}` before
choosing an operation; it works before a target is opened, and
`result.tool_availability` reports per-operation availability, reasons and
client requirements for the current session state. It does not guarantee that
a particular caller-supplied input will be valid. See
[MCP discovery](mcp-contracts.md#identity-and-discovery).
Select the part you want to check:
| Task | Readiness check |
@@ -155,6 +333,21 @@ on `open_binary`. A `capability_unavailable` failure carries
`details.candidate_ids`, while `provider_unavailable` asks you to repair the
selected engine. See [provider selection](cli.md#choose-a-provider).
### Linux owned-process prerequisite
On Linux, REA's shared process supervisor requires a procps-compatible `ps`
on REA's `PATH` for ownership checks before launching a child. Ghidra, JADX,
firmware tools and static JavaScript/Electron analysis use these checks.
Install your distribution's `procps` or `procps-ng` package, or put another
procps-compatible `ps` on `PATH` before
running those workflows. A missing or incompatible command causes a refusal
before the supervised child starts.
The public error code varies by caller: `capability_unavailable`,
`provider_unavailable` or `execution_failure`. Some provider adapters report
only a generic startup error. Check this prerequisite before following
provider-specific recovery advice.
## Diagnose, update, and remove
`rea doctor --json` is strictly read-only. `rea update` updates only the npm or Bun global installation that owns the running CLI. Source checkouts and package-runner copies must be updated through the mechanism that owns them; a fresh package-runner invocation can use `npx rea-agents@latest` or `bunx rea-agents@latest`. `rea uninstall` removes only REA-owned agent registrations and skill files; `--purge-data` additionally removes REA cache and state paths.
@@ -207,9 +400,11 @@ running server or its registration.
The npm release checked on **2026-10-11** was **6.4.0**, published from
[`a56f7bb6`](https://github.com/morluto/rea/commit/a56f7bb668ab23fec8d33f7af353ac7ed2fe1895).
The actual registry package accepted dry-run setup plans for the 17 client IDs
in that release, including Qwen Code, Pi and Hermes. DeepSeek Harness (`dsh`)
is on main and is not in npm 6.4.0; `rea setup --client dsh` against `@latest`
reports an unsupported client until a release containing it is published.
in that release, including Qwen Code, Pi and Hermes. Seven client IDs are
included from 6.5.0 and were absent from the checked npm 6.4.0 package: `droid`,
`dsh`, `kimi`, `qoder`, `trae`, `workbuddy` and `zcode`.
The 6.4.0 registry check does not establish 6.5.0 publication or live DeepSeek
API integration.
OpenCode's plan selected
`OPENCODE_CONFIG_DIR` even when that directory did not yet exist. These checks
used an isolated home on macOS with Node 24; they verify planning and argument
@@ -277,18 +472,21 @@ hosted connector; see [its configuration](client-configuration.md#other-client-s
| GitHub Copilot CLI | `copilot_cli` |
| Command Code | `commandcode` |
| Qwen Code | `qwen_code` |
| Kimi Code | `kimi` |
| VS Code | `vscode` |
| Grok Build | `grok_build` |
| OMP | `omp` |
| Pi | `pi` |
| Hermes | `hermes` |
| Qoder | `qoder` |
| Factory Droid | `droid` |
| Trae | `trae` |
| WorkBuddy | `workbuddy` |
| ZCode | `zcode` |
| DeepSeek Harness | `dsh` |
`dsh` is main-only until a release after 6.4.0. The registry check above does
not cover it.
`droid`, `dsh`, `kimi`, `qoder`, `trae`, `workbuddy`, and `zcode` require
`rea-agents@6.5.0` or later. The 6.4.0 registry check above does not cover them.
See [client configuration and troubleshooting](client-configuration.md) for
configuration paths, profiles, trust policies, and schema-limit workarounds.
@@ -307,9 +505,12 @@ Hopper is separate commercial software with its own license. Its free demo has
vendor-defined limits, and a paid license is optional. REA reuses any detected
installation and preserves Hopper during uninstall.
The supported native host baseline is macOS 12+, Ubuntu 24.04+, Fedora 41+,
Nobara 44+, 64-bit Arch Linux, CachyOS, or Omarchy. Ghidra and IDA have their own provider-specific
host requirements; Windows Ghidra uses the [experimental P0 boundary](windows-ghidra-p0.md).
The automated Hopper installer supports macOS 12+, Ubuntu 24.04+, Fedora 41+,
Nobara 44+, 64-bit Arch Linux, CachyOS, or Omarchy. REA itself runs on any
Linux distribution with a recognized apt, dnf, or pacman package family, where
an existing Hopper launcher is selected through `HOPPER_LAUNCHER_PATH`. Ghidra
and IDA have their own provider-specific host requirements; Windows Ghidra uses
the [experimental P0 boundary](windows-ghidra-p0.md).
On macOS, approved setup downloads the official DMG, checks its published size
and digest, validates the application bundle, and atomically installs it to
@@ -327,8 +528,53 @@ document for the same target. REA does not currently identify, focus, or reuse
that existing GUI document, and the supported launcher exposes no attach or
reuse action for it.
REA currently declines saved Hopper database (`.hop`) targets on macOS because
the native `open database` launch route does not run REA's bridge script. To
analyze that program with REA, select its original executable as the target and
open it through Hopper; this starts the supported bridge route. This does not
recover database-only annotations or analysis changes. Saved-database analysis
on Linux continues to use its supported demo-launch route.
Hopper lease directories must be readable and owned by the current user.
REA validates and secures an opened directory before reserving a lease. If
opening that directory fails with a permission error, the diagnostic identifies
the path and recovery steps while leaving its permissions unchanged. Check
ownership and parent-directory access, then restore owner-only read, write, and
search permissions (`0700`) on a directory you own before retrying.
On supported Linux distributions, approved setup verifies Hopper's official
`.deb`, `.rpm`, or Arch package before invoking the native package manager.
The official release feed selects the current public demo without a version
pin. REA verifies the package's detached PGP signature against Hopper's stable
primary fingerprint `7C2D73BAFAF5B9A710A69DA1A26DF815099B1BCD`, then checks
that the signed package and installed launcher agree with `main_exe_hash`.
Rotating signing subkeys remain bound to that primary identity.
The feed row itself is not PGP-signed; doctor labels its display version as
`reported_release_version`, separately from the authenticated launcher identity.
The approval plan includes the caller-selected cache location. Approved setup
retains the signed archive, public key and release metadata under
`${XDG_CACHE_HOME:-$HOME/.cache}/rea/hopper-releases/<launcher-sha256>`.
An empty `XDG_CACHE_HOME` uses `$HOME/.cache`. Approved setup can repair a
damaged caller-owned entry after authenticating a fresh package; the previous
entry is preserved beside it as `<launcher-sha256>.backup-<id>`. Ordinary doctor
and launches reject damaged evidence and never repair it implicitly.
Approved publications of the same canonical cache entry share a process-backed
lease. Concurrent setup waits without moving another writer's evidence; a
failed replacement restores the previous directory when possible and reports
any retained backup path. Unavailable reads or native inspection tools do not
authorize replacing existing evidence.
Doctor and launches authenticate this evidence for offline use; an editable
digest receipt alone cannot authorize a launcher. Verification reuses at most
eight immutable package proofs per process, keyed by the complete package,
key, signature, package format and expected launcher digest. Cached file reads
still check identity and content. Without retained evidence, inspecting an
existing install requires the matching current official package and network
access. Successful live inspection can be reused within that process after
rehashing the launcher; it does not write a persistent cache or claim that a
previously observed release is still the latest. Older uncached builds require
an approved update while the feed exposes only its current release.
REA runs the supported demo build on a private Xvfb display and selects Hopper's
offered demo mode for each analysis session; it does not require the user's
desktop display. Unattended package-manager access requires
@@ -339,6 +585,13 @@ host mount and the rest of `/tmp` remain unchanged; this fallback never invokes
`sudo`. `rea doctor --provider hopper --json` reports the selected
strategy and both host and effective mount facts.
When the package-manager step fails, setup reports the failing command and its
captured output, so a package conflict is distinguishable from an authorization
failure. On CachyOS, an unversioned compatibility package such as
`zlib-ng-compat` may conflict with Hopper's versioned `zlib` dependency. Resolve
the package conflict yourself or install Hopper's official package manually,
then rerun `rea setup`.
The supported Linux demo forwards additional launches into its existing
application, even across private displays. REA therefore reserves one Linux
Hopper application per user across cooperating REA processes. A competing
@@ -360,11 +613,19 @@ sessions before removing that recovery directory and retrying.
### Launcher paths and troubleshooting
On macOS, REA uses
`/Applications/Hopper Disassembler.app/Contents/MacOS/hopper` by default.
On macOS, REA checks executable Hopper launchers in this order: the canonical
system application, the canonical application installed by `rea setup` under
`~/Applications`, then other `Hopper*.app` bundles in `/Applications` and
`~/Applications`. The Homebrew cask location is an optional `rea doctor`
diagnostic; to use a Homebrew-installed launcher for CLI/MCP startup, set
`HOPPER_LAUNCHER_PATH` to the path reported by doctor. REA does not run Homebrew
while loading runtime configuration.
Automatic discovery accepts executable regular files, including file symlinks,
and skips directories.
On Linux, it prefers executable `/opt/hopper/bin/Hopper`, then executable
`~/.local/share/rea/hopper/bin/Hopper`; if neither exists, the former remains
the diagnostic fallback. `HOPPER_LAUNCHER_PATH` overrides those choices:
`~/.local/share/rea/hopper/bin/Hopper`, followed by `/opt/hopper-v4/bin/hopper`
and `/usr/bin/hopper`; if none exists, the former remains the diagnostic
fallback. `HOPPER_LAUNCHER_PATH` overrides those choices:
```bash
export HOPPER_LAUNCHER_PATH=/absolute/path/to/Hopper
@@ -376,6 +637,11 @@ bridge readiness. For large targets, set `REA_HOPPER_STARTUP_TIMEOUT_MS` to a
decimal integer from 1 to 2,147,483,647 milliseconds in the environment that
starts REA. CLI and MCP use the same setting; invalid values fail configuration
validation. An MCP client's own request timeout must also allow enough time.
`open_binary` validates and selects a target without waiting for native provider
readiness. During Hopper startup, `binary_session.provider_operation_health`
reports `busy` at stage `launch`; `analysis_activity` describes bridge requests
and can remain idle until the socket is ready. Cleanup work reports `busy` at
stage `connection`.
If macOS denies Apple Events, allow the application that starts REA under
**System Settings → Privacy & Security → Automation**, then retry. REA releases
@@ -385,9 +651,12 @@ Arguments containing quotes, backslashes, or line breaks remain unchanged but
cannot establish that a failed handoff opened no document. Other failed handoffs
retain ownership until document closure is confirmed. If a timed-out bridge
becomes ready later, `close_binary` connects to that run, authenticates its
identity, and requests document shutdown. After a connection drops, the bridge
allows 30 seconds for an authenticated cleanup reconnect, then releases Hopper's
Python execution thread. Expiry does not establish document closure, so REA
identity, and requests document shutdown. Once its listener is ready, the bridge
allows 30 seconds for the first connection and capability validation. After a
connection drops, it allows the same grace period for an authenticated cleanup
reconnect, then releases Hopper's Python execution thread. Initial analysis is
outside this listener grace period, and authenticated native work is not bounded
by it. Expiry does not establish document closure, so REA
retains unconfirmed ownership. Retry close while the bridge remains available.
If a Linux launcher exists but cannot start, inspect its shared libraries:
@@ -408,6 +677,20 @@ If startup reports `Cannot launch Hopper! Connection is invalid.` or waits for
bridge readiness, check Hopper for a pending demo, license, or loader dialog.
Complete the dialog and retry. If REA reports `cleanup_incomplete`, follow the
retained-resource guidance below before reopening the target.
On macOS, Hopper can show an `Executing Python Script...` overlay while the REA
bridge serves an active MCP connection. Long-lived MCP clients can keep that
connection open across agent turns, so the overlay may remain visible after the
analysis calls finish; by itself, it does not establish a stalled startup. REA
does not automatically idle-close a still-connected target. When the
investigation is finished, export an Evidence bundle first if you need to keep
the session ledger, then call `close_binary`; set `snapshot_path` to save a
provider-neutral analysis snapshot as part of closing. A successful close
shuts down the REA bridge and clears its overlay; REA does not control the
macOS Hopper application lifetime.
The Hopper GUI Cancel button's behavior for this bridge has not been verified;
`close_binary` is the supported cleanup path.
Hopper serializes analysis requests. Cancelling a wait can leave provider work
running, which the session reports. Successful decompilation is cached until
a relevant rename or comment changes it.
@@ -421,12 +704,18 @@ memory has no original file offset. For FAT Mach-O, offsets refer to the origina
container file. Results retain Hopper's image-relative offset, the observed slice
base, and the source executable path.
Loader selection reads the Mach-O container header, including FAT files with a
single architecture. For FAT64, REA validates the architecture table and selected
Mach-O header, prepares a private thin image, and loads it with Hopper's native
Mach-O loader. It checks the entire source's SHA-256 while copying the slice;
source identity and reported offsets still refer to the original container.
An ambiguous architecture subtype requires an explicitly extracted thin image;
REA does not guess. Configured loader arguments remain explicit overrides.
single architecture. For FAT32 and FAT64 containers, REA validates the
architecture table and selected Mach-O header, prepares a private thin image,
and loads it with Hopper's native Mach-O loader instead of Hopper's implicit
FAT slice choice. For arm64 targets, REA ranks subtype bases 0 (plain arm64) and
2 (arm64e), ignoring capability bits for ranking while retaining the raw subtype
for slice identity. These slices' function-entry encodings decode under
`--aarch64`, ahead of Apple's arm64e.x1 variant (base subtype 12), which is
selected only when it is the arm64 family's only candidate. REA checks the
entire source's SHA-256 while copying the slice; source identity and reported
offsets still refer to the original container. Any other ambiguous architecture
subtype requires an explicitly extracted thin image; REA does not guess.
Configured loader arguments remain explicit overrides.
FAT64 is distinct from the CPU architecture: FAT32 containers can contain 64-bit
executable images. This preparation avoids the Raw Binary loader dialog observed
with native FAT64 loading on Hopper 6.1.0-demo.
@@ -457,9 +746,17 @@ Once document and process closure are confirmed, the lease can be released even
if temporary-file cleanup fails. Another client can then launch, but the owning
client must finish its retained cleanup before starting again.
An unconfirmed external document can be retried while its authenticated bridge
remains connected. If that bridge has disconnected, another close cannot confirm
the document: inspect and close the reported document in Hopper before ending
the owning REA connection.
remains available. The same authentication and process-identity capture apply
when the first successful handshake occurs during cleanup after a startup
timeout. REA can confirm closure if that exact authenticated process exits,
including while a shutdown request is in flight. A helper exit alone is
insufficient, and an application that never authenticated has no verified
process identity. If no matching bridge becomes available, inspect and close
the reported document in Hopper before ending the owning REA connection.
Export the Evidence bundle before `close_binary` clears session records.
`binary_session.cleanup_pending` remains true while provider cleanup is retained,
and provider health remains visible even with `open: false`; that flag alone
does not establish successful cleanup.
### Hopper in CI
@@ -486,7 +783,13 @@ copying license secrets into logs, or killing unrelated Hopper processes.
REA connects to an existing Ghidra installation on Linux x64/arm64, macOS x64/arm64,
or experimental Windows x64 P0.
It accepts Ghidra 12.1.x and the 64-bit full JDK declared by that installation's
`application.java.min` and `application.java.max`. Current 12.1 releases require
`application.java.min` and `application.java.max`. The JDK's reported platform must also
map to the same native decompiler directory as the host: a JDK running under
emulation (for example x86_64 under Rosetta on Apple silicon) or reporting a
non-host `os.name` fails the doctor
even when it is an otherwise accepted 64-bit full JDK. Select a matching JDK
through `JAVA_HOME`; adding another decompiler in the JVM-selected directory
does not satisfy this host-validated admission rule. Current 12.1 releases require
JDK 21 or newer and set no maximum. The bridge is verified with Ghidra 12.1.4
and JDK 21. Each installation must include the native decompiler for the host
architecture in `Ghidra/Features/Decompiler/os/<platform>/` or the corresponding
@@ -498,7 +801,7 @@ reported as incompatible instead of being admitted from its directory name, and
a header that cannot be read or recognized is reported as unknown. REA does not
build or install native tools, or change Gatekeeper quarantine settings.
The adapter exposes 25 read-only operations: thirteen inventory/name/search
The adapter exposes 26 read-only operations: fourteen inventory/name/search
operations and twelve function-analysis operations. These cover metadata,
decompilation, assembly, resolved calls, typed references, xrefs, function
dossiers, instructions, recovered data types, measured load mappings, loaded
@@ -512,7 +815,10 @@ and `set_addresses_names` name a function entry or any other mapped address
session metadata edits leave executable bytes unchanged and are discarded on close. GUI controls
require Hopper; Windows P0 remains read-only.
Windows P0 admits native x86 and x86-64 PE applications and DLLs on fixed local NTFS volumes.
Windows P0 admits native x86, x86-64, ARMNT (Thumb-2) and ARM64 PE applications and DLLs, plus explicitly
selected [raw NMOS 6502 images](raw-binary.md#nmos-6502) with a caller-supplied
entry at file offset 0, on fixed local NTFS volumes. Other raw CPU families remain
unsupported on Windows.
The npm package bundles native Job Object ownership, protected private runtime
DACLs, and handle-based path admission; no separate addon installation is needed.
See the [Windows Ghidra P0 guide](windows-ghidra-p0.md) for verified scope and
@@ -543,9 +849,22 @@ installation remains unavailable on Windows. Setup never installs Ghidra,
Java, or Python. It preserves valid detected Ghidra/JDK settings in agent
registrations. See [Windows Ghidra P0](windows-ghidra-p0.md) for provider diagnostics.
When `GHIDRA_INSTALL_DIR` is unset, doctor and the Ghidra provider also search
`PATH` for `ghidra` or `ghidraRun`, resolve the selected launcher through its
real path, and accept it only when that location belongs to a Ghidra tree with
the application metadata and headless launcher. An explicitly supplied
`GHIDRA_INSTALL_DIR` always takes precedence, including when it is invalid;
REA reports that error instead of silently selecting another installation.
This discovery is read-only and does not execute the Ghidra launcher.
Doctor validates the platform, architecture, Ghidra 12.1.x application version,
`support/analyzeHeadless` or `support/analyzeHeadless.bat`, the installation's
Java major range, 64-bit JDK bitness, and the presence of `javac`/`javac.exe`.
It also checks that the reported JVM `os.arch` and `os.name` map to the same native decompiler
directory validated for the host; unrecognized reported architectures or a
non-host operating system are refused. LaunchSupport overrides of either
property, including through helper-environment JVM option variables, are
rejected at launch.
When Java is found through `PATH`, setup records its observed JDK home so GUI
MCP clients do not depend on an incidental shell path. Setup shows every exact
environment entry in its plan, writes only after approval, and never downloads,
@@ -754,6 +1073,9 @@ errors expose Error names, messages, and available codes under
truncation markers. Darwin ownership-helper compilation failures also retain
the compiler outcome, exit code or signal, and bounded stderr; a timed-out
compiler remains distinguishable from a compiler that exited with an error.
The helper compile deadline defaults to 120,000 ms; set
`REA_SWIFTC_COMPILE_TIMEOUT_MS` to an integer between 1 and 2,147,483,647 when
a busy host needs longer.
Other rejection values retain their primitive value or an explicit type.
Shutdown warnings include the failure
kind, message, and diagnostics, while process and temporary-project cleanup
@@ -775,7 +1097,7 @@ On a controlled Windows x64 runner, use
`npm run verify:ghidra:windows`. The verifier generates a deterministic native
PE fixture from source bytes and requires the Windows native authority before
opening the provider. The controls are implemented on main; see the
[release boundary](#released-package-and-main). The lane verifies all 25 admitted
[release boundary](#released-package-and-main). The lane verifies all 26 admitted
read-only operations, digest identity, and runtime cleanup. The separate
`verify:ghidra:windows:package` lane checks the packed CLI/MCP boundary with an
ordinary user. It requires the built Windows native bundle and an existing
@@ -800,7 +1122,7 @@ For a client that requires manual configuration, use:
"mcpServers": {
"rea": {
"command": "npx",
"args": ["-y", "rea-agents@6.4.0", "mcp"]
"args": ["-y", "rea-agents@6.5.0", "mcp"]
}
}
}
+11
View File
@@ -68,6 +68,17 @@ The node vocabulary covers:
- runtime script instance;
- an explicit `unknown` kind when classification is not justified.
Static network endpoints can have `resolution: "partial"` when the analyzer
recognizes a request call but cannot resolve its target to one exact value.
The observation's `value` then preserves the source expression, and its
evidence points to the call site. Partial and exact endpoints have distinct
identities even when their displayed values match. Partial identities also retain
the source site: matching expression text at different sites does not establish
the same runtime target. This records observed source syntax; it does not
establish that the call ran or contacted that endpoint.
Graph coverage describes the analyzed artifact scope and resource limits, not
runtime execution. A partial target alone does not imply omitted source.
The directed relation vocabulary is `contains`, `loads`, `imports`, `maps_to`,
`exposes`, `sends`, `invokes`, `handles`, `calls`, `persists_to`, `observed_as`,
and `changed_from`. Provider names are deliberately absent from both
+97 -4
View File
@@ -6,7 +6,7 @@ are `inspect_analysis_view`, `trace_application_feature`,
`trace_javascript_semantics`, `compare_application_versions`,
`compare_source_to_bundle`, and `compare_javascript_export_shapes`; their CLI
equivalents use the same names with hyphens. `inspect_analysis_view` projects a
summary, module page, or one module from already completed application
summary, module page, typed module references, or one module from already completed application
Evidence; it does not walk relationships.
`inspect_analysis_view` also projects retained `inspect_binary_layout`
@@ -40,6 +40,46 @@ combine its fields with the fact's location. Semantic trace results include the
canonical context subset referenced by their returned facts, so those facts
remain self-contained in the response.
## Retained module facts
For canonical identities and retained source-range counts, query the existing
`inspect_analysis_view` tool with a typed module-reference projection:
```json
{
"source": {
"kind": "retained-evidence",
"evidence_id": "ev_ACTUAL_PARENT_ID"
},
"view": {
"kind": "javascript",
"collection": "modules",
"projection": "references",
"where": { "path": "main.js", "kind": "javascript-module" },
"offset": 0,
"limit": 16
}
}
```
Replace the parent ID with the actual analysis summary's `parent_evidence_id`.
Filters match exact retained paths and kinds. Omission includes all values;
`path: null` selects an unavailable path and `path: ""` selects a recorded empty
path. Multiple identities can share a path. The default offset is 0 and limit
is 64; follow `coverage.next_offset` for later matching rows in retained order.
Each row contains its `node_id`, kind, path, canonical identity, retained
source-range observation count, and distinct supporting authority/state/confidence
combinations in `basis`. These counts do not prove unique reference sites or
runtime loading. Use the existing module item view with the row's `node_id` for
exact source ranges and complete observations. The result retains parent/query
Evidence identity, artifact identity, limitations and unknowns. `source_coverage`
keeps selected/unselected sources, graph coverage and unknown counts separate
from page exhaustion; an exhausted or empty page cannot prove absence from an
incompletely analyzed application. CLI `inspect-analysis-view` reads the same
request from a JSON file; portable input uses `source.kind: "inline"` with the
complete parent Evidence.
## Feature tracing
Select one literal seed kind: node ID, route, string, API, IPC channel, module,
@@ -55,11 +95,21 @@ matches exactly. Otherwise the result recommends provider-neutral follow-up
tools and reports `requires-provider-analysis`; it never starts or switches a
provider implicitly.
A handoff requires the add-on's exact content identity. Unavailable unpacked
companions remain visible in the trace graph with their canonical path,
availability and container-backed Evidence. They yield no actionable handoff
until their bytes are available and the application is reanalyzed; the archive
digest does not identify the missing add-on. An empty handoff list does not prove
that the trace contains no native components. Source partial coverage remains
visible independently of handoff eligibility.
## Semantic relation tracing
`trace_javascript_semantics` queries the authenticated companion graph without
rereading mutable files. A seed may identify a semantic or application node,
literal, function fingerprint, property, endpoint, event, or boundary field.
For a recovered parse, coercion, or validation boundary, its field value is the
static operation's method name (for example, `Number`).
The query declares backward provenance, forward influence, callers, or
ownership and can restrict admitted relation kinds. REA returns every matching
seed and the complete reachable subgraph; callers do not choose node, relation,
@@ -83,10 +133,22 @@ callee bodies and built-ins such as `Object.assign`, `Reflect.set`, and
values. This uncertainty does not prove that a call actually mutated a value.
Interprocedural aliasing beyond the supported call-site references remains
outside this mutation pass.
For process configuration, only known environment value slots and canonical
argument indexes are treated as primitive when passed to calls. Unknown or
inherited selections can expose a mutable prototype, so later reads of that
configuration container retain uncertainty; earlier top-level reads and unrelated
configuration containers remain independent.
Function fingerprints commit normalized syntax, control-flow shape, relation
shape, literal sets, arity, and detected effects without using local names or
source offsets. Equal duplicate fingerprints remain ambiguous. Dynamic
source offsets. Function-local directive literals contribute their values while
preserving escape spelling: `"use\x20strict"` differs from `"use strict"`.
Quote style, formatting, and local names remain normalized. Equal duplicate
fingerprints remain ambiguous.
Fingerprints are static candidates, not behavioral-equivalence proofs or a
version-independent identity. Re-analyze affected sources and obtain new seeds
after analyzer corrections; previously retained Evidence remains queryable using
its original component commitments. Dynamic
calls/properties/names, parser recovery, unresolved handles, and unsupported API
shapes stay explicit unknowns; a missing relation is never reported as proven
behavioral absence.
@@ -112,8 +174,13 @@ graph and read literal values from `properties.value`, rather than decoding
role keys or labels. Previously stored graphs and their original IDs remain
valid inputs.
Endpoint nodes keep the exact endpoint in each observation's `properties.value`;
semantic request and response nodes keep it in `properties.endpoint`. Endpoint
Endpoint observations keep their value in `properties.value`. When
`properties.resolution` is `partial`, that value is the unresolved source
expression, with an identity distinct from an exact endpoint of the same text.
Semantic request and response nodes keep their endpoint in `properties.endpoint`. Endpoint
lookup identities are preserved independently of the primitive-value lattice's
byte budget. An oversized normalized literal can become unknown while the same
string remains an exact endpoint for tracing and comparison. Endpoint
identity keys use `value-sha256:<digest>` over the canonical JSON string when
that is shorter, or when the original value starts with the reserved digest
prefix. Other short endpoint keys remain unchanged. Endpoint labels use the
@@ -194,6 +261,13 @@ Direct return expressions, including expression-bodied arrows, are evaluated
through an execution-free value lattice. Literal object fields and direct
return sites are represented in the result. Calls, dynamic spreads, computed
keys, and parser recovery remain partial or unknown.
Rebound values are evaluated at their read position: saving an incoming parameter
before a later assignment preserves caller-dependent uncertainty, while a proven
preceding assignment can supply a constant. Unresolved ordering remains unknown.
Supported reads within object and array literals follow left-to-right evaluation;
an unproved relative order remains unknown. A later write in a separate conditional
block can still leave an earlier capture unknown because ordering across those
blocks is not proven.
Objects and arrays passed to calls, constructors, or tagged templates, used
as method receivers, or stored through property targets are not assumed
unchanged afterward. Property stores conservatively retain reference escape
@@ -208,6 +282,25 @@ arrays, and later explicit data properties and methods replace earlier object
spread references. A known array prefix does not expose values from a following
spread. Accessors, custom or uncertain iteration, unknown computed keys, and
unknown spread lengths retain conservative uncertainty.
Getter-local aliases of `this`, including direct destructuring and assignments,
retain their receiver when a returned shared reference is written. These writes
invalidate the earlier literal instead of claiming the getter executed or the
new value is known. Straight-line alias reads retain their capture order;
uncertain control flow remains conservative. A binding outside the getter or
`this` in a nested ordinary function is not attributed to the getter's receiver.
Private reads keep receiver projections separate from the private value's path.
Receiver projections follow known data fields initialized from `this` or earlier
self-referencing fields; static fields retain their declaring class identity.
An alias cycle that repeatedly selects deeper children retains escape uncertainty
at its recovered roots rather than claiming a fixed number of loop iterations.
Writes through an object getter's returned receiver retain that uncertainty
when the object is an array element, nested inside mixed object/array
containers, or assigned to a previously declared identifier. Saving the getter
result in an alias does not make its old field values immutable. Known array
indices include holes; an element after a spread has only a minimum possible
index. Unrelated properties and known array prefixes remain precise, and a
getter read without a write does not invalidate the receiver's fields.
Nested callable returns are not assigned to their parent callable. Projected
graph observations carry source ranges but never source text.
+83 -11
View File
@@ -182,6 +182,18 @@ partial Evidence. Cancellation after source analysis begins can also retain
completed facts, including when interrupted during cleanup, graph construction,
or Evidence sealing. Unexamined relationships remain unknown.
Per-file gaps identify the applicable processing phase: JavaScript source analysis
uses AST authority, while package/JSON, HTML-reference and source-map processing
use artifact-byte authority. File-analysis interruption gaps carry `analysis_status`:
`interrupted` means the phase was entered (including source-worker startup),
and `not-started` means interruption prevented that phase from beginning.
Neither state establishes that parsing completed. Remaining parsers are not run
after interruption just to populate a result.
Native add-ons remain metadata-only and acquire no JavaScript parse or analysis
gap. This also applies when their paths are outside `source_paths`; their inventory
and caller selection remain available without implying applicable AST work.
MCP returns a `partial_observation` retained reference after recording succeeds;
open it with `inspect_analysis_view` on the same connection. If the SDK stopped
waiting before receiving the reply, follow [MCP cancellation recovery](mcp-contracts.md#progress-and-cancellation)
@@ -206,6 +218,9 @@ code unit, matching the original bytes identified by the artifact digest.
Plain `.ts` artifact sources use TypeScript syntax without JSX, including
angle-bracket type assertions. `.tsx` and `.jsx` retain JSX parsing.
JavaScript sources use JavaScript grammar with JSX, never TypeScript syntax,
so valid JavaScript that TypeScript reads differently keeps its JavaScript
meaning.
Relative TypeScript imports can name their emitted `.js`, `.mjs`, or `.cjs`
counterparts while resolving to inventoried source files. This substitution
@@ -220,7 +235,10 @@ Selected bounded text is then parsed as inert data to recover:
- Webpack and Rspack chunk registrations and module factories represented as
AST literals;
- static imports, dynamic imports, CommonJS `require` calls, workers, and
service workers;
service workers (only `new Worker(new URL(path, import.meta.url))` resolves
relative to the calling module; string worker and service-worker paths use
the document base URL or Node's working directory, which static analysis
does not establish, so they keep their expression and stay unresolved);
- route, network endpoint, storage, and vendor-marker observations;
- local source-map declarations and original
source names and content digests;
@@ -270,8 +288,19 @@ RPC client syntax also contributes network endpoint candidates, in both
named operation found at the start of a string or cooked template literal —
the name must be followed by variable definitions (`($`), a directive, or a
selection set starting with a field name — or in a precompiled
`OperationDefinition` document object. A graphql-tag `loc.source.body` copy is
not reported twice. Anonymous operations are not reported.
`OperationDefinition` document object. A standalone Source object's `body`
remains an operation candidate even when it has `locationOffset` metadata.
A graphql-tag `loc.source.body` copy is suppressed only when that source
belongs to a recovered document with the same operation and its containing
document, location and source have proven private metadata ownership. Writes,
escapes and classic-script global bindings retain the source observation.
Anonymous operations are not reported.
Literal link options and precompiled document fields follow object property
order: the final definition owns the key. A later spread, dynamic computed key,
method, or accessor can make a field unknown, so an overwritten literal is not
reported as the endpoint or operation. A final explicit property after a spread
or dynamic key remains usable.
A procedure path is the server-side procedure name, not a resolved URL; the
transport URL depends on the link configuration and batching. When the proxy is
@@ -282,9 +311,26 @@ suffix. Utility calls such as `useUtils().invoice.list.fetch()`, `useQueries`,
and links whose callee was renamed by a minifier are not recognized.
Each recovered bundle module retains the exact factory-source digest. A complete
bounded AST also receives a `babel-ast-v1` structural fingerprint that ignores
ordinary identifier names while retaining syntax, literals, operators, object
keys, and member properties. If fingerprint construction reaches its structural
bounded AST also receives a `babel-ast-v3` structural fingerprint. It ignores
ordinary identifier spellings while retaining literal and directive values,
operators, fixed property names, and selected syntax flags such as computed
keys, declaration kind, function async/generator state, update-expression
prefix, method kind, and class static state. Local names used in computed keys
remain rename-resistant; private member names are also ignored while their
method kind and static state remain represented.
This fingerprint is structural evidence for candidate matching, not proof that
two modules behave equivalently. Its scope follows the AST facts listed above;
unrepresented syntax can still collide. The exact source digest remains
available to show byte-level change, and comparison reports content changes even
when structural matching succeeds.
Version comparison includes the algorithm in its structural matching key.
Retained `babel-ast-v1` results can still match other v1 results, and v2 results
can still match v2 results, but v1/v2 results cannot structurally match v3
results. Other matching tiers remain available across algorithm versions.
Re-analyze both snapshots to compare using the v3 rules.
If fingerprint construction reaches its structural
bound, the fingerprint is unavailable and its status is `truncated`; a bounded
prefix is never presented as a complete stable fingerprint.
@@ -380,10 +426,13 @@ For `.node` bindings, member names mean “requested by JavaScript syntax.” A
resolved add-on path does not convert them into verified native exports. Native
symbol verification remains a separate deep-analysis claim.
Endpoint observations preserve useful local diagnostics, including query values
and fragments, while removing only URL username/password credentials. Artifact
paths, digests, parse locations, and analysis metadata remain actionable
because REA is local-only.
Exact endpoint values preserve useful local diagnostics, including query values
and fragments, while removing URL username/password credentials when the value
parses as a URL. When a recognized request target cannot be resolved exactly,
the endpoint's partial resolution retains the original source expression with
its call-site provenance. This does not establish that the request executed.
Artifact paths, digests, parse locations, and analysis metadata remain
actionable because REA is local-only.
## Safety boundary
@@ -415,7 +464,18 @@ retained candidate strings and temporary canonical-key serialization. This is
an in-memory semantic allocation bound independent of CLI or MCP transport
budgets. Source literal bytes remain in the parsed source and artifact evidence;
when a normalized semantic value exceeds the bound, its value is unknown and
coverage is partial. Expression
coverage is partial. Direct string literals, cooked templates without
substitutions, and derived strings all use this normalization boundary. A
single exact primitive is validated without allocating deduplication or sort
state or serializing a canonical key. Configuration defaults use the same
normalizer; limited defaults retain `value_resource_limit` and an anchored
resource-limit unknown. Known null, false, zero, and empty-string defaults stay
exact. This primitive-value bound does not truncate lookup identities such as
source paths, field names or request endpoints. Full endpoint properties remain
available for endpoint queries, tracing and comparison. Each analysis records encountered limit classifications independently of
later projections: a selected healthy sibling can remain exact while whole-file
coverage reports the encountered bound. Projection preserves limiting values
from the shared property-slot owner. Expression
evaluation and provenance walks stop after 256 nested levels. Union products
and string lengths are checked before allocating combinations or concatenated
strings. When a boundary is reached, the value stays unknown, semantic coverage
@@ -615,7 +675,19 @@ href are read without surrounding whitespace or embedded tabs and newlines, and
their percent-encoded path bytes are decoded; encoded dot and separator bytes
are still rejected. CommonJS module lookups retain extension and directory
resolution.
ESM, HTML, and URL references apply URL dot segments to empty path segments:
`./assets//../app.js` selects `assets/app.js`, while a remaining empty segment
such as `./assets//app.js` stays unavailable. CommonJS keeps filesystem path
semantics and selects `app.js` for the same text.
Unresolved HTML references retain their declaration, source range, and resolution
reason in the renderer observations.
HTML script source ranges follow the HTML parser across LF, CRLF, and bare CR
line endings, preserving UTF-16 columns.
Inline JavaScript in local HTML, meaning classic or module `<script>` bodies
without a `src` attribute, is not analyzed. Each HTML file that has one gets an
unknown `parse-html-inline-scripts` scope with the exact body count and the
script type and source range of the first 100 bodies; beyond that, coverage is
truncated under the `html_inline_script_locations_per_file` limit, and the
scope's evidence counts the omitted locations. The graph names the
file and up to 10 starting lines in its limitations, and coverage is partial. Import maps, speculation rules, data blocks, and blank
bodies are not JavaScript and are not reported.
+11
View File
@@ -36,6 +36,12 @@ application starts, and REA never resumes it or sends
`Runtime.runIfWaitingForDebugger`. Resume a paused target with a separate
caller-owned debugger before requesting application runtime observations.
For a packaged Electron app, read
[`inspect_electron_fuses`](electron-fuses.md) before starting an `electron-main`
target. When `EnableNodeCliInspectArguments` is disabled or removed, Electron
ignores `--inspect` and `--inspect-brk`. A missing main-process target after
launch does not, by itself, show that fuse.
The caller supplies the literal-loopback HTTP endpoint directly. Selecting an endpoint exposes all targets it serves and their local script locations or renderer origins. A target must match the exact requested target ID and expose a same-port WebSocket reported by that endpoint. Targets explicitly marked attached are rejected rather than displacing another debugger. Node's `/json/list` omits attachment state, so REA preserves it as `null` (unknown), reports that limitation, and permits observation unless the endpoint explicitly reports `attached: true`. Local paths are canonicalized after symlink resolution.
The `attached` field in JavaScript runtime and browser target results is nullable: `true` and `false` are reported only when discovery supplied a boolean; `null` means the endpoint omitted or could not report the state. CDP `TargetInfo` worker records require an `attached` boolean; REA excludes a worker record with a missing or malformed value as an invalid protocol value.
@@ -89,6 +95,11 @@ pauses or resumes execution, calls `Runtime.runIfWaitingForDebugger`, launches
the target, instruments JavaScript, or invokes Electron APIs. Closing a call
closes only REA's WebSocket.
Each WebSocket handshake and command response has its own 20-second deadline,
separate from the caller-selected observation window. A stalled handshake or
command reports `provider_timeout` and retains observations already received;
caller cancellation still reports cancellation.
The direct observations are:
- `Debugger.scriptParsed`, establishing that a bounded script location was
+7 -4
View File
@@ -91,17 +91,20 @@ nodes. REA evaluates compatible static candidates conservatively:
1. A captured-source SHA-256 plus one matching mapped location yields
a content-and-location match.
2. One unique exact content digest can match even when no location mapping is
available. Whole-asset digests and reconstructed module-factory source
2. When no static candidate exists at a mapped location, one unique exact
content digest can match. Whole-asset digests and reconstructed module-factory source
digests remain distinguishable in the reported basis.
3. Without captured bytes, one unique mapped artifact path can produce a
medium-confidence location match.
4. Multiple candidates remain `ambiguous`; no `observed_as` edge is emitted.
With captured source and mapped candidates, only mapped content matches
qualify; unrelated content matches elsewhere are excluded.
`candidate_static_count` reports the full qualified layer/node count, and
`candidate_static_nodes` includes every unique candidate reference.
5. If captured bytes disagree with the static candidate at the mapped path,
the result is an explicit mismatch. A path never overrides a digest
disagreement.
the result is an explicit mismatch. An equal digest elsewhere cannot hide
that disagreement. If a mapped candidate's digest is unavailable and no
mapped content match is known, the comparison remains `unknown`.
6. Missing authorization mappings and incomplete or truncated input coverage
remain unknown or truncated rather than becoming absence claims.
Loaded 100 of 2247 files, more files were not shown because too many files have changed in this diff. Show more