Compare commits

...
Author SHA1 Message Date
gensokyo-dev a54464548e fix(environment)!: keep unsupported HAR body encodings opaque
Preserve declared encodings in capture Evidence and distinguish unsupported
response decoding from an absent body in shared replay and state extraction.
Use the production HAR decoder in replay regressions instead of synthesizing
its binary sidecars.

BREAKING CHANGE: replay metadata body_encoding can now be "unsupported".
Clients must treat its null body as unavailable decoded content while
body_recorded remains true; the cited Evidence retains the encoded source.
2026-10-23 19:03:32 +00:00
morluto 34563fcc9b fix(process): own bounded worker control framing
Share incremental replacement UTF-8 decoding across web and JavaScript workers, bound content before complete-line allocation and handle split CRLF consistently. Keep named schemas, request versus checkpoint failures, worker reuse and stream cleanup authority in their servers. Cover real server recovery and deterministic byte boundaries.
2026-10-24 02:35:59 +08:00
morluto f91b7f8009 fix(javascript): distinguish resolver discovery from module loading
Preserve load and resolve operations through semantic facts, worker transfer and graph Evidence. Only loads propagate executable ownership. Project ambient member writes and escapes through the shared path- and site-aware mutation owner; retain source literals, bases and uncertainty. Cover ownership and mutation boundaries through CLI and MCP.
2026-10-24 02:35:59 +08:00
morluto 635f673a8c fix(gpu): own CCOB boundaries and payload representations
Use completed codec input to establish legacy CCOB extents, preserving declaration/integrity failures independently. Reuse bounded input windows, charge actual reads and decoding attempts, and qualify recovery after incomplete decoding. Publish only established or qualified terminal extents so interruption retains completed declarations. Recognize bitcode representation in HIP bundles while leaving inner facts unsupported.

Fixes #6887
Fixes #6880
2026-10-24 02:34:35 +08:00
morluto e737e6c1c6 fix(erlang): distinguish empty optional BEAM chunks
Select legacy Atom when AtU8 is empty and skip zero-byte optional FunT. Preserve explicitly empty counted arrays, malformed nonempty failures, last-occurrence selection and original chunk provenance. Share structural fixtures across producer and public workflow regressions.

Fixes #6816
Fixes #6874
2026-10-24 02:34:35 +08:00
morluto 75a757d8ef fix(elf): distinguish compressed note storage from decoded notes
Refuse compressed note sections with typed unsupported-target semantics before framing or decoding, preserving stored-range checks and physical descriptor-offset provenance. Flutter retains independent build IDs with partial coverage when compressed notes are skipped. Verify the existing shared alignment fix with prefixed notes across classes and table kinds.

Fixes #6869
Closes #6563
2026-10-24 02:34:35 +08:00
morluto 25aaf89287 fix(sqlite): budget schema names by returned JSON text
Charge the whole-catalog admission guard by escaped decoded names, preserving raw-byte lookup identity and final worker reply limits. Keep UTF-16 inventory and detailed-schema results whose returned facts fit the documented allowance.

Fixes #6888
2026-10-24 02:34:35 +08:00
gensokyo-dev 3787ec5f83 test(ida): align native search fixtures with Unicode folding (#6898) 2026-10-23 18:31:32 +00:00
kiranandgensokyo-dev 5d2c8c2b78 feat(cli)!: export row-oriented results as JSON Lines (#5629)
* feat(cli): add JSON Lines output for row-oriented results (#5487)

- Support opt-in JSON Lines format (--format jsonl) for row-oriented results and collection views with clear record boundaries
- Preserve stable source identities and stream records deterministically line-by-line
- Emit final coverage and limitation metadata as a distinct line with https://rea.dev/schemas/jsonl-coverage-v1.json to prevent confusion with data rows
- Distinguish error outputs with https://rea.dev/schemas/jsonl-error-v1.json
- Reject non-row-oriented results that lack record boundaries with an unreadable_output error
- Document JSON Lines streaming conventions in docs/cli.md
- Add boundary tests covering empty results, multiline strings, partial coverage, large fields, and non-row rejection

* fix(cli): preserve diagnostics, provenance, and nested collections in JSONL

* fix(cli): frame classified failures and reject unrepresented JSONL facets

---------

Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
2026-10-24 02:13:28 +08:00
morluto 1de720e02e docs: stop and profile unexpectedly costly test runs 2026-10-24 02:06:18 +08:00
morluto 878da39caf chore: reconcile current main boundary fixes 2026-10-24 02:06:18 +08:00
morluto 80d5c8dc80 fix(comparison): preserve call procedure endpoint identity
Share endpoint identity and repeated-name ambiguity across call and reference dimensions. Compare same-artifact procedure entries by canonical coordinates while retaining raw reference sites and source Evidence. Keep observed comparison status separate from reconstruction proof.

Fixes #6853
2026-10-24 02:00:11 +08:00
morluto 6911a609f1 fix(model): preserve uncertainty across released GGUF layouts
Separate common row constraints from established storage sizes. Preserve Q8_1 and K-layout uncertainty, plus numeric IDs and ambiguous textual identities for historically shifted IDs16–25. Keep zero-byte facts and identity limitations independently, and propagate producer facts through totals, cancellation and retained views. Pin released source evidence and document the compatibility cost.

Fixes #6848
2026-10-24 02:00:10 +08:00
morluto 907f4d99f5 fix(flutter): derive dynamic symbol tables from their source
Decode only consumed dynamic-entry payloads. Discover PT_DYNAMIC symbols when retained sections omit SHT_DYNSYM, with table provenance governing section-index applicability. Preserve malformed present-table failures and shared source fixtures.

Fixes #6851
Fixes #6884
2026-10-24 02:00:10 +08:00
morluto 6fbe67d66e fix(apple)!: use archive UIDs for keyed nib identity
Use original object-table UIDs for inventory, hierarchy and connector references. Retain authored identity fields as data, refuse forged reference aliases and select UIKit top-level collections without substituting unrelated roots.

BREAKING CHANGE: Keyed archives that previously used authored objectID or object-id graph identities now use decimal table UIDs. Authored values remain attributes; ibtool and native NIBArchive identities retain their existing source semantics.

Fixes #6849
Fixes #6850
2026-10-24 02:00:09 +08:00
morluto 5e4004c413 fix(artifacts): preserve traversal coverage through bundle workflows
Retain producer traversal uncertainty through inventory, extraction, Interface Builder decoding and executable-root discovery. Keep known copied files and decoded facts with honest coverage; share omission normalization and bound omitted archive paths with their source reasons.

Fixes #6852
Fixes #6825
Fixes #6582
2026-10-24 02:00:09 +08:00
morluto d1fea89e1e style: format integrated boundary fixes 2026-10-24 01:58:35 +08:00
morluto 162b9e5863 test: share Frida SDK connection setup within fixtures 2026-10-24 01:57:28 +08:00
morluto 8ad3e0ea30 test(managed): split edge comparison scenarios by semantics 2026-10-24 01:57:28 +08:00
morluto 7665bf0831 test(apple): separate catalog traversal coverage cases 2026-10-24 01:57:28 +08:00
morluto a8758da09b fix(apple): account for roots skipped by ordered reads 2026-10-24 01:53:17 +08:00
morluto 5feac72c79 test: reuse Frida SDK setup across status scenarios 2026-10-24 01:49:39 +08:00
morluto 4f3d486d8f fix(setup): retain TOML comments from replaced registration code 2026-10-24 01:49:13 +08:00
morluto 1e53933786 test(session): acquire provider before mixed cleanup recovery 2026-10-24 01:49:13 +08:00
morluto 88bb8d5fe3 test(frida): pass the signal to finalization listeners 2026-10-24 01:47:31 +08:00
morluto 2732d538e8 fix(session): own target admission descriptors through finalization 2026-10-24 01:47:31 +08:00
morluto a8276d1289 docs: clarify capture comparison coverage and identity 2026-10-24 01:47:31 +08:00
morluto 6e324b2244 fix(javascript): share accessor selection and invocation effects 2026-10-24 01:47:31 +08:00
morluto 48f700ee35 fix!: retain Frida observations through shared finalization
Reuse the shared one-shot finalizer for discovery and instrumentation, preserve finalization cancellation with completed observations, and bind Frida cleanup errors to exact owners so confirmed retry recovers the primary error.

BREAKING CHANGE: instrument-with-frida JSON now returns the canonical Evidence record directly. Read normalized_result.cleanup_error instead of the earlier top-level cleanup_error wrapper field.
2026-10-24 01:47:31 +08:00
morluto 37122745b6 fix: retain status observations when session cleanup fails 2026-10-24 01:47:31 +08:00
morluto aedd394471 perf(tests): share native Interface Builder encoding production 2026-10-24 01:47:31 +08:00
morluto 78f8eca531 fix(apple): preserve bundle discovery coverage and decode failures 2026-10-24 01:47:31 +08:00
morluto 8f29c7ab66 fix(artifacts): preserve extraction coverage and provenance 2026-10-24 01:47:31 +08:00
morluto 693a3851ec test(managed): bound capacity fixtures with reserved budgets 2026-10-24 01:47:31 +08:00
morluto 357e354ac7 fix(managed): preserve member scope and nominal edge identity 2026-10-24 01:47:31 +08:00
morluto f5d791f2a1 fix(setup): preserve client-owned MCP policy on refresh 2026-10-24 01:47:31 +08:00
morluto a0c22ff012 fix(browser): preserve correspondence and uncertainty in capture comparisons 2026-10-24 01:47:31 +08:00
morluto edf7b3d32c fix(native): preserve endpoint context in function comparisons 2026-10-24 01:47:31 +08:00
morluto 211e3533b3 fix(setup): retain skill publication ownership through rollback 2026-10-24 01:47:31 +08:00
morluto b3c912ab79 fix(filesystem): retain read cleanup capabilities across retries 2026-10-24 01:47:31 +08:00
morluto 0c6fed90b7 perf: batch modern IDA callee function lookups 2026-10-24 01:47:30 +08:00
morluto 5e4ec0fa1b perf: retain call path ancestry once per visited node 2026-10-24 01:47:30 +08:00
morluto 8aa40e9a61 fix(cache): scope Evidence display refresh to matching snapshot bindings 2026-10-24 01:47:30 +08:00
morluto f8b65e7872 fix(reconstruction)!: bind proof to owner and obligation revision
Parse a named proof contract once and qualify exact obligation, owner, revision, fixture/case, and verifier/claim tuples. Preserve unavailable proof separately from applicable failed outcomes.

BREAKING CHANGE: ID-array reconstruction proof records no longer qualify for verified or ready ledgers. Producers must supply implementation and revision bindings with individual claim outcomes; ledger diagnostics now distinguish inapplicable, unavailable, and failed proof.
2026-10-24 01:47:30 +08:00
morluto 592c46b001 test(managed): preserve token identity in graph assertions
Replace ambiguous display-label sorting with exact relation assertions keyed by metadata tokens. Distinguish same-name methods and verify directed ownership, edge counts, properties and Evidence without depending on graph order.
2026-10-24 01:37:47 +08:00
morluto a496131e5a refactor(qlog): reuse capture redaction policy
Reuse credential header classification and lossless numeric redaction while preserving qlog event scopes, source literals and provenance pointers. Cover HTTP and extension fields plus explicit numeric sensitivity.
2026-10-24 01:33:41 +08:00
morluto cee14e1fda refactor(javascript): reuse semantic member-chain facts
Resolve member structure through the existing AST owner while preserving binding, shadowing and global process identity checks in their consumers.
2026-10-24 01:33:41 +08:00
morluto 92f28b2f56 refactor(evidence): share comparison payload interpretation
Decode named comparison payloads and validate nested citations at one domain boundary. Preserve workflow-specific authentication, source closure policy and validation order; cover malformed payloads and citation boundaries.
2026-10-24 01:33:41 +08:00
morluto ef475fcf8b fix(sqlite): distinguish query capacity and unavailable row sources
Use one bound JSON selection set for inventory metadata, keeping raw-name byte
comparison and filtering inside SQLite while preserving caller projection order
and repetition. Large requests reach native result-column capacity attribution.

Separate unavailable row sources from generated-expression failures. Preserve
SQLite's reason and selected table/path, offer schema-only recovery, and avoid
claiming corruption or recommending unreadable columns.

Validated UTF-8/UTF-16 identity, native inspection/worker tests and CLI/MCP failure
and recovery workflows. The large CLI selection uses the existing framed CLI
fixture to avoid host argv limits. Source bytes and identity remain unchanged.

Fixes #6855
Fixes #6604
2026-10-24 01:24:43 +08:00
morluto 39a91d4db9 fix(gpu): preserve declaration grammar and partial resource coverage
Share AMDGPU triple grammar between bundle and note owners so modern target
spellings participate in processor/feature conflict detection. Propagate CUBIN
register conflicts into object, target and report coverage.

Normalize adjacent PTX dot keywords in the lexer, removing the downstream pointer
splitter. Parse post-type alignment while distinguishing parameter and pointee
layout, preserving names and byte provenance when layout remains unknown.

Validated production-boundary matrices and public CLI/MCP coverage workflows with
inert fixtures. Correct the HIP-v3 control's ELF processor declaration. Real GPU,
assembler and compiler-produced modern-note behavior remains unverified.

Fixes #6844
Fixes #6823
Fixes #6858
Fixes #6868
Fixes #6861
2026-10-24 01:24:43 +08:00
morluto d137a4cdde fix(android): preserve metadata dialect and root manifest identity
Read Apktool 3's producer string dialect before YAML can discard hashes or
reinterpret values. Preserve older YAML behavior and decode producer escapes once.

Select the exact root AndroidManifest.xml resource in REA's JADX bridge and
derive summaries once with namespace-aware XML parsing. Preserve decoded text,
summarize before its display cap, invalidate cached facts with the loaded session,
and remove superseded upstream manifest handlers. Keep the engine pinned to 0.7.1.

Validated Apktool parser/provider and public adapter controls; compiled the bridge
against the verified pinned JAR and ran its real Java regressions plus public CLI
and stdio MCP manifest parity checks. Real Apktool execution remains unverified;
the new manifest fixtures use plain XML. No Android application was executed.

Fixes #6804
Fixes #6866
Fixes #6863
Fixes #6819
2026-10-24 01:24:43 +08:00
morluto f8f52f205a fix(python): preserve marshal names and packaged bytecode
Use one marshal surrogatepass decoder for PYZ directories and Python bytecode.
Validate ordinary UTF-8 spans with the native fatal decoder, preserving BOMs,
lone surrogates, raw bytes and marshal reference slots.

Distinguish legacy full-pyc CArchive modules from modern marshal-only modules
before reconstructing headers. Share typed source/codec failures between stream
output and completion while retaining native-read drain and cleanup ownership.

Validated focused producer, reader, lifecycle, CLI and MCP regressions, plus
public inventory/extraction of CPython 3.14-generated marshal tables and code.
No packaged application was executed. Document original versus derived bytes.

Fixes #6800
Fixes #6789
2026-10-24 01:24:43 +08:00
morluto 2443b949a4 docs: clarify evidence applicability and boundary limitations 2026-10-24 01:18:46 +08:00
morluto d0432d63c9 refactor(managed): share decoded type definition facts 2026-10-24 01:11:58 +08:00
morluto f276bcbf6c refactor(javascript): own paired graph evidence resolution 2026-10-24 01:11:58 +08:00
morluto 6cd3fa09b1 refactor(process): centralize post-run artifact verification 2026-10-24 01:11:58 +08:00
morluto 3e7e102a51 fix(rust): separate section storage from compressed stream admission
Keep validated PE padding in the selected Evidence range while consumed zlib bytes own compressed and inflate-ratio limits. Preserve typed resource failures and bounded allocation. Validated accepted padding, over-ratio and compressed-size controls through source, real CLI and stdio MCP workflows.
2026-10-24 01:00:14 +08:00
morluto 42e785925c fix(adb): accept Android Java float spellings unchanged
Use Float.valueOf grammar at shared admission and retain selected spelling through am --ef. Validated scientific notation, malformed controls and exact MCP forwarding with a source-owned ADB fixture; no device execution.

Fixes #6796
2026-10-24 01:00:14 +08:00
morluto d4415444c4 fix(native): retain processor, hidden-slice and property type facts
Separate AMDGPU processors from feature-qualified note identities. Read Apple hidden ARM64 table rows once and preserve visibility through artifact, dylib and fuse consumers. Keep Objective-C++ template commas within type attributes and incomplete metadata unknown.

Validated provider/parser and production property regressions, Electron CLI/MCP consumers, and native Apple lipo -hideARM64 output through the production slice reader and public artifact inspection. LLVM execution remains unverified.

Fixes #6806

Fixes #6799

Fixes #6801
2026-10-24 01:00:14 +08:00
morluto 6fdcac6cad fix(sqlite): preserve catalog byte identity and worker context
Match table metadata by stored name bytes and fold only ASCII code units for relationships, reusing parsed facts without per-trigger queries. Preserve caller paths on structured worker limits and V8 heap failures.

Validated invalid UTF-8 collisions, distinct UTF-16 names and real worker transport refusal through CLI and stdio MCP. Controlled stderr covers OOM classification; actual memory exhaustion was not induced.

Fixes #6795

Fixes #6607
2026-10-24 01:00:14 +08:00
morluto ccba355fa9 fix(javascript): preserve map boundaries and loader provenance
Share embedded-map version admission and derive indexed overlap from represented mappings. Carry createRequire filename bases through worker facts and graph identity. Select factory export bindings at each bundler owner.

Validated accepted and malformed source maps through real CLI and stdio MCP codec workflows; verified loader and esbuild graph output against Node runtime controls. Rebased consumer checks passed.

Fixes #6803

Fixes #6614

Fixes #6798

Fixes #6797
2026-10-24 01:00:14 +08:00
morluto d78892eb4b refactor: remove superseded Hopper and Babel compatibility paths 2026-10-24 00:54:44 +08:00
morluto 33f90f95b6 test: validate evidence and select fixtures structurally 2026-10-24 00:54:36 +08:00
morluto a32d731ed7 fix(providers): share selected-file error classification 2026-10-24 00:54:28 +08:00
morluto 8d8d85ba22 refactor(cli): share parsed output controls across consumers 2026-10-24 00:32:05 +08:00
morluto 4526862728 fix(apple): decode valid nonstandard plist table widths
Reuse preflight-parsed container references for bounded decoder-copy normalization. Preserve original artifact bytes, scalar provenance and reference order. Verify three-byte offset and reference encodings through CLI and stdio MCP.

Fixes #6805
2026-10-24 00:30:34 +08:00
morluto 6c6e2ad62b fix(wasm): preserve core inspection with optional metadata warnings
Retain warnings from validator, objdump and decoder through one diagnostic owner. Keep uninterpretable name bytes and explicit name-metadata coverage in complete results and summaries.

Fixes #6812
2026-10-24 00:30:34 +08:00
morluto 929a0a76c8 fix(rust): preserve JSON field identity and PE stream extents
Reject duplicate decoded JSON names before object conversion. Use one checksummed zlib stream parse and retained PE FileAlignment to distinguish exact zero padding from trailing payload bytes. Exercise public CLI and stdio MCP.

Fixes #6814

Fixes #6793
2026-10-24 00:27:57 +08:00
morluto 4c5fae5179 fix(il2cpp): decode valid shared-suffix string offsets
Preserve bounds, termination and UTF-8 checks; verify CLI and stdio MCP preserve names and selected artifact identity.

Fixes #6808
2026-10-24 00:27:57 +08:00
morluto fc63fe8682 fix(gpu): distinguish host descriptor failures from invalid targets
Share selected-file errno ownership with model inspection and codec result accounting with Rust. Verify actual descriptor exhaustion and unchanged-input recovery in an isolated child.

Fixes #6666
2026-10-24 00:27:57 +08:00
morluto 96ea32b7e4 perf(environment): decode only selected replay query values 2026-10-24 00:14:15 +08:00
morluto b784912811 fix(cli): share Markdown formatting with standalone reports
Route version and doctor report tables through the same formatter used for command output, preserving escaped cells and structured JSON facts.
2026-10-24 00:11:41 +08:00
morluto 87fa1b613d fix(managed): retain nested names and ReadyToRun observations
Resolve attribute display names from validated nesting and parsed member facts, admit composed names before allocation, and preserve unknown malformed chains. Keep ReadyToRun classification ahead of metadata hints while retaining each observed hint.

Fixes #6815
Fixes #6810
2026-10-24 00:11:41 +08:00
morluto ddd7ac7b84 fix(javascript): preserve Next filesystem route spelling
Apply Next-specific uppercase %5F normalization while retaining ordinary percent sequences, encoded separators, and literal percent characters as distinct source segments.

Fixes #6641
2026-10-24 00:11:41 +08:00
morluto f719dc46c5 fix(views): select every retained module path without losing identity
Use one retained-path owner for module references and items. Preserve selected paths and genuine ambiguity across module kinds; remove the superseded singular helper.

Fixes #6790
2026-10-24 00:11:41 +08:00
morluto 9a3780229b test(search): pin native Unicode substring matching
The shared literal owner already applies Unicode simple folding. Verify the reported production-helper cases across insensitive literal, sensitive literal and regex selection.

Fixes #6634
2026-10-24 00:11:41 +08:00
morlutoandEzra 678c4cfa68 fix(flutter): preserve short GNU build IDs through public workflows
Reuse the reader correction from PR #6705, and share the byte projection budget with output validation. Verify actual APK parsing through compiled CLI and stdio MCP at 0, 1, 2, 3, 64 and 65 descriptor bytes.

Co-authored-by: Ezra <64943463+ezrabrilliant@users.noreply.github.com>

Fixes #6697
2026-10-24 00:11:41 +08:00
morluto 866c72c907 docs(desktop): list all supported IL2CPP metadata versions
Match the declared probe versions and retain the version-24 layout limitation.

Fixes #6672
2026-10-24 00:11:41 +08:00
morluto 7ba05210fe fix(reference): preserve raw symlink target identity
Read link targets as bytes before strict UTF-8 admission. Retain malformed targets as unreadable with source hex; preserve literal replacement characters and valid BOM-prefixed targets.

Fixes #6765
2026-10-24 00:11:41 +08:00
morluto 51e5938776 fix(pcap): retain UTF-8 decoding failures across tool output
Validate supervised text once before version or capture interpretation. Exercise POSIX client probes through compiled CLI and stdio MCP; capture-summary native bounds remain host-specific.

Fixes #6775
2026-10-24 00:11:41 +08:00
+12 169512a35b feat(cli): report version and runtime with an explicit output format (#6530)
* feat(cli): report version and runtime with an explicit output format

`rea --version --json` and `--version --format <format>` ignored the format
and printed only the bare version, so support reports gathered the runtime
and platform by hand.

A root --version request that selects a format now renders a documented
report: package name and version, JavaScript runtime and version, platform,
architecture, and whether the runtime satisfies the supported range. It
contains no paths, environment values, user or host names, and starts no
provider. Plain --version is unchanged, --help keeps precedence, and other
output controls keep Incur's plain version.

The runtime verdict uses a new shared supportsJavaScriptRuntime rule that
rea doctor now uses too, so both judge Bun by its Node.js compatibility
version. mcp doctor's private JSON/format renderer moves to cliOutput as
renderCliDocument and is shared with the version report.

Closes #5483

* refactor(errors): centralize input failure construction

Carry caller messages through the existing schema issue owner instead of constructing and discarding an intermediate input error. Remove forwarding schema helpers and migrate equivalent constructors across investigation, firmware, dylib, process, and screenshot workflows while preserving diagnostic paths and causes.

* docs(readme): add Roman Urdu edition (#6499)

* docs(readme): add Roman Urdu edition

Register ur-Latn with a pending website catalog and preserve English command examples.

Script-tag handling follows morluto's shared helper proposed in PR #2043 (c46f606c89).

* test(website): cover completed script-tag page metadata

* fix(process): parse native teardown relation sentinels

* fix(cli): make version report privacy checks environment-independent

The CLI test rejected any report containing the OS account name, so an
account named node, linux or rea failed a correct report. Check unique
caller-controlled HOME, cwd and environment markers instead; the strict
schema still rejects any additional field.

The CLI guide said --version with other output controls prints the plain
version, but invalid combinations such as --json with --token-limit report
their existing output-flag error first. Describe both outcomes and test them.

* refactor(nativeaot): share report facts across budget stages

Centralize parse-wide identity and coverage facts while keeping conservative admission values separate from measured rows and counters. Reuse the derived truncation reason and retain the final serialization bound.

Tighten the report-budget regression so unexpected parser finalization errors cannot be swallowed by its budget search.

* fix(adb): preserve empty properties and verbatim version names

* fix(artifacts): recognize empty Zip64 containers

Fixes #6540

* fix(erlang): accept uninterpreted BEAM alignment bytes

Fixes #6517

* fix(gpu): accept PTX entries without parameter lists

Fixes #6493

* fix(python): preserve signed code-object line numbers

Fixes #6490

* refactor(ghidra): centralize load image check recording

Share expected and observed check construction across MZ, COM, and raw-binary attestations. Preserve format-specific rules and source coordinates, and derive mapping callback types from the recording owner.

* fix(inspector): preserve script identity and parse-failure facts

Fixes #6535

Fixes #6546

* fix(artifacts): recover malformed PYZ sizes without losing siblings

Fixes #6120

* fix(erlang): accept extended BEAM atom lengths

Fixes #6503

* refactor(erlang): reject overflowing BEAM atom lengths

* fix(gpu): preserve PTX and AMD target facts

Fixes #6469

Fixes #6483

* refactor(targets): preserve ambiguous MZ header evidence

* fix(adb): retain refused activity launch reports

Parse complete nonzero Activity Manager reports while preserving other command failures and their captured output. Admit dollar signs only in the activity component name for nested Android classes.

Fixes #6528

Fixes #6527

* fix(mcp): retain partial evidence through shared delivery

* fix(python): honor version-specific code equality

Fixes #6547

* fix(pyinstaller): preserve namespace declarations

Project PYZ namespace packages as metadata so their query paths do not claim output directories. This lets independent CArchive files retain the same logical path and remain inspectable and extractable.

Fixes #6539

* docs: clarify evidence ownership and verification guidance

* docs(adb): clarify emulator observation field

* docs(artifacts): clarify active-reader extraction scope

* fix(erlang): select the last repeated BEAM chunk

Fixes #6518

* fix(il2cpp): distinguish rank-one array types

Fixes #6534

* fix(python): align pyc and PYZ marshal decoding

Fixes #6560
Fixes #6559

* fix(browser): preserve frame identity across captures (#4040)

* fix(browser): preserve capture evidence fidelity

* fix(browser): align frame identity comparisons

* fix(browser): retain historical frame evidence

* fix(electron): retain same-document script owners

* fix(electron): bind frame proof to tree reads

* fix(browser): retain malformed frame scope

* fix(browser): retain WebMCP event scope

* fix(browser): retain malformed timeline identity

* fix(browser): unify frame identity and scope

* fix(electron): prove enclosing frame ancestry

* fix(electron): separate ancestry from file policy

* fix(browser): preserve transient fragment semantics

* fix(browser): retry uncertain initial frame scope

* fix(browser): preserve runtime frame identity

* refactor(browser): separate independent capture fixes

* fix(electron): detect tree-first document replacement

* fix(electron): retain same-document tree authority

---------

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

* fix(elf): resolve symbol versions from requirements

Fixes #6533

* fix(elf): follow loader version index precedence

VERDEF entries replace VERNEED names for shared indices, matching glibc lookup behavior.

* test(firmware): bind owned producers and exercise real limits

* fix(adb): preserve push target semantics

Fixes #6566

Fixes #6567

* fix(cli): merge sibling output filter slices

Fixes #6542

* fix(browser): preserve Link context semantics

Fixes #6504

Fixes #6505

Fixes #6506

* fix(python): classify historical headers and signed flags

Fixes #6568, #6487

* fix(python): normalize legacy code flags for equality

Preserve CPython 3.7–3.10 constructor flag semantics in code keys.

* fix(environment): compare captured bodies by representation

Keep JSON, text, and retained bytes distinct through fidelity comparison. Compare binary bodies by bytes, including valid UTF-8 bytes against equivalent text, and keep empty-body compatibility explicit.

Fixes #6564

* docs: clarify derived values and comparison controls

* fix(environment): scope fidelity ID normalization

Reserve equal global ID pairs and stop normalizing contested pairings. Keep numeric measurements literal, scope keyed collection identities through learning and comparison, and normalize string references only where learning established the same field as an ID.

Fixes #6565
Fixes #6550
Fixes #6557

* test(process): print the progress frames when the sparse-child bound fails (#6427)

The sparse-child capture test asserted the frame bound with a bare boolean,
so a failure only printed "expected false to be true". The assertion now
carries the running progress details (elapsed time, frames, samples,
interactions) as its failure message. This only adds diagnostics to a
failure; it does not change what passes and does not fix the flake.

Refs #5889.

🤖 Generated with Claude Code (Sonnet 5.5) 🦊

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

* fix(il2cpp): name the metadata file when linking finds malformed metadata (#6425)

* fix(flutter): honor ELF note alignment when reading build IDs (#6423)

* fix(flutter): honor ELF note alignment when reading build IDs

* test(flutter): key shared note ranges by effective alignment

Cover a program and section declaration that share one note range: differing effective alignments (8 and 4) must both be read, while declarations that both normalize to four bytes (1 and 4) still share one read. 24-byte build IDs pad identically at both alignments, so only the range key decides the read count.

* refactor(firmware): centralize audited tool requirements

* fix(javascript): associate requested bindings only with resolved native addons (#6402)

* fix(javascript): associate requested bindings only with resolved native addons

* test(javascript): cover the exact native addon association

---------

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

* fix(python): reject code keys and set members with unhashable consts (#6424)

* fix(python): reject code keys and set members with unhashable consts

* fix(python): refuse to hash a tuple that is still being read

* fix(python): hash a code object's co_consts tuple, not its item list

---------

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

* fix(model): recognize named chat templates in summaries

Count retained template content rather than only the default key; keep names-only inventories distinct.

Fixes #6569

* fix(model): retain resolved GGUF alignment under cancellation

Publish alignment from the parser when explicit metadata validates it, or when metadata completion establishes the default. Preserve that fact through later parsing and hashing failures.

Fixes #6579

* fix(environment): reject out-of-range clock offsets

Reject invalid ISO offsets while preserving valid boundary values. Validate stored clock-format offsets through the same parser so malformed imported formats cannot reach rendering.

Fixes #6524

* fix(environment): preserve submillisecond insert ordering

Compare retained submillisecond components after milliseconds, so ordered collections recognize and place distinct timestamps correctly.

Fixes #6525

* fix(environment): restrict epoch normalization to clock fields

Require a time-field token boundary before treating numeric values as epoch clocks, so identifiers such as media format codes remain literal. Clarify recognized field forms and clock offset and precision semantics in the fidelity guide.

Fixes #6551

* fix(process): preserve sibling path identities

Keep aliases on selected paths at clear text delimiters so a longer sibling filename is not rewritten as the executable. Version the normalization contract and cover source and actual PTY behavior.

Fixes #6586

* fix(sigrok): retain recording suffix for autodetection

Copy recordings under a private filename that retains the caller-selected final suffix, and pass that filename to sigrok. This restores filename-based autodetection without exposing the source directory.

Fixes #6580

* test(go): assert unresolved MZ candidate boundary

* fix(pe): ignore unused export ordinal slots

Fixes #6577

* fix(macho): recognize arm64_32 binaries

Fixes #6581

* fix(go): honor amd64p32 pointer width

Fixes #6590

* fix(pe): accept BER indefinite CMS values

Fixes #6574

* fix(browser): reject overlapping indexed source maps

Fixes #6562

* fix(browser): resolve prefixed XHTML form actions

Fixes #6561

* fix(browser): preserve well-known path case

Fixes #6571

* docs(browser): clarify parser evidence contracts

* fix(gpu): accept percent-prefixed PTX entry names

Fixes #6592

* fix(gpu): accept PTX compute target aliases

Preserve the source spelling of NVIDIA's documented compute_xx target synonym.

Fixes #6494

* fix(gpu): parse legacy PTX body parameters

PTX 1.0 through 1.3 declares kernel parameters inside the entry body. Keep later-version parameter-list parsing unchanged.

Fixes #6594

* fix(gpu): keep unknown targets out of exact filters

Fixes #6599

* fix(gpu): resolve extended CUBIN symbol sections

Fixes #6600

* docs(firmware): document the Windows Unblob submodule checkout (#6464)

On Windows, the pinned Unblob commit cannot be checked out because 11
upstream fixture paths under tests/integration/ are invalid on NTFS.
Document the verified sparse-checkout workaround and point Windows
contributors to it from CONTRIBUTING.


Claude-Session: https://claude.ai/code/session_01LjhoPGpqAwHwcCgectzRsp

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

* fix(recovery): retain file outcomes and cleanup ownership

Use shared owned-file and directory primitives throughout Wakaru discovery,
snapshots, report parsing and publication. Preserve primary read failures and
completed fingerprints when final close fails or receives cancellation.

Retain exact cleanup capabilities before retiring workspaces, block new work
while cleanup is unresolved, and bind error status to the aggregate owner so
partial cleanup and later retry keep resource attribution current.

* fix(pe): preserve biased export ordinals

Accept the format-derived ordinal range for PE export slots.

* docs: clarify parser authority and compatibility evidence

* fix(python): preserve code-name identity

Fixes #6593

* fix(managed): validate metadata operands and locals declarations

Classify MemberRef operands from parsed signatures, validate signature table references and LocalVarSig blobs, and compare observed locals declaration presence. Keep partial CIL facts and transient-budget ownership at the method reader.

Fixes #6578
Fixes #6572
Fixes #6558
Fixes #6513

* fix(python): preserve historical signed code fields

Keep signed co_stacksize and co_flags for historical magic entries whose constructors accepted them. Preserve magic 3410 with a constructor-revision limitation; reject negative values from 3411 onward.

Fixes #6487

* fix(python): compare code objects without inline caches

Fixes #6597

* fix(tests): stabilize process fixtures and malformed input coverage (#6570)

Preserve renamed-executable identity, complete-line readiness and bounded child lifetimes. Use genuinely malformed Inspector input and explicit ADB file-probe/push/hash facts while preserving cancellation, cleanup, Evidence and completeness assertions. Adopt current main Go fixtures and retain malformed COFF coverage.

Fixes #6556
Refs #6480

* test(apktool): own lifecycle fixtures and process observations

* refactor(mcp): reuse parsed Evidence tool parameters

* fix(runtime): align remaining Node security pins and setup guidance (#6316)

Derive exported Docker image versions from .nvmrc through generated package metadata. Align active setup translations with patched Node release floors and verify actual CLI exports, real Docker/agentenv, package and website workflows.

Eight built-test shards pass; three unrelated managed source failures reproduce on clean main and remain tracked in #6650.

Refs #6480

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

* fix(model): retain exact safetensors shape dimensions

Keep unsigned integer declarations exact until their owning field is interpreted. Bound actual offsets at conversion and validate shape products in declaration order, preserving large empty tensor shapes without hiding an earlier overflow.

Fixes #6627

* fix(python): read legacy TYPE_INT64 marshal values

Fixes #6609

* fix(apk): read constructed BER signer signatures

Share the bounded BER tree reader between PE certificates and APK v1 CMS, then accept constructed OCTET STRING signature values while retaining the declared signer and certificate facts. Embedded X.509 certificate decoding keeps its DER requirement.

Fixes #6603

* fix(adb): count Unicode requested permissions

Count requested-permission rows containing Unicode names, and keep counting subsequent rows in the same dumpsys section.

Fixes #6584

* fix(signals): reject missing sigrok channels

Recognize the exact selected-channel diagnostic even when sigrok-cli exits successfully, while preserving partial producer output in the invalid-input details. Cover both public JSON adapters and add the missing-channel case to the Linux real-tool verifier.

Fixes #6631

* fix(elf): reuse recognized machine architecture facts

Preserve the shared EM_HEXAGON classification in Go and Rust image reports.

Fixes #6629

* fix(asn1): decode four-byte BER lengths unsigned

Accumulate length octets arithmetically so a four-octet length with bit 31 set remains a positive unsigned value and is rejected by the containing-range check rather than becoming a negative length.

* fix(gpu): preserve parameter and target declarations

Decode observed V2 CUDA parameters, retain qualified PTX parameter names and entry resource counts, and preserve family and feature target declarations through selection and consistency checks.

Fixes #6624
Fixes #6625
Fixes #6626
Fixes #6618
Fixes #6613
Fixes #6606

* fix(captures): preserve canonical TShark alias fields

TShark resolves selected field aliases before writing packet JSON. Keep the requested selector in Evidence while accepting the canonical field key produced for it.

Fixes #6595

* fix(qlog): mark records with unknown STREAM FIN incomplete

Keep known STREAM ranges and the unknown FIN value, while listing the source event among incomplete fact records so summary and retained views agree.

Fixes #6598

* fix(pcap): retain absolute HTTP request targets

Reuse parsed URI facts and preserve uncertainty when mixed request records cannot all be resolved from the producer output.

Fixes #6537

* fix(rust): preserve historical cargo-auditable feature facts

Accept observed feature arrays, preserve omitted values as unknown across producer revisions, and keep older retained reports compatible. Reuse bounded JSON sizing before graph expansion.

Fixes #6622

* fix(ptx): normalize parameter declarators once

Fixes #6606

* fix(managed): compare observed call targets

Compare aligned managed call edges by their reported declaring type and method name, keeping missing target facts unknown without comparing build-local tokens. Document the static comparison limits.

Fixes #6630

* fix(artifacts): compare PyInstaller declaration facts

Compare stable producer-tagged declarations independently of materialized payloads. Preserve missing facts as unknown without applying PyInstaller rules to foreign metadata or nested container entries.

Fixes #6484
Fixes #6485

* test(managed): retain derived facts in cloned method fixtures

Clone caller-scoped call and field facts with the fixture methods. Keep known CIL changes alongside unavailable target metadata in the missing-row controls.

* refactor(apk): consume shared BER node facts

Remove the APK reader's pass-through children and content projections so format consumers read the shared BER owner's nodes directly. Keep only the encoded-span helper needed for exact issuer and certificate bytes, and describe CMS BER as this parser's structural owner.

* fix(adb): delimit permission section by indentation

* test(managed): preserve field-access owner identities

* fix(managed): share reference instruction semantics

Use the same parsed method/field operand predicate when producing member edges and comparing call and field shapes. Exclude ldtoken and operands without a value from both interpretations.

* test(managed): align source goldens with metadata validation

Fixes #6650

* fix(native): consolidate observed address validation

* fix(gdb): separate queued work from MI synchronization

Queued console followers have not dispatched MI records. Use active protocol requests when deciding whether the admitted close can verify and detach its inferior.

Fixes #6661

* fix(gdb): wait for the outer console command to finish

After a running result, queue a tokenized read-only MI query to establish that GDB has returned to its command loop. Keep collecting through intermediate inferior stops and retain the query reply as protocol Evidence.

Fixes #6660

* fix(erlang): retain admission cancellation reason

Preserve the caller's AbortSignal reason when BEAM inspection is cancelled before provider admission. Keep the production filesystem boundary and late-completion contracts covered.

Fixes #6658

* fix(desktop): probe GameMaker data filenames

Select conventional Linux and macOS game data files for the existing bounded FORM/GEN8 probe. Add service-boundary regressions and document the selected names.

Fixes #6659

* fix(harmony): exclude raw resources from manifests

Classify known resource paths once and keep a raw resource named module.json from changing the package model or appearing as a manifest. Add a filesystem-boundary regression and clarify the documented hint.

Fixes #6638

* fix(gpu): bound embedded section reads

Fixes #6657

* fix(gpu): retain resources across entry pragmas

Fixes #6651

* fix(reference-source): preserve raw POSIX filename identity

Mark directories with non-UTF-8 names unsupported without following replacement-character paths, while retaining readable sibling observations.

Fixes #6653

* fix(reference-imports): retain exact dynamic import targets

Use the shared exact-literal reader for dynamic imports, preserving expression-free templates and transparent TypeScript wrappers while leaving computed targets unknown.

Fixes #6654

* fix(reference-imports): retain exported import-equals dependencies

Route exported external import-equals declarations through the same relationship owner as plain declarations so their required target is not dropped.

Fixes #6655

* fix(managed): respect native module case semantics

Preserve the observed native format when comparing module identities so ELF case-only differences remain mismatches while unknown platform behavior stays unresolved.

Fixes #6652

* fix(il2cpp): reject duplicate method tokens per image

Reject duplicate MethodDef row identities within one owning image before publishing native addresses or Ghidra seeds, while retaining cross-image reuse and folded code addresses.

Fixes #6610

* refactor(reference-imports): share exact literal parsing

Route legacy nested import-expression handling through the same exact-literal owner as dynamic imports, removing a duplicate string-only check.

Fixes #6654

* fix(model): preserve optional metadata and tensor alignment

Fixes #6656
Fixes #6637
Fixes #6636

* fix(macho): infer legacy Intel simulator platforms

Fixes #6649

* fix(macho)!: preserve ambiguous chained import names

Keep the exact producer expression and report unknown symbol identity when chained-import text cannot distinguish a binding addend from a literal name suffix.

Fixes #6648

BREAKING CHANGE: inspect_macho import names can be null when identity is ambiguous. Use raw_name for the observed expression and name_status to distinguish it from an exact symbol name.

* fix(elf): retain raw export name bytes

Fixes #6647

* refactor(macho): separate dyld import and export parsing

Keep mode-specific inputs and parsed row types at their respective parser boundaries.

* fix(macho): preserve import identity in retained views

Use distinct row contracts for imports and exports, retaining ambiguous names and historical import defaults without adding import-only fields to export pages. Verify the SDK-advertised schemas and both facets through MCP.

* style(desktop): format GameMaker probe coverage

* chore(deps): update Vite security patch and dependency inventory (#6416)

Update Vite/PostCSS and refresh the lock-derived inventory while retaining TSRS platform selection and runtime dependency versions.

Source/static checks, installed-platform signatures, full/production audits, real CLI/MCP workflows and all four package platforms pass. Portable CI retains the independently reproduced main PTX fixture failure tracked in #6675; use the owner-authorized existing PR-only baseline exception. Refs #6480.

* fix(release): isolate npm publication from dependency execution (#6681)

Keep dependency execution in read-only release jobs and validate the original qualified archive with Node built-ins before publication. Refresh registry state on every publisher attempt; retain exact bytes/channel verification, v1 read-only recovery, and installed CLI/MCP checks before MCP publication.

Static/docs checks, artifact/registry/recovery tests and real package/rehearsal verification pass. All four hosted package platforms pass. Portable CI retains the independently reproduced main PTX failure in #6675 and fail-fast missing coverage; use the owner-authorized existing PR-only exception, with limitations documented in the PR. Refs #6480.

* refactor(json): consolidate representation size accounting

Share bounded UTF-8 and UTF-16 JSON measurements across state serving, write retention and source-map admission. Remove duplicate counters and distinguish retained state estimates from encoded bytes while preserving limits and exact refusal diagnostics.

* refactor(android): own search summaries at the producer boundary

Reuse validated class pages for inline pagination audits while retaining complete provider replies. Move retention into the shared Android workflow and remove the MCP-only service wrapper and application text reparser. Preserve unknown JSON metadata, late cancellation and retention failures.

* test: align boundary fixtures with producer contracts

Model GDB console completion, use real directory reads and cleanup for late-enumeration failures, and distinguish supported PTX pointer qualifiers from unknown declarations. Preserve original assertion and cleanup failures through scoped test finalizers.

* fix(rust): retain parsed defaults in cached Evidence views

Borrow sealed reports only when they match the parsed facts; otherwise cache the owned normalized snapshot. Keep historical feature omissions unknown through saved bundles and repeated retained pages without changing the source Evidence.

Fixes #6689

* fix(model): retain parsed headers when hashing is interrupted

Share partial observation ownership across GGUF and Safetensors. Preserve completed header facts through cancellation and MCP error delivery without claiming finalized digests or file identity.

Fixes #6684

* fix(gpu): preserve PTX parameter and target facts

Fixes #6664
Fixes #6675
Fixes #6680
Fixes #6695

* fix(go): read inline build strings from ELF mappings

Fixes #6716

* fix(rust): scan read-only ELF load data

Fixes #6663

* fix(il2cpp): deduplicate consistent ELF registration aliases

Fixes #6715

* fix(il2cpp): track unresolved ELF relocation widths

Fixes #6701

* fix(artifacts): preserve raw directory name identity

Fixes #6665

* fix(desktop): retain unsupported filename coverage

Fixes #6665

* fix: preserve unknown historical module resolution

Fixes #6679

Fixes #6678

Fixes #6612

* fix: retain proven createRequire URL bases

Fixes #6612

Fixes #6678

* fix(apple): preserve keyed archive table identity

Fixes #6662

* fix(plist): reject XML dictionary keys without values

Fixes #6682

* fix(apple): preserve NIBArchive special real values

Fixes #6583

* fix(signals): reject missing targets and invalid UTF-8

Fixes #6671

Fixes #6693

* fix(beam): accept bytes after declared form

Fixes #6717

* fix(artifacts): compare source map target identity

Fixes #6706

* fix(pyinstaller): retain executable member metadata

Fixes #6702

* fix(pyinstaller): decode interned unicode names

Fixes #6676

* fix(pyinstaller): retain colliding archive observations

Keep source-distinct CArchive rows and decoded PYZ children in inventory. Refuse only when extraction reaches a destination conflict.

Fixes #6674

* fix(dotnet): preserve managed metadata comparison facts

Decode VARARG sentinels and allow omitted empty blob heaps while retaining failures for referenced missing data. Compare field and catch targets using owning parsed facts instead of build-local tokens.

Fixes #6720
Fixes #6686
Fixes #6713
Fixes #6712

* fix(dotnet): keep TypeRef catch identity unknown

Fixes #6712

* test(apple): assert projected interface builder facts

* fix(dotnet): correct metadata comparison regressions

* fix(il2cpp): deduplicate aliased registration fields

Fixes #6715

* test(pyinstaller): match interned name producer encoding

Use Python 3.13 PYZ magic with the matching archive version and exercise FLAG_REF with TYPE_INTERNED.

Fixes #6676

* test(dotnet): split managed comparison blocks

* test(apple): isolate NIBArchive real projection group

* fix(parsers): correct Rust import and PTX alignment control

* fix: recognize aliased createRequire bindings

Fixes #6612

Fixes #6678

* test(apple): retain NIBArchive limitation path

* fix(il2cpp): deduplicate aliased metadata registration

Fixes #6715

* test(managed): retain observed target changes across member remaps

* fix: track require.resolve references

Fixes #6678

* test(pyinstaller): encode PYZ list length correctly

Write the marshal list length as its raw little-endian count and surface inventory limitations when completeness regresses.

Fixes #6676

* fix(managed): retain unknown catch identity beside observed name changes

* fix: retain require.resolve source facts

Fixes #6678

* refactor(javascript): name retained require call facts explicitly

* fix: resolve selected require call members

Fixes #6678

* fix(javascript): keep unresolved created-loader bases unknown

* refactor(artifacts): centralize completed file outcomes

Project completed values and cancellation from the descriptor owner once across artifact consumers. Preserve completed void outcomes and format-specific validation and cleanup policies.

Keep later aborts from replacing captured APK and SQLite close failures, and retain the exact APK cancellation reason. Exercise real descriptors, public workflows and cleanup retries.

* fix(javascript): isolate package facts from host prototypes

Build frozen package field snapshots with a null prototype and restrict reference-source main lookup to own properties. Preserve declared null and empty values, index fallback, source identity and unavailable manifest evidence.

Compare inherited main and exports lookup with the native Node resolver, retain package observation coverage and verify CLI/MCP package workflows.

* fix(captures): advertise offline capture inspection without a browser provider (#6725)

* fix(parsers): preserve format boundaries and query occurrence identity

Normalize parsed facts at their owning boundaries across PE exports and certificates, ELF section names, GPU bundles, IL2CPP generic types, dyld slice selection, source maps, environment replay, and JavaScript mapped arguments. Preserve partial facts and explicit unsupported cases; clarify the corresponding documentation.

Validation: 831 affected tests passed with one existing skip across 23 files; build, typecheck/lint, documentation, formatting, and 2133-file test discovery passed. Format controls are synthetic; real Unity, GPU, and ARM-device execution is unverified.

Fixes #6709
Fixes #6688
Fixes #6719
Fixes #6718
Fixes #6704
Fixes #6703
Fixes #6692
Fixes #6723
Fixes #6687
Fixes #6726
Fixes #6696
Fixes #6691
Fixes #6727

* fix(process): require observed environment ownership tokens

Centralize run-token extraction at the environment observation boundary and require an own field. Build decoded OS environment records without host prototypes while retaining source names, empty values and last-assignment precedence.

Reject inherited matching tokens before cleanup signaling, preserve normal owned-group behavior, and verify live Linux process environments alongside native CLI/MCP capture workflows.

* fix(parsers): retain independent facts and boundary identity

Share bounded ELF section-name decoding across native and note readers; retain read-only ranges and coverage reasons when names are unknown. Preserve ELF symbol-version bytes, dyld-bound method uncertainty, partial gRPC-Web trailers, and per-parent model identity checks. Compare GPU signatures as bytes and require a PTX module header. Share selected-path errno classification across EVM and BEAM.

Validation: 397 shared-parser tests passed (one existing skip), 5 MCP schema tests passed, 14 EVM error-mapping tests passed, and 23 protobuf CLI/MCP matrix cases passed across focused runs. Build, typecheck/lint, docs, formatting, and 2134-file discovery passed. Full EVM provider workflow remains locally unverified on Darwin arm64; format controls are synthetic.

Fixes #6738
Fixes #6737
Fixes #6745
Fixes #6740
Fixes #6749
Fixes #6747
Fixes #6739

* fix(setup): preserve source keys across configuration boundaries

* fix(setup): keep the CLI-only skill choice offered under a client ID (#6401)

* fix(setup): keep the CLI-only skill choice offered under a client ID

* test(setup): cover the accept-all CLI-only skill selection

* fix(apple): report a read-denied selected archive as host access denial (#6479)

* fix(apple): report a read-denied selected archive as host access denial

inspect-keyed-archive reported a host read refusal on the caller-selected bundle entry as artifact_operation_failed/execution_failure with reason io, leaving EACCES or EPERM only inside message text. The caller selected that entry, so the refusal is a selection outcome with its own typed classification, system code and read-access recovery.

DirectoryArtifactReader now marks a refused entry open with DirectoryArtifactEntryAccessDenied, which retains the host system code and the selected entry path. KeyedArchiveReader, which owns the caller's selection, maps it to the shared AnalysisAccessDeniedError, and artifact failure translation keeps that typed error instead of replacing it with a generic io failure. Other readers and other reader failures are unchanged; every existing reason keeps its current projection.

Adds the access_denied projection coverage plus a real host refusal through the production reader and a non-permission control.

* fix(apple): carry cleanup ownership and cover raw refusals for a denied selection

Addresses the two review findings on the denied-selection classification:

- The typed host access denial now carries the reader's cleanup observation, so a denied open that left a resource owned does not project as fully cleaned.
- Adds coverage that starts from raw host EACCES/EPERM/EIO refusals with the real DirectoryArtifactReader open path, so the marker itself is exercised rather than constructed by the test. EIO stays an artifact failure.

* fix(native): report plist symlink loops and overlong paths as selection failures (#6371)

Co-authored-by: Abelo9996 <Abelo9996@users.noreply.github.com>

* docs(cli): document opt-in Bash completion setup (#6250)

* fix(doctor): check only the current platform's Hopper launcher candidates (#6253)

* fix(doctor): check only the current platform's Hopper launcher candidates

Doctor built one launcher list for every platform, so on macOS it could
report a Linux-only path as a healthy Hopper ahead of a renamed bundle,
and setup then registered that path. Give per-platform automatic
candidates one owner that runtime configuration and doctor both read,
scan renamed macOS bundles and probe the Homebrew cask only on macOS,
and share runtime's renamed-bundle scan with the doctor host.

Fixes #6228

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

* fix(doctor): keep the renamed Hopper bundle scan asynchronous

Sharing runtime's synchronous scan made the doctor host read
~/Applications with readdirSync, which blocks the event loop on a slow
or network-mounted home. Give MacHopperPaths an asynchronous scan for
doctor beside runtime's lazy synchronous one, sharing the directory
filter and candidate builder, and check that both return the same
candidates.

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

* test(doctor): model renamed launchers with runtime's POSIX joins

Since #5235, runtime builds macOS launcher paths with posix.join, so on
Windows the discovery table's native-join renamed launcher no longer
matched runtime's candidate. Model launchers with posix.join and keep
filesystem setup native, as MacHopperPaths.test.ts does.

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

---------

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

* fix(entropy): name offset when the byte window starts past EOF (#6495)

Fixes #6482

Co-authored-by: Abelo9996 <Abelo9996@users.noreply.github.com>

* fix(sqlite): report result-column capacity refusals as capacity (#6327)

A projection wider than SQLite's native result-column limit failed with the
generic "too many columns in result set" SQL error. The inspector could not
attribute that error to a generated column, so the worker reported "format"
and the parent blamed the caller's path as invalid_format even though the
selected database was readable.

Classify the refusal from SQLite's own column limit for the private
connection, carry the selected and engine counts through the worker reply,
and project it as a capacity resource constraint. The database stays
readable, no partial rows are returned, and the recovery names the column
counts and splitting the projection across calls.

* fix(web): refuse an existing script-export destination as invalid input (#6313)

* fix(web): refuse an existing script-export destination as invalid input

export_web_scripts refused an existing output directory or link correctly,
but reported it as artifact_operation_failed / execution_failure with no
field issue or recovery. SafeOutputTree.create raises that refusal as a
path failure before any output tree exists, and the export mapper wrapped
it as an artifact failure.

Attribute pre-admission path refusals to output_directory as an
AnalysisInputError, as environment export already does for its directory.
Both now share one helper owned beside SafeOutputTree; failures after the
tree is admitted keep their artifact semantics.

Fixes #6029

* fix(artifacts): type SafeOutputTree destination refusals

Admission conversion treated any cleanup-free path failure raised before
admission as a refusal of the selected directory, so an acquired-tree setup
failure, such as being unable to restrict a created root to 0700, blamed
valid output_directory or directory input.

SafeOutputTree.create now raises SafeOutputTreeDestinationRefusal for the
refusals of the selection itself: a relative or reserved root, an existing
root, and an accepted existing root that is not an empty directory. The shared
conversion accepts only that type, directly or wrapped by
SafeOutputTreeCreationFailure, so script and environment export no longer
track admission separately.

* fix(evm): reject repeated container keys in Solidity metadata (#6230)

* fix(evm): reject repeated container keys in Solidity metadata

* test(evm): check the repeat-key diagnostic for deeply nested keys

* docs(ci): add a repeatable static analysis example (#6496)

* docs(ci): add a repeatable static analysis example

* fix(ci): isolate example reports and repair the guide link

---------

Co-authored-by: gongfpp <22683417+gongfpp@users.noreply.github.com>

* fix(setup): retain unresolved environment facts when deriving budgets

* fix(parsers): preserve format evidence and caller input identity

Retain bounded FAT and offset-tagged PE observations through archive hashing,
classification, and content nodes. Preserve PyInstaller member paths according
to the observed carrier dialect and share APK v1 signing-name semantics.

Read SQLite's implicit sqlite_schema table without inventing a stored schema
declaration. Distinguish unsupported ELF numbering and decoded-name admission
from malformed data and fixed scan allowances. Preserve Sigrok basenames and
classify matching explicit input-format refusals at their owning boundary.

Validation: 554 distinct tests passed, 3 skipped across serial focused runs;
build, typecheck/lint, docs, formatting, and discovery (2141 files) passed.
Sigrok command-boundary simulators do not establish real-tool decoding.
Small repeated public PyInstaller CLI runs had equivalent inventory facts;
warm median elapsed 0.5431s before / 0.5426s after, with no speed claim.

Fixes #6733
Fixes #6741
Fixes #6722
Fixes #6732
Fixes #6736
Fixes #6734
Fixes #6764
Fixes #6766
Refs #6739

* fix(parsers): respect container facts and real decoding boundaries

Use zip.js's parsed symlink fact for inventory and open refusal. Treat an
unmatched Godot trailer as an unconfirmed candidate while preserving failures
for confirmed malformed packs. Report entropy path-resolution failures at the
selected-input boundary.

Separate Go header discovery from inline string extents in ELF and Mach-O,
and apply the aggregate content allowance after validating real source ranges.
Normalize binary plist UID widths through the existing scalar-copy owner,
budget exact clone growth, preserve source identity, and refuse inexact UIDs.

Validation: 389 distinct tests passed, one Linux /proc test skipped on Darwin;
build, typecheck/lint, docs, single-thread formatting, and discovery (2142 files)
passed. CLI/MCP Go fixtures and public ZIP/Godot/keyed-archive workflows ran.
Real producer prevalence and external provider execution were not established.

Repeated ordinary keyed-archive CLI inventory facts and estimated reply tokens
were equal before/after. Warm median elapsed was 0.5166s / 0.5177s; no speed claim.

Fixes #6751
Fixes #6763
Fixes #6754
Fixes #6767
Fixes #6762
Fixes #6779

* refactor(mcp): consolidate Evidence recording and operation progress

* fix(setup): reuse URL component admission for Hopper releases

* perf(cli): avoid loading analysis commands for version reports

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
Co-authored-by: Hifza Zafar <152292315+hifzabuildsai@users.noreply.github.com>
Co-authored-by: Can <159528514+eco-null@users.noreply.github.com>
Co-authored-by: eco-null <eco-null@users.noreply.github.com>
Co-authored-by: Cassie_Fox <249835913+CassieFox23@users.noreply.github.com>
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-authored-by: Wade <160517347+Sriram-PR@users.noreply.github.com>
Co-authored-by: RRFZ <247457381+Plastic-time@users.noreply.github.com>
Co-authored-by: pablodiazjorge <102741971+pablodiazjorge@users.noreply.github.com>
Co-authored-by: Ezra Brilliant <64943463+ezrabrilliant@users.noreply.github.com>
Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
Co-authored-by: GamerDubz <105694977+GamerDubz@users.noreply.github.com>
Co-authored-by: raidnouredden-ux <raidnouredden@gmail.com>
Co-authored-by: 吴佳浩Alben <35291372+XiaoBinGan@users.noreply.github.com>
Co-authored-by: Abel Yagubyan <abelyagubyan@berkeley.edu>
Co-authored-by: Abelo9996 <Abelo9996@users.noreply.github.com>
Co-authored-by: ShuaixinHuang <1836656514@qq.com>
Co-authored-by: Paritosh Lahre <155938586+SandstromPL@users.noreply.github.com>
Co-authored-by: Dery Ferdika <114888155+DeryFerd@users.noreply.github.com>
Co-authored-by: gongfpp <1360187968@qq.com>
Co-authored-by: gongfpp <22683417+gongfpp@users.noreply.github.com>
2026-10-24 00:06:03 +08:00
morluto 8bf882f401 test(environment): derive capacity fixtures from current contracts 2026-10-24 00:05:06 +08:00
morluto 9066444f6b fix(source-maps): reuse admitted source URL identities 2026-10-24 00:01:19 +08:00
morluto 7fbc2b5652 refactor(cli): remove redundant runtime provider contexts 2026-10-23 23:54:04 +08:00
morluto ecd997e512 fix(managed): compare catch types by scoped nominal identity
Preserve boundary facts and uncertainty through their consumers. Add focused production-boundary regressions and update affected documentation.

Fixes #6760
2026-10-23 23:49:26 +08:00
morluto c409a8bbe8 fix(managed): preserve native module names under platform loader rules
Preserve boundary facts and uncertainty through their consumers. Add focused production-boundary regressions and update affected documentation.

Fixes #6758
2026-10-23 23:49:26 +08:00
morluto 17cd07342d fix(cli): preserve selected JSON members and Markdown table text
Preserve boundary facts and uncertainty through their consumers. Add focused production-boundary regressions and update affected documentation.

Fixes #6756
Fixes #6752
Fixes #6755
2026-10-23 23:49:26 +08:00
morluto 52b0a54afd fix(evm): reject nested duplicate CBOR map keys
Preserve boundary facts and uncertainty through their consumers. Add focused production-boundary regressions and update affected documentation.

Fixes #6780
2026-10-23 23:49:26 +08:00
morluto 7fd722e15b fix(native): retain Objective-C implementation alternatives 2026-10-23 23:42:39 +08:00
morluto 567b559474 fix(call-path): retain unresolved outgoing-call caveats
Preserve format and Evidence semantics at their owning boundaries, with focused regression coverage and documentation where applicable.

Fixes #6788
2026-10-23 23:36:21 +08:00
morluto 607f509505 fix(flutter): ignore unused ELF DT_NULL values
Preserve format and Evidence semantics at their owning boundaries, with focused regression coverage and documentation where applicable.

Fixes #6768
2026-10-23 23:36:21 +08:00
morluto 40c235c642 fix(comparison): preserve decoding uncertainty and known string facts
Preserve format and Evidence semantics at their owning boundaries, with focused regression coverage and documentation where applicable.

Fixes #6774
2026-10-23 23:36:21 +08:00
morluto a06dbd509d fix(browser): retain unknown resource sizes and accessibility roles
Preserve format and Evidence semantics at their owning boundaries, with focused regression coverage and documentation where applicable.

Fixes #6773
Fixes #6786
2026-10-23 23:36:21 +08:00
morluto 3d24683b06 fix(search): share Unicode literal matching and preserve field boundaries
Preserve format and Evidence semantics at their owning boundaries, with focused regression coverage and documentation where applicable.

Fixes #6785
Fixes #6714
Fixes #6735
Fixes #6750
2026-10-23 23:36:21 +08:00
morluto b3995a2ae5 fix(apple): preserve archived className fields and descriptor identity
Preserve format and Evidence semantics at their owning boundaries, with focused regression coverage and documentation where applicable.

Fixes #6776
2026-10-23 23:36:21 +08:00
morluto 5af154fc4b fix(gpu): accept numeric percent-prefixed PTX identifiers
Preserve format and Evidence semantics at their owning boundaries, with focused regression coverage and documentation where applicable.

Fixes #6771
2026-10-23 23:36:21 +08:00
morluto 7f613149be fix(model): accept released GGUF writer metadata and vocab-only output
Preserve format and Evidence semantics at their owning boundaries, with focused regression coverage and documentation where applicable.

Fixes #6772
Fixes #6787
2026-10-23 23:36:21 +08:00
morluto 50af8de5e8 fix(reconstruction): validate all retained case Evidence references
Preserve format and Evidence semantics at their owning boundaries, with focused regression coverage and documentation where applicable.

Fixes #6770
2026-10-23 23:36:21 +08:00
morluto e2efd10b70 fix(comparison): reuse parsed native address coordinates 2026-10-23 23:26:29 +08:00
morluto 6a34be416b refactor(adapters): remove redundant error and provider facades 2026-10-23 23:15:46 +08:00
morluto b33f68deb7 refactor(providers): separate identities from workflow profiles 2026-10-23 23:14:14 +08:00
morluto 60accad2de fix(setup): reuse URL component admission for Hopper releases 2026-10-23 23:04:32 +08:00
morluto c343b787f0 refactor(mcp): consolidate Evidence recording and operation progress 2026-10-23 23:04:32 +08:00
morluto 6ba6aea5eb fix(parsers): respect container facts and real decoding boundaries
Use zip.js's parsed symlink fact for inventory and open refusal. Treat an
unmatched Godot trailer as an unconfirmed candidate while preserving failures
for confirmed malformed packs. Report entropy path-resolution failures at the
selected-input boundary.

Separate Go header discovery from inline string extents in ELF and Mach-O,
and apply the aggregate content allowance after validating real source ranges.
Normalize binary plist UID widths through the existing scalar-copy owner,
budget exact clone growth, preserve source identity, and refuse inexact UIDs.

Validation: 389 distinct tests passed, one Linux /proc test skipped on Darwin;
build, typecheck/lint, docs, single-thread formatting, and discovery (2142 files)
passed. CLI/MCP Go fixtures and public ZIP/Godot/keyed-archive workflows ran.
Real producer prevalence and external provider execution were not established.

Repeated ordinary keyed-archive CLI inventory facts and estimated reply tokens
were equal before/after. Warm median elapsed was 0.5166s / 0.5177s; no speed claim.

Fixes #6751
Fixes #6763
Fixes #6754
Fixes #6767
Fixes #6762
Fixes #6779
2026-10-23 23:02:07 +08:00
morluto bd4f5e9c19 fix(parsers): preserve format evidence and caller input identity
Retain bounded FAT and offset-tagged PE observations through archive hashing,
classification, and content nodes. Preserve PyInstaller member paths according
to the observed carrier dialect and share APK v1 signing-name semantics.

Read SQLite's implicit sqlite_schema table without inventing a stored schema
declaration. Distinguish unsupported ELF numbering and decoded-name admission
from malformed data and fixed scan allowances. Preserve Sigrok basenames and
classify matching explicit input-format refusals at their owning boundary.

Validation: 554 distinct tests passed, 3 skipped across serial focused runs;
build, typecheck/lint, docs, formatting, and discovery (2141 files) passed.
Sigrok command-boundary simulators do not establish real-tool decoding.
Small repeated public PyInstaller CLI runs had equivalent inventory facts;
warm median elapsed 0.5431s before / 0.5426s after, with no speed claim.

Fixes #6733
Fixes #6741
Fixes #6722
Fixes #6732
Fixes #6736
Fixes #6734
Fixes #6764
Fixes #6766
Refs #6739
2026-10-23 22:54:28 +08:00
morluto a718037033 fix(setup): retain unresolved environment facts when deriving budgets 2026-10-23 22:45:41 +08:00
gongfppandgongfpp 1b3d57ca8a docs(ci): add a repeatable static analysis example (#6496)
* docs(ci): add a repeatable static analysis example

* fix(ci): isolate example reports and repair the guide link

---------

Co-authored-by: gongfpp <22683417+gongfpp@users.noreply.github.com>
2026-10-23 23:42:58 +09:00
Wade fd782cf2b1 fix(evm): reject repeated container keys in Solidity metadata (#6230)
* fix(evm): reject repeated container keys in Solidity metadata

* test(evm): check the repeat-key diagnostic for deeply nested keys
2026-10-23 23:42:45 +09:00
Muhammad Hamza b5dba08dba fix(web): refuse an existing script-export destination as invalid input (#6313)
* fix(web): refuse an existing script-export destination as invalid input

export_web_scripts refused an existing output directory or link correctly,
but reported it as artifact_operation_failed / execution_failure with no
field issue or recovery. SafeOutputTree.create raises that refusal as a
path failure before any output tree exists, and the export mapper wrapped
it as an artifact failure.

Attribute pre-admission path refusals to output_directory as an
AnalysisInputError, as environment export already does for its directory.
Both now share one helper owned beside SafeOutputTree; failures after the
tree is admitted keep their artifact semantics.

Fixes #6029

* fix(artifacts): type SafeOutputTree destination refusals

Admission conversion treated any cleanup-free path failure raised before
admission as a refusal of the selected directory, so an acquired-tree setup
failure, such as being unable to restrict a created root to 0700, blamed
valid output_directory or directory input.

SafeOutputTree.create now raises SafeOutputTreeDestinationRefusal for the
refusals of the selection itself: a relative or reserved root, an existing
root, and an accepted existing root that is not an empty directory. The shared
conversion accepts only that type, directly or wrapped by
SafeOutputTreeCreationFailure, so script and environment export no longer
track admission separately.
2026-10-23 23:42:33 +09:00
Dery Ferdika 89fea8130a fix(sqlite): report result-column capacity refusals as capacity (#6327)
A projection wider than SQLite's native result-column limit failed with the
generic "too many columns in result set" SQL error. The inspector could not
attribute that error to a generated column, so the worker reported "format"
and the parent blamed the caller's path as invalid_format even though the
selected database was readable.

Classify the refusal from SQLite's own column limit for the private
connection, carry the selected and engine counts through the worker reply,
and project it as a capacity resource constraint. The database stays
readable, no partial rows are returned, and the recovery names the column
counts and splitting the projection across calls.
2026-10-23 23:42:20 +09:00
Abel YagubyanandAbelo9996 e8dad898da fix(entropy): name offset when the byte window starts past EOF (#6495)
Fixes #6482

Co-authored-by: Abelo9996 <Abelo9996@users.noreply.github.com>
2026-10-23 23:42:07 +09:00
Paritosh LahreandClaude Opus 5.5 8f91e60558 fix(doctor): check only the current platform's Hopper launcher candidates (#6253)
* fix(doctor): check only the current platform's Hopper launcher candidates

Doctor built one launcher list for every platform, so on macOS it could
report a Linux-only path as a healthy Hopper ahead of a renamed bundle,
and setup then registered that path. Give per-platform automatic
candidates one owner that runtime configuration and doctor both read,
scan renamed macOS bundles and probe the Homebrew cask only on macOS,
and share runtime's renamed-bundle scan with the doctor host.

Fixes #6228

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

* fix(doctor): keep the renamed Hopper bundle scan asynchronous

Sharing runtime's synchronous scan made the doctor host read
~/Applications with readdirSync, which blocks the event loop on a slow
or network-mounted home. Give MacHopperPaths an asynchronous scan for
doctor beside runtime's lazy synchronous one, sharing the directory
filter and candidate builder, and check that both return the same
candidates.

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

* test(doctor): model renamed launchers with runtime's POSIX joins

Since #5235, runtime builds macOS launcher paths with posix.join, so on
Windows the discovery table's native-join renamed launcher no longer
matched runtime's candidate. Model launchers with posix.join and keep
filesystem setup native, as MacHopperPaths.test.ts does.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 23:41:54 +09:00
ShuaixinHuang c9204b96d4 docs(cli): document opt-in Bash completion setup (#6250) 2026-10-23 23:41:42 +09:00
Abel YagubyanandAbelo9996 f290b27299 fix(native): report plist symlink loops and overlong paths as selection failures (#6371)
Co-authored-by: Abelo9996 <Abelo9996@users.noreply.github.com>
2026-10-23 23:41:29 +09:00
吴佳浩Alben eab61d02a1 fix(apple): report a read-denied selected archive as host access denial (#6479)
* fix(apple): report a read-denied selected archive as host access denial

inspect-keyed-archive reported a host read refusal on the caller-selected bundle entry as artifact_operation_failed/execution_failure with reason io, leaving EACCES or EPERM only inside message text. The caller selected that entry, so the refusal is a selection outcome with its own typed classification, system code and read-access recovery.

DirectoryArtifactReader now marks a refused entry open with DirectoryArtifactEntryAccessDenied, which retains the host system code and the selected entry path. KeyedArchiveReader, which owns the caller's selection, maps it to the shared AnalysisAccessDeniedError, and artifact failure translation keeps that typed error instead of replacing it with a generic io failure. Other readers and other reader failures are unchanged; every existing reason keeps its current projection.

Adds the access_denied projection coverage plus a real host refusal through the production reader and a non-permission control.

* fix(apple): carry cleanup ownership and cover raw refusals for a denied selection

Addresses the two review findings on the denied-selection classification:

- The typed host access denial now carries the reader's cleanup observation, so a denied open that left a resource owned does not project as fully cleaned.
- Adds coverage that starts from raw host EACCES/EPERM/EIO refusals with the real DirectoryArtifactReader open path, so the marker itself is exercised rather than constructed by the test. EIO stays an artifact failure.
2026-10-23 23:41:16 +09:00
pablodiazjorge 6c1b7f86d1 fix(setup): keep the CLI-only skill choice offered under a client ID (#6401)
* fix(setup): keep the CLI-only skill choice offered under a client ID

* test(setup): cover the accept-all CLI-only skill selection
2026-10-23 23:41:04 +09:00
morluto 98ca6c8270 fix(setup): preserve source keys across configuration boundaries 2026-10-23 22:32:06 +08:00
morluto 99d2d7ad22 fix(parsers): retain independent facts and boundary identity
Share bounded ELF section-name decoding across native and note readers; retain read-only ranges and coverage reasons when names are unknown. Preserve ELF symbol-version bytes, dyld-bound method uncertainty, partial gRPC-Web trailers, and per-parent model identity checks. Compare GPU signatures as bytes and require a PTX module header. Share selected-path errno classification across EVM and BEAM.

Validation: 397 shared-parser tests passed (one existing skip), 5 MCP schema tests passed, 14 EVM error-mapping tests passed, and 23 protobuf CLI/MCP matrix cases passed across focused runs. Build, typecheck/lint, docs, formatting, and 2134-file discovery passed. Full EVM provider workflow remains locally unverified on Darwin arm64; format controls are synthetic.

Fixes #6738
Fixes #6737
Fixes #6745
Fixes #6740
Fixes #6749
Fixes #6747
Fixes #6739
2026-10-23 22:18:43 +08:00
morluto a68cfd7db0 fix(process): require observed environment ownership tokens
Centralize run-token extraction at the environment observation boundary and require an own field. Build decoded OS environment records without host prototypes while retaining source names, empty values and last-assignment precedence.

Reject inherited matching tokens before cleanup signaling, preserve normal owned-group behavior, and verify live Linux process environments alongside native CLI/MCP capture workflows.
2026-10-23 22:13:12 +08:00
morluto 5f702dd6cc fix(parsers): preserve format boundaries and query occurrence identity
Normalize parsed facts at their owning boundaries across PE exports and certificates, ELF section names, GPU bundles, IL2CPP generic types, dyld slice selection, source maps, environment replay, and JavaScript mapped arguments. Preserve partial facts and explicit unsupported cases; clarify the corresponding documentation.

Validation: 831 affected tests passed with one existing skip across 23 files; build, typecheck/lint, documentation, formatting, and 2133-file test discovery passed. Format controls are synthetic; real Unity, GPU, and ARM-device execution is unverified.

Fixes #6709
Fixes #6688
Fixes #6719
Fixes #6718
Fixes #6704
Fixes #6703
Fixes #6692
Fixes #6723
Fixes #6687
Fixes #6726
Fixes #6696
Fixes #6691
Fixes #6727
2026-10-23 22:01:16 +08:00
Wade 4193bc5bf8 fix(captures): advertise offline capture inspection without a browser provider (#6725) 2026-10-23 22:00:42 +08:00
morluto 610966b836 fix(javascript): isolate package facts from host prototypes
Build frozen package field snapshots with a null prototype and restrict reference-source main lookup to own properties. Preserve declared null and empty values, index fallback, source identity and unavailable manifest evidence.

Compare inherited main and exports lookup with the native Node resolver, retain package observation coverage and verify CLI/MCP package workflows.
2026-10-23 21:57:14 +08:00
morluto 2bcd7e6d13 refactor(artifacts): centralize completed file outcomes
Project completed values and cancellation from the descriptor owner once across artifact consumers. Preserve completed void outcomes and format-specific validation and cleanup policies.

Keep later aborts from replacing captured APK and SQLite close failures, and retain the exact APK cancellation reason. Exercise real descriptors, public workflows and cleanup retries.
2026-10-23 21:42:40 +08:00
morluto 7fd6e486ae fix(javascript): keep unresolved created-loader bases unknown 2026-10-23 21:36:43 +08:00
morluto 9b598076e3 fix: resolve selected require call members
Fixes #6678
2026-10-23 21:35:30 +08:00
morluto e966b9f9a5 refactor(javascript): name retained require call facts explicitly 2026-10-23 21:34:45 +08:00
morluto 1a430f055d fix: retain require.resolve source facts
Fixes #6678
2026-10-23 21:29:20 +08:00
morluto 529038f557 fix(managed): retain unknown catch identity beside observed name changes 2026-10-23 21:28:11 +08:00
morluto 66e25e7912 test(pyinstaller): encode PYZ list length correctly
Write the marshal list length as its raw little-endian count and surface inventory limitations when completeness regresses.

Fixes #6676
2026-10-23 21:28:11 +08:00
morluto ae013ad23c fix: track require.resolve references
Fixes #6678
2026-10-23 21:28:11 +08:00
morluto 0f04043a5a test(managed): retain observed target changes across member remaps 2026-10-23 21:28:11 +08:00
morluto 8cd10d4727 fix(il2cpp): deduplicate aliased metadata registration
Fixes #6715
2026-10-23 21:28:11 +08:00
morluto 7a93cfd118 test(apple): retain NIBArchive limitation path 2026-10-23 21:28:11 +08:00
morluto 6cb0684372 fix: recognize aliased createRequire bindings
Fixes #6612

Fixes #6678
2026-10-23 21:28:11 +08:00
morluto f09dde0cc9 fix(parsers): correct Rust import and PTX alignment control 2026-10-23 21:28:11 +08:00
morluto fabb68bb9b test(apple): isolate NIBArchive real projection group 2026-10-23 21:28:11 +08:00
morluto 97f6678578 test(dotnet): split managed comparison blocks 2026-10-23 21:28:10 +08:00
morluto 72270ac275 test(pyinstaller): match interned name producer encoding
Use Python 3.13 PYZ magic with the matching archive version and exercise FLAG_REF with TYPE_INTERNED.

Fixes #6676
2026-10-23 21:28:10 +08:00
morluto f40bd0c4fb fix(il2cpp): deduplicate aliased registration fields
Fixes #6715
2026-10-23 21:28:10 +08:00
morluto 4f0c33e8c4 fix(dotnet): correct metadata comparison regressions 2026-10-23 21:28:10 +08:00
morluto 5e16e30c6b test(apple): assert projected interface builder facts 2026-10-23 21:28:10 +08:00
morluto b305104e4a fix(dotnet): keep TypeRef catch identity unknown
Fixes #6712
2026-10-23 21:28:10 +08:00
morluto a8b8ff2acf fix(dotnet): preserve managed metadata comparison facts
Decode VARARG sentinels and allow omitted empty blob heaps while retaining failures for referenced missing data. Compare field and catch targets using owning parsed facts instead of build-local tokens.

Fixes #6720
Fixes #6686
Fixes #6713
Fixes #6712
2026-10-23 21:28:10 +08:00
morluto 5bcf71c660 fix(pyinstaller): retain colliding archive observations
Keep source-distinct CArchive rows and decoded PYZ children in inventory. Refuse only when extraction reaches a destination conflict.

Fixes #6674
2026-10-23 21:28:10 +08:00
morluto c4a59101ac fix(pyinstaller): decode interned unicode names
Fixes #6676
2026-10-23 21:28:10 +08:00
morluto 045c7dd63a fix(pyinstaller): retain executable member metadata
Fixes #6702
2026-10-23 21:28:10 +08:00
morluto 131c1f1219 fix(artifacts): compare source map target identity
Fixes #6706
2026-10-23 21:28:10 +08:00
morluto 8520d425d0 fix(beam): accept bytes after declared form
Fixes #6717
2026-10-23 21:28:10 +08:00
morluto f6230e2f9d fix(signals): reject missing targets and invalid UTF-8
Fixes #6671

Fixes #6693
2026-10-23 21:28:10 +08:00
morluto 43e7917ec2 fix(apple): preserve NIBArchive special real values
Fixes #6583
2026-10-23 21:28:10 +08:00
morluto 869d2bc2ec fix(plist): reject XML dictionary keys without values
Fixes #6682
2026-10-23 21:28:10 +08:00
morluto 78ce91f1c2 fix(apple): preserve keyed archive table identity
Fixes #6662
2026-10-23 21:28:10 +08:00
morluto 4bad48d873 fix: retain proven createRequire URL bases
Fixes #6612

Fixes #6678
2026-10-23 21:28:10 +08:00
morluto 857a75baac fix: preserve unknown historical module resolution
Fixes #6679

Fixes #6678

Fixes #6612
2026-10-23 21:28:10 +08:00
morluto 992e930b86 fix(desktop): retain unsupported filename coverage
Fixes #6665
2026-10-23 21:28:10 +08:00
morluto 47900f99c5 fix(artifacts): preserve raw directory name identity
Fixes #6665
2026-10-23 21:28:10 +08:00
morluto bdf13a63c4 fix(il2cpp): track unresolved ELF relocation widths
Fixes #6701
2026-10-23 21:28:10 +08:00
morluto d31e42699f fix(il2cpp): deduplicate consistent ELF registration aliases
Fixes #6715
2026-10-23 21:28:10 +08:00
morluto d2739960af fix(rust): scan read-only ELF load data
Fixes #6663
2026-10-23 21:28:10 +08:00
morluto 386359060b fix(go): read inline build strings from ELF mappings
Fixes #6716
2026-10-23 21:28:10 +08:00
morluto aa52ff6f4b fix(gpu): preserve PTX parameter and target facts
Fixes #6664
Fixes #6675
Fixes #6680
Fixes #6695
2026-10-23 21:28:10 +08:00
morluto eed99a588e fix(model): retain parsed headers when hashing is interrupted
Share partial observation ownership across GGUF and Safetensors. Preserve completed header facts through cancellation and MCP error delivery without claiming finalized digests or file identity.

Fixes #6684
2026-10-23 21:28:10 +08:00
morluto 02940796f1 fix(rust): retain parsed defaults in cached Evidence views
Borrow sealed reports only when they match the parsed facts; otherwise cache the owned normalized snapshot. Keep historical feature omissions unknown through saved bundles and repeated retained pages without changing the source Evidence.

Fixes #6689
2026-10-23 21:28:10 +08:00
morluto bc411c24be test: align boundary fixtures with producer contracts
Model GDB console completion, use real directory reads and cleanup for late-enumeration failures, and distinguish supported PTX pointer qualifiers from unknown declarations. Preserve original assertion and cleanup failures through scoped test finalizers.
2026-10-23 21:22:52 +08:00
morluto 76b56957e9 refactor(android): own search summaries at the producer boundary
Reuse validated class pages for inline pagination audits while retaining complete provider replies. Move retention into the shared Android workflow and remove the MCP-only service wrapper and application text reparser. Preserve unknown JSON metadata, late cancellation and retention failures.
2026-10-23 21:12:42 +08:00
morluto 3c5c887a2c refactor(json): consolidate representation size accounting
Share bounded UTF-8 and UTF-16 JSON measurements across state serving, write retention and source-map admission. Remove duplicate counters and distinguish retained state estimates from encoded bytes while preserving limits and exact refusal diagnostics.
2026-10-23 20:54:19 +08:00
gensokyo-dev 5d6b8a0ab6 fix(release): isolate npm publication from dependency execution (#6681)
Keep dependency execution in read-only release jobs and validate the original qualified archive with Node built-ins before publication. Refresh registry state on every publisher attempt; retain exact bytes/channel verification, v1 read-only recovery, and installed CLI/MCP checks before MCP publication.

Static/docs checks, artifact/registry/recovery tests and real package/rehearsal verification pass. All four hosted package platforms pass. Portable CI retains the independently reproduced main PTX failure in #6675 and fail-fast missing coverage; use the owner-authorized existing PR-only exception, with limitations documented in the PR. Refs #6480.
2026-10-23 12:27:24 +00:00
raidnouredden-ux 8c4f96ef47 chore(deps): update Vite security patch and dependency inventory (#6416)
Update Vite/PostCSS and refresh the lock-derived inventory while retaining TSRS platform selection and runtime dependency versions.

Source/static checks, installed-platform signatures, full/production audits, real CLI/MCP workflows and all four package platforms pass. Portable CI retains the independently reproduced main PTX fixture failure tracked in #6675; use the owner-authorized existing PR-only baseline exception. Refs #6480.
2026-10-23 11:44:32 +00:00
morluto a7f967cf24 style(desktop): format GameMaker probe coverage 2026-10-23 19:30:13 +08:00
morluto 23e68cbdfb fix(macho): preserve import identity in retained views
Use distinct row contracts for imports and exports, retaining ambiguous names and historical import defaults without adding import-only fields to export pages. Verify the SDK-advertised schemas and both facets through MCP.
2026-10-23 19:29:41 +08:00
morluto aa357d6d91 refactor(macho): separate dyld import and export parsing
Keep mode-specific inputs and parsed row types at their respective parser boundaries.
2026-10-23 19:26:17 +08:00
morluto 22df16186f fix(elf): retain raw export name bytes
Fixes #6647
2026-10-23 19:25:29 +08:00
morluto 5d0d243a45 fix(macho)!: preserve ambiguous chained import names
Keep the exact producer expression and report unknown symbol identity when chained-import text cannot distinguish a binding addend from a literal name suffix.

Fixes #6648

BREAKING CHANGE: inspect_macho import names can be null when identity is ambiguous. Use raw_name for the observed expression and name_status to distinguish it from an exact symbol name.
2026-10-23 19:25:29 +08:00
morluto 1280e7e513 fix(macho): infer legacy Intel simulator platforms
Fixes #6649
2026-10-23 19:25:29 +08:00
morluto 5c7755636d fix(model): preserve optional metadata and tensor alignment
Fixes #6656
Fixes #6637
Fixes #6636
2026-10-23 19:23:08 +08:00
morluto d20c7b09a6 refactor(reference-imports): share exact literal parsing
Route legacy nested import-expression handling through the same exact-literal owner as dynamic imports, removing a duplicate string-only check.

Fixes #6654
2026-10-23 19:20:53 +08:00
morluto 139fd52477 fix(il2cpp): reject duplicate method tokens per image
Reject duplicate MethodDef row identities within one owning image before publishing native addresses or Ghidra seeds, while retaining cross-image reuse and folded code addresses.

Fixes #6610
2026-10-23 19:20:53 +08:00
morluto 72dd48a44c fix(managed): respect native module case semantics
Preserve the observed native format when comparing module identities so ELF case-only differences remain mismatches while unknown platform behavior stays unresolved.

Fixes #6652
2026-10-23 19:20:52 +08:00
morluto 9200adea61 fix(reference-imports): retain exported import-equals dependencies
Route exported external import-equals declarations through the same relationship owner as plain declarations so their required target is not dropped.

Fixes #6655
2026-10-23 19:18:47 +08:00
morluto 16e64d58ff fix(reference-imports): retain exact dynamic import targets
Use the shared exact-literal reader for dynamic imports, preserving expression-free templates and transparent TypeScript wrappers while leaving computed targets unknown.

Fixes #6654
2026-10-23 19:18:47 +08:00
morluto 8f9b2ab948 fix(reference-source): preserve raw POSIX filename identity
Mark directories with non-UTF-8 names unsupported without following replacement-character paths, while retaining readable sibling observations.

Fixes #6653
2026-10-23 19:18:47 +08:00
morluto 42086fdba7 fix(gpu): retain resources across entry pragmas
Fixes #6651
2026-10-23 19:18:47 +08:00
morluto 8695ca8257 fix(gpu): bound embedded section reads
Fixes #6657
2026-10-23 19:18:47 +08:00
morluto b443b5a89b fix(harmony): exclude raw resources from manifests
Classify known resource paths once and keep a raw resource named module.json from changing the package model or appearing as a manifest. Add a filesystem-boundary regression and clarify the documented hint.

Fixes #6638
2026-10-23 19:18:47 +08:00
morluto 79b3f4694d fix(desktop): probe GameMaker data filenames
Select conventional Linux and macOS game data files for the existing bounded FORM/GEN8 probe. Add service-boundary regressions and document the selected names.

Fixes #6659
2026-10-23 19:18:47 +08:00
morluto 0e3e577568 fix(erlang): retain admission cancellation reason
Preserve the caller's AbortSignal reason when BEAM inspection is cancelled before provider admission. Keep the production filesystem boundary and late-completion contracts covered.

Fixes #6658
2026-10-23 19:18:47 +08:00
morluto 5bb01ca9a3 fix(gdb): wait for the outer console command to finish
After a running result, queue a tokenized read-only MI query to establish that GDB has returned to its command loop. Keep collecting through intermediate inferior stops and retain the query reply as protocol Evidence.

Fixes #6660
2026-10-23 19:18:47 +08:00
morluto 86a0428d5f fix(gdb): separate queued work from MI synchronization
Queued console followers have not dispatched MI records. Use active protocol requests when deciding whether the admitted close can verify and detach its inferior.

Fixes #6661
2026-10-23 19:18:47 +08:00
morluto 99a5ffae3b fix(native): consolidate observed address validation 2026-10-23 19:11:38 +08:00
morluto 5a5bcb6b54 test(managed): align source goldens with metadata validation
Fixes #6650
2026-10-23 19:10:32 +08:00
morluto 749ae11b15 fix(managed): share reference instruction semantics
Use the same parsed method/field operand predicate when producing member edges and comparing call and field shapes. Exclude ldtoken and operands without a value from both interpretations.
2026-10-23 19:07:46 +08:00
morluto 007f0d214d test(managed): preserve field-access owner identities 2026-10-23 19:07:46 +08:00
morluto e90782283c fix(adb): delimit permission section by indentation 2026-10-23 19:07:46 +08:00
morluto 486111725e refactor(apk): consume shared BER node facts
Remove the APK reader's pass-through children and content projections so format consumers read the shared BER owner's nodes directly. Keep only the encoded-span helper needed for exact issuer and certificate bytes, and describe CMS BER as this parser's structural owner.
2026-10-23 19:07:46 +08:00
morluto 40717c931b test(managed): retain derived facts in cloned method fixtures
Clone caller-scoped call and field facts with the fixture methods. Keep known CIL changes alongside unavailable target metadata in the missing-row controls.
2026-10-23 19:07:46 +08:00
morluto 142cf188a7 fix(artifacts): compare PyInstaller declaration facts
Compare stable producer-tagged declarations independently of materialized payloads. Preserve missing facts as unknown without applying PyInstaller rules to foreign metadata or nested container entries.

Fixes #6484
Fixes #6485
2026-10-23 19:07:46 +08:00
morluto c70eefad50 fix(managed): compare observed call targets
Compare aligned managed call edges by their reported declaring type and method name, keeping missing target facts unknown without comparing build-local tokens. Document the static comparison limits.

Fixes #6630
2026-10-23 19:07:46 +08:00
morluto 74c23005c0 fix(ptx): normalize parameter declarators once
Fixes #6606
2026-10-23 19:07:46 +08:00
morluto 9c4dd7eb23 fix(rust): preserve historical cargo-auditable feature facts
Accept observed feature arrays, preserve omitted values as unknown across producer revisions, and keep older retained reports compatible. Reuse bounded JSON sizing before graph expansion.

Fixes #6622
2026-10-23 19:07:46 +08:00
morluto 1ece9630e6 fix(pcap): retain absolute HTTP request targets
Reuse parsed URI facts and preserve uncertainty when mixed request records cannot all be resolved from the producer output.

Fixes #6537
2026-10-23 19:07:46 +08:00
morluto 4524bbd8df fix(qlog): mark records with unknown STREAM FIN incomplete
Keep known STREAM ranges and the unknown FIN value, while listing the source event among incomplete fact records so summary and retained views agree.

Fixes #6598
2026-10-23 19:07:46 +08:00
morluto d97db6ebaf fix(captures): preserve canonical TShark alias fields
TShark resolves selected field aliases before writing packet JSON. Keep the requested selector in Evidence while accepting the canonical field key produced for it.

Fixes #6595
2026-10-23 19:07:46 +08:00
morluto 25aa298c61 fix(gpu): preserve parameter and target declarations
Decode observed V2 CUDA parameters, retain qualified PTX parameter names and entry resource counts, and preserve family and feature target declarations through selection and consistency checks.

Fixes #6624
Fixes #6625
Fixes #6626
Fixes #6618
Fixes #6613
Fixes #6606
2026-10-23 19:07:46 +08:00
morluto 3a2074b633 fix(asn1): decode four-byte BER lengths unsigned
Accumulate length octets arithmetically so a four-octet length with bit 31 set remains a positive unsigned value and is rejected by the containing-range check rather than becoming a negative length.
2026-10-23 19:07:46 +08:00
morluto ef05713d86 fix(elf): reuse recognized machine architecture facts
Preserve the shared EM_HEXAGON classification in Go and Rust image reports.

Fixes #6629
2026-10-23 19:07:46 +08:00
morluto 48cd33af18 fix(signals): reject missing sigrok channels
Recognize the exact selected-channel diagnostic even when sigrok-cli exits successfully, while preserving partial producer output in the invalid-input details. Cover both public JSON adapters and add the missing-channel case to the Linux real-tool verifier.

Fixes #6631
2026-10-23 19:07:45 +08:00
morluto 80d3fd40f1 fix(adb): count Unicode requested permissions
Count requested-permission rows containing Unicode names, and keep counting subsequent rows in the same dumpsys section.

Fixes #6584
2026-10-23 19:07:45 +08:00
morluto 55185abec8 fix(apk): read constructed BER signer signatures
Share the bounded BER tree reader between PE certificates and APK v1 CMS, then accept constructed OCTET STRING signature values while retaining the declared signer and certificate facts. Embedded X.509 certificate decoding keeps its DER requirement.

Fixes #6603
2026-10-23 19:07:45 +08:00
morluto d0a8e3c60d fix(python): read legacy TYPE_INT64 marshal values
Fixes #6609
2026-10-23 19:07:45 +08:00
morluto 0d44afed7e fix(model): retain exact safetensors shape dimensions
Keep unsigned integer declarations exact until their owning field is interpreted. Bound actual offsets at conversion and validate shape products in declaration order, preserving large empty tensor shapes without hiding an earlier overflow.

Fixes #6627
2026-10-23 19:07:45 +08:00
GamerDubzandgensokyo-dev 520f8e8042 fix(runtime): align remaining Node security pins and setup guidance (#6316)
Derive exported Docker image versions from .nvmrc through generated package metadata. Align active setup translations with patched Node release floors and verify actual CLI exports, real Docker/agentenv, package and website workflows.

Eight built-test shards pass; three unrelated managed source failures reproduce on clean main and remain tracked in #6650.

Refs #6480

Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
2026-10-23 11:03:03 +00:00
morluto 19520d0008 refactor(mcp): reuse parsed Evidence tool parameters 2026-10-23 18:53:38 +08:00
morluto 23def04096 test(apktool): own lifecycle fixtures and process observations 2026-10-23 18:40:23 +08:00
gensokyo-dev cc8835c7a3 fix(tests): stabilize process fixtures and malformed input coverage (#6570)
Preserve renamed-executable identity, complete-line readiness and bounded child lifetimes. Use genuinely malformed Inspector input and explicit ADB file-probe/push/hash facts while preserving cancellation, cleanup, Evidence and completeness assertions. Adopt current main Go fixtures and retain malformed COFF coverage.

Fixes #6556
Refs #6480
2026-10-23 10:39:28 +00:00
morluto 3784a8e547 fix(python): compare code objects without inline caches
Fixes #6597
2026-10-23 18:31:56 +08:00
morluto c6ab965062 fix(python): preserve historical signed code fields
Keep signed co_stacksize and co_flags for historical magic entries whose constructors accepted them. Preserve magic 3410 with a constructor-revision limitation; reject negative values from 3411 onward.

Fixes #6487
2026-10-23 18:31:02 +08:00
morluto 667402846c fix(managed): validate metadata operands and locals declarations
Classify MemberRef operands from parsed signatures, validate signature table references and LocalVarSig blobs, and compare observed locals declaration presence. Keep partial CIL facts and transient-budget ownership at the method reader.

Fixes #6578
Fixes #6572
Fixes #6558
Fixes #6513
2026-10-23 18:30:45 +08:00
morluto f921cb943e fix(python): preserve code-name identity
Fixes #6593
2026-10-23 18:25:56 +08:00
morluto a281455f0b docs: clarify parser authority and compatibility evidence 2026-10-23 18:25:11 +08:00
morluto 73d85f6299 fix(pe): preserve biased export ordinals
Accept the format-derived ordinal range for PE export slots.
2026-10-23 18:23:54 +08:00
morluto 0473a3091c fix(recovery): retain file outcomes and cleanup ownership
Use shared owned-file and directory primitives throughout Wakaru discovery,
snapshots, report parsing and publication. Preserve primary read failures and
completed fingerprints when final close fails or receives cancellation.

Retain exact cleanup capabilities before retiring workspaces, block new work
while cleanup is unresolved, and bind error status to the aggregate owner so
partial cleanup and later retry keep resource attribution current.
2026-10-23 18:21:31 +08:00
Ezra BrilliantandClaude Opus 5.5 3ed9f4b7e4 docs(firmware): document the Windows Unblob submodule checkout (#6464)
On Windows, the pinned Unblob commit cannot be checked out because 11
upstream fixture paths under tests/integration/ are invalid on NTFS.
Document the verified sparse-checkout workaround and point Windows
contributors to it from CONTRIBUTING.


Claude-Session: https://claude.ai/code/session_01LjhoPGpqAwHwcCgectzRsp

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 18:15:00 +08:00
morluto 00a9e95de9 fix(gpu): resolve extended CUBIN symbol sections
Fixes #6600
2026-10-23 18:08:46 +08:00
morluto 0205f870db fix(gpu): keep unknown targets out of exact filters
Fixes #6599
2026-10-23 18:08:46 +08:00
morluto 6442329717 fix(gpu): parse legacy PTX body parameters
PTX 1.0 through 1.3 declares kernel parameters inside the entry body. Keep later-version parameter-list parsing unchanged.

Fixes #6594
2026-10-23 18:08:46 +08:00
morluto f250c8cca5 fix(gpu): accept PTX compute target aliases
Preserve the source spelling of NVIDIA's documented compute_xx target synonym.

Fixes #6494
2026-10-23 18:08:46 +08:00
morluto 2dfcc6463b fix(gpu): accept percent-prefixed PTX entry names
Fixes #6592
2026-10-23 18:08:46 +08:00
morluto d0e93fa34b docs(browser): clarify parser evidence contracts 2026-10-23 18:07:46 +08:00
morluto 401d72997b fix(browser): preserve well-known path case
Fixes #6571
2026-10-23 18:07:46 +08:00
morluto cde858d546 fix(browser): resolve prefixed XHTML form actions
Fixes #6561
2026-10-23 18:07:46 +08:00
morluto ba7c3dafab fix(browser): reject overlapping indexed source maps
Fixes #6562
2026-10-23 18:07:46 +08:00
morluto 553262443a fix(pe): accept BER indefinite CMS values
Fixes #6574
2026-10-23 18:07:46 +08:00
morluto 98a2322998 fix(go): honor amd64p32 pointer width
Fixes #6590
2026-10-23 18:07:46 +08:00
morluto abbb1d7135 fix(macho): recognize arm64_32 binaries
Fixes #6581
2026-10-23 18:07:45 +08:00
morluto 379f37307a fix(pe): ignore unused export ordinal slots
Fixes #6577
2026-10-23 18:07:45 +08:00
morluto ce48f43550 test(go): assert unresolved MZ candidate boundary 2026-10-23 18:07:45 +08:00
morluto ea579ebfae fix(sigrok): retain recording suffix for autodetection
Copy recordings under a private filename that retains the caller-selected final suffix, and pass that filename to sigrok. This restores filename-based autodetection without exposing the source directory.

Fixes #6580
2026-10-23 18:07:45 +08:00
morluto c74bc2108c fix(process): preserve sibling path identities
Keep aliases on selected paths at clear text delimiters so a longer sibling filename is not rewritten as the executable. Version the normalization contract and cover source and actual PTY behavior.

Fixes #6586
2026-10-23 18:07:45 +08:00
morluto 65168f77fb fix(environment): restrict epoch normalization to clock fields
Require a time-field token boundary before treating numeric values as epoch clocks, so identifiers such as media format codes remain literal. Clarify recognized field forms and clock offset and precision semantics in the fidelity guide.

Fixes #6551
2026-10-23 18:07:45 +08:00
morluto 18225b9bd6 fix(environment): preserve submillisecond insert ordering
Compare retained submillisecond components after milliseconds, so ordered collections recognize and place distinct timestamps correctly.

Fixes #6525
2026-10-23 18:07:45 +08:00
morluto 54c666a5ae fix(environment): reject out-of-range clock offsets
Reject invalid ISO offsets while preserving valid boundary values. Validate stored clock-format offsets through the same parser so malformed imported formats cannot reach rendering.

Fixes #6524
2026-10-23 18:07:45 +08:00
morluto 275095a9c6 fix(model): retain resolved GGUF alignment under cancellation
Publish alignment from the parser when explicit metadata validates it, or when metadata completion establishes the default. Preserve that fact through later parsing and hashing failures.

Fixes #6579
2026-10-23 18:07:45 +08:00
morluto b6833b9f61 fix(model): recognize named chat templates in summaries
Count retained template content rather than only the default key; keep names-only inventories distinct.

Fixes #6569
2026-10-23 18:07:45 +08:00
Wadeandmorluto cc99d896be fix(python): reject code keys and set members with unhashable consts (#6424)
* fix(python): reject code keys and set members with unhashable consts

* fix(python): refuse to hash a tuple that is still being read

* fix(python): hash a code object's co_consts tuple, not its item list

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-23 18:05:58 +08:00
pablodiazjorgeandmorluto 630e6becdf fix(javascript): associate requested bindings only with resolved native addons (#6402)
* fix(javascript): associate requested bindings only with resolved native addons

* test(javascript): cover the exact native addon association

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-23 17:55:43 +08:00
morluto 99be47a9a3 refactor(firmware): centralize audited tool requirements 2026-10-23 17:54:24 +08:00
RRFZ 572d0c2340 fix(flutter): honor ELF note alignment when reading build IDs (#6423)
* fix(flutter): honor ELF note alignment when reading build IDs

* test(flutter): key shared note ranges by effective alignment

Cover a program and section declaration that share one note range: differing effective alignments (8 and 4) must both be read, while declarations that both normalize to four bytes (1 and 4) still share one read. 24-byte build IDs pad identically at both alignments, so only the range key decides the read count.
2026-10-23 17:53:43 +08:00
Wade 19d975751f fix(il2cpp): name the metadata file when linking finds malformed metadata (#6425) 2026-10-23 17:52:33 +08:00
Cassie_FoxandClaude Sonnet 5.5 21b836a327 test(process): print the progress frames when the sparse-child bound fails (#6427)
The sparse-child capture test asserted the frame bound with a bare boolean,
so a failure only printed "expected false to be true". The assertion now
carries the running progress details (elapsed time, frames, samples,
interactions) as its failure message. This only adds diagnostics to a
failure; it does not change what passes and does not fix the flake.

Refs #5889.

🤖 Generated with Claude Code (Sonnet 5.5) 🦊

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-23 17:52:21 +08:00
morluto fe0325c6d1 fix(environment): scope fidelity ID normalization
Reserve equal global ID pairs and stop normalizing contested pairings. Keep numeric measurements literal, scope keyed collection identities through learning and comparison, and normalize string references only where learning established the same field as an ID.

Fixes #6565
Fixes #6550
Fixes #6557
2026-10-23 17:51:09 +08:00
morluto e6537df806 docs: clarify derived values and comparison controls 2026-10-23 17:45:15 +08:00
morluto 7af6fa01f5 fix(environment): compare captured bodies by representation
Keep JSON, text, and retained bytes distinct through fidelity comparison. Compare binary bodies by bytes, including valid UTF-8 bytes against equivalent text, and keep empty-body compatibility explicit.

Fixes #6564
2026-10-23 17:45:00 +08:00
morluto aee249b1dd fix(python): normalize legacy code flags for equality
Preserve CPython 3.7–3.10 constructor flag semantics in code keys.
2026-10-23 17:43:07 +08:00
morluto 81de82a1a4 fix(python): classify historical headers and signed flags
Fixes #6568, #6487
2026-10-23 17:43:07 +08:00
morluto d53584a10a fix(browser): preserve Link context semantics
Fixes #6504

Fixes #6505

Fixes #6506
2026-10-23 17:43:07 +08:00
morluto a4bf6f83a6 fix(cli): merge sibling output filter slices
Fixes #6542
2026-10-23 17:43:07 +08:00
morluto 50241b368b fix(adb): preserve push target semantics
Fixes #6566

Fixes #6567
2026-10-23 17:43:07 +08:00
morluto 3ba1942631 test(firmware): bind owned producers and exercise real limits 2026-10-23 17:33:05 +08:00
morluto 642f6571ea fix(elf): follow loader version index precedence
VERDEF entries replace VERNEED names for shared indices, matching glibc lookup behavior.
2026-10-23 17:26:11 +08:00
morluto 446b284239 fix(elf): resolve symbol versions from requirements
Fixes #6533
2026-10-23 17:26:11 +08:00
0154141304 fix(browser): preserve frame identity across captures (#4040)
* fix(browser): preserve capture evidence fidelity

* fix(browser): align frame identity comparisons

* fix(browser): retain historical frame evidence

* fix(electron): retain same-document script owners

* fix(electron): bind frame proof to tree reads

* fix(browser): retain malformed frame scope

* fix(browser): retain WebMCP event scope

* fix(browser): retain malformed timeline identity

* fix(browser): unify frame identity and scope

* fix(electron): prove enclosing frame ancestry

* fix(electron): separate ancestry from file policy

* fix(browser): preserve transient fragment semantics

* fix(browser): retry uncertain initial frame scope

* fix(browser): preserve runtime frame identity

* refactor(browser): separate independent capture fixes

* fix(electron): detect tree-first document replacement

* fix(electron): retain same-document tree authority

---------

Co-authored-by: eco-null <eco-null@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-23 17:19:35 +08:00
morluto fa56001f18 fix(python): align pyc and PYZ marshal decoding
Fixes #6560
Fixes #6559
2026-10-23 17:16:28 +08:00
morluto 667f73aa54 fix(il2cpp): distinguish rank-one array types
Fixes #6534
2026-10-23 17:15:56 +08:00
morluto f28c0fdfb0 fix(erlang): select the last repeated BEAM chunk
Fixes #6518
2026-10-23 17:14:38 +08:00
morluto 0c99194b8c docs(artifacts): clarify active-reader extraction scope 2026-10-23 17:14:38 +08:00
morluto e36df2c8de docs(adb): clarify emulator observation field 2026-10-23 17:13:00 +08:00
morluto 461541348e docs: clarify evidence ownership and verification guidance 2026-10-23 17:13:00 +08:00
morluto 45a411e8f8 fix(pyinstaller): preserve namespace declarations
Project PYZ namespace packages as metadata so their query paths do not claim output directories. This lets independent CArchive files retain the same logical path and remain inspectable and extractable.

Fixes #6539
2026-10-23 17:04:03 +08:00
morluto 60981e0801 fix(python): honor version-specific code equality
Fixes #6547
2026-10-23 17:04:03 +08:00
morluto 012959e3cc fix(mcp): retain partial evidence through shared delivery 2026-10-23 17:01:21 +08:00
morluto 46111dd091 fix(adb): retain refused activity launch reports
Parse complete nonzero Activity Manager reports while preserving other command failures and their captured output. Admit dollar signs only in the activity component name for nested Android classes.

Fixes #6528

Fixes #6527
2026-10-23 16:57:44 +08:00
morluto 39d1b2d395 refactor(targets): preserve ambiguous MZ header evidence 2026-10-23 16:57:43 +08:00
morluto 06828b2e65 fix(gpu): preserve PTX and AMD target facts
Fixes #6469

Fixes #6483
2026-10-23 16:53:58 +08:00
morluto be85386e27 refactor(erlang): reject overflowing BEAM atom lengths 2026-10-23 16:52:06 +08:00
morluto 933aa480d3 fix(erlang): accept extended BEAM atom lengths
Fixes #6503
2026-10-23 16:50:32 +08:00
morluto f8f4a5c3b8 fix(artifacts): recover malformed PYZ sizes without losing siblings
Fixes #6120
2026-10-23 16:49:31 +08:00
morluto 56685c6ec6 fix(inspector): preserve script identity and parse-failure facts
Fixes #6535

Fixes #6546
2026-10-23 16:49:31 +08:00
morluto 6bb4f4bf7d refactor(ghidra): centralize load image check recording
Share expected and observed check construction across MZ, COM, and raw-binary attestations. Preserve format-specific rules and source coordinates, and derive mapping callback types from the recording owner.
2026-10-23 16:38:07 +08:00
morluto b58aa63796 fix(python): preserve signed code-object line numbers
Fixes #6490
2026-10-23 16:31:49 +08:00
morluto e44899d510 fix(gpu): accept PTX entries without parameter lists
Fixes #6493
2026-10-23 16:30:34 +08:00
morluto 860b3dd6b0 fix(erlang): accept uninterpreted BEAM alignment bytes
Fixes #6517
2026-10-23 16:30:34 +08:00
morluto e44ff4666a fix(artifacts): recognize empty Zip64 containers
Fixes #6540
2026-10-23 16:30:34 +08:00
morluto bcd1f95e68 fix(adb): preserve empty properties and verbatim version names 2026-10-23 16:23:57 +08:00
morluto 14e745950e refactor(nativeaot): share report facts across budget stages
Centralize parse-wide identity and coverage facts while keeping conservative admission values separate from measured rows and counters. Reuse the derived truncation reason and retain the final serialization bound.

Tighten the report-budget regression so unexpected parser finalization errors cannot be swallowed by its budget search.
2026-10-23 16:21:32 +08:00
morluto 6c4d880f2d fix(process): parse native teardown relation sentinels 2026-10-23 16:10:04 +08:00
Hifza Zafar 524b84792f docs(readme): add Roman Urdu edition (#6499)
* docs(readme): add Roman Urdu edition

Register ur-Latn with a pending website catalog and preserve English command examples.

Script-tag handling follows morluto's shared helper proposed in PR #2043 (c46f606c89).

* test(website): cover completed script-tag page metadata
2026-10-23 16:06:27 +08:00
morluto 1635eadfcc refactor(errors): centralize input failure construction
Carry caller messages through the existing schema issue owner instead of constructing and discarding an intermediate input error. Remove forwarding schema helpers and migrate equivalent constructors across investigation, firmware, dylib, process, and screenshot workflows while preserving diagnostic paths and causes.
2026-10-23 16:04:39 +08:00
morluto 5f4e39fea2 fix(comparison): validate function result projections
Validate canonical dimension ownership, summary counts, and exact changed or unknown projections before investigation consumers publish derived Evidence. Reuse the shared dimension contract in selectors and keep historical source fixtures internally consistent.

Verify authentic malformed comparisons across all three MCP consumers while preserving valid reordered and unknown results and retained source Evidence.
2026-10-23 15:54:55 +08:00
morluto 02c8532901 docs: require buy-in for speculative hardening complexity 2026-10-23 15:54:36 +08:00
Adolanium 2944afa5b8 fix(javascript): report colliding recovery inputs as input errors (#6520)
* fix(javascript): report recovery inputs that collide on a case-insensitive filesystem

* test(javascript): cover a recovery input selected twice on any filesystem

* test(javascript): cover snapshot collision causes and unrelated write failures
2026-10-23 15:53:49 +08:00
ShikamaruNaaraaandgensokyo-dev 28a33cae8f fix(runtime)!: require patched Node.js security releases (#6315)
Require Node.js ^22.23.4 || ^24.22.1 || >=26.12.1 across package engines, setup/doctor and the standalone installer. Validate patch boundaries and maintain runtime-independent listener identity, heap-option and HTTP schema-binding tests.

Closes #6283
Refs #6480

BREAKING CHANGE: Node versions below 22.23.4, 24.22.1 and 26.12.1 are no longer supported. Upgrade Node before installing or updating REA; the installer does not upgrade Node.

Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
2026-10-23 07:21:50 +00:00
morluto 49c9c9d4a1 refactor(logging): unify startup policy and test ownership 2026-10-23 15:21:14 +08:00
morluto e073f64be0 refactor(process): centralize comparison evidence ownership 2026-10-23 15:05:20 +08:00
morluto 4c547008c5 refactor: remove obsolete forwarding exports 2026-10-23 14:54:31 +08:00
592c3e2793 fix(lifecycle): reject session work once close has started (#6411)
* fix(lifecycle): reject session work once close has started

Process shutdown closed the shared binary session without sealing it, so a
stdio tool call could open a provider after that close and leave the child
running. GDB ran a queued console command or a second close after the session
had started closing because the queued turn did not repeat admission checks.

Shutdown now refuses later target acquisition, and a queued GDB command or
duplicate close fails as unavailable once its turn finds the session closing
or gone.

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

* fix(gdb): block console admission after cleanup starts

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: morluto <morluto@users.noreply.github.com>
2026-10-23 14:53:19 +08:00
MeskineMorty 5d81363391 fix(evm): report the carrier input allowance as a file-size constraint (#6414)
The EVM interface failure translator grouped the stable reader's input
limit with path and format refusals, so a carrier over the fixed 4 MiB
input allowance surfaced as a non-retryable invalid_request whose only
record of the allowance was issue text pointing at the input schema.
The same refusal now returns a non-retryable resource_constraint with
resource file-size and reported_limits.maximum_input_bytes, following
the WABT input translator, while path and format reader refusals stay
input errors. The reply-size write failure keeps its own branch.

Fixes #6053
2026-10-23 14:46:38 +08:00
morluto 37e92e430b refactor(javascript): centralize package and JSON document facts 2026-10-23 14:40:52 +08:00
Muhammad Hamza 4cfc61147b fix(process): record no normal exit code for signal-terminated targets (#6486)
capture-process recorded exit.code 0 for a target a scenario event killed
with SIGKILL or SIGTERM, beside the nonzero exit.signal. The result builder
and the retained observation each kept any nonnegative PTY exit code for an
exited capture without consulting the signal; only finalization checked it.

Project the exit once in captureExitObservation, whose normal exit code is
null when a nonzero signal ended the target, and use the same rule for
finalization. Normal exits keep their codes, including 0 and 3, and deadline
exits remain null.

Fixes #6445
2026-10-23 14:40:01 +08:00
Ezra BrilliantandClaude Opus 5.5 6d6cbdf8c4 fix(doctor): report a missing --format value as MISSING_OUTPUT_VALUE (#6489)
`mcp doctor --format` and `--format=` reported VALIDATION_ERROR
"Invalid output format: missing", although docs/cli.md documents
MISSING_OUTPUT_VALUE for valued output flags without a value and
`capabilities` already returns it. Classify absent and empty values as
missing, share the existing missing-value message, and keep
VALIDATION_ERROR for a present but unsupported format.

Fixes #6455


Claude-Session: https://claude.ai/code/session_01LjhoPGpqAwHwcCgectzRsp

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 14:39:41 +08:00
morluto c106cf20cb fix(setup): preserve owned Gemini user settings 2026-10-23 14:03:20 +08:00
xjh b395190e42 fix(artifacts): disclose unavailable content hashes in summaries (#6481) 2026-10-23 14:01:50 +08:00
morluto b6a0f32b14 fix(evidence): centralize authentication failure ownership 2026-10-23 13:48:11 +08:00
morluto 0407bbfbe6 fix(ledger): authenticate evidence before committing 2026-10-23 13:32:58 +08:00
morluto 6dff37594c refactor(evidence): centralize analysis observation facts 2026-10-23 13:17:19 +08:00
morluto 7f9a03d6ab refactor(evidence): move artifact construction into domain 2026-10-23 13:03:06 +08:00
Abel YagubyanandAbelo9996 4f8465d8db fix(hopper): report invalid regex patterns as pattern input errors (#6372)
Co-authored-by: Abelo9996 <Abelo9996@users.noreply.github.com>
2026-10-23 13:02:31 +08:00
Canandeco-null 3e85da9195 fix(screenshot): validate complete PNG IHDR (#6412)
Co-authored-by: eco-null <eco-null@users.noreply.github.com>
2026-10-23 13:02:07 +08:00
Canandeco-null c8d58d2e7c fix(network): count omitted response content promptly (#6413)
* fix(network): count omitted response content promptly

* test(network): pin response omission timing

---------

Co-authored-by: eco-null <eco-null@users.noreply.github.com>
2026-10-23 13:01:17 +08:00
morluto 8b1e3143a2 refactor(evidence): own bundle validation failures 2026-10-23 12:55:44 +08:00
morluto e32a0494d4 fix(ci): use an available command for the Ghidra probe marker (#6400)
Check the ARM/Thumb success marker with the runner-provided grep -F. The real Ghidra probe succeeded, but the previous rg dependency made the lane exit 127.

Validated with the actual workflow shell, producer-failure and missing/incorrect marker controls, 17 focused tests, and hosted Ghidra 12.1.4 / JDK 21 compilation and ARM/Thumb context checks.
2026-10-23 03:51:10 +00:00
gensokyo-dev f850b7c894 test(cli): repair analysis-view finalization cancellation seams (#6431)
Inject interrupts through imported projection and Evidence resolution boundaries so lexical service calls cannot bypass the test. Preserve exact normalized partial results, authenticated Evidence provenance and signal exit codes.

Assisted-by: Reimu (gensokyo-skills:reimu-incident-triage)
2026-10-23 03:30:07 +00:00
morluto 9eb829aca3 fix(process): centralize capture evidence admission 2026-10-23 10:42:28 +08:00
morluto 71a024c789 refactor(javascript): consume normalized feature seeds
Require parsed seed facts in the pure trace projection and leave raw request defaults at the shared workflow boundary. Remove the redundant seed parse, unused raw-seed alias and duplicate seed-match binding.

Move omitted-versus-explicit defaults and full Evidence identity checks to compiled CLI/MCP acceptance. Reuse the owned MCP fixture and request files in the tests. Matching, native handoff, empty-literal, coverage and immutability checks remain covered; public schemas and successful outputs are unchanged.
2026-10-23 10:22:07 +08:00
morluto f4b2ba1313 refactor(javascript): own runtime reconciliation failures
Return expected source and semantic failures from the runtime input owner as tagged Results. Bind source schema validation to concrete schemas, preserve nested active-capture paths, and let unexpected interpretation and derivation failures reach the adapter boundary.

Remove broad protocol-error wrappers, duplicate active identity validation, and redundant JSON result parsing. Migrate parsed-fact consumers and retain CLI/MCP contracts. Relevant source and public-boundary tests pass; six injected implementation-fault controls fail on the prior revision. Live Electron behavior was not exercised.
2026-10-23 09:55:02 +08:00
morluto ed069bb9d4 fix(coverage): consolidate request validation ownership
Route CLI and MCP coverage requests through the shared strict workflow boundary before authenticating source Evidence. Preserve canonical advertised schemas, use integrity errors for malformed source bundles, and use the workflow operation for input diagnostics.

Remove the unused throwing domain facade and keep semantic fixture assertions on the parsed Result APIs. Validate compiled CLI and stdio MCP parity, strict request rejection, domain authority/time semantics, generated schemas, and unexpected failure propagation. No external providers execute in these checks.
2026-10-23 09:29:30 +08:00
morluto da9a580a0d refactor(javascript): unify application workflow ownership 2026-10-23 09:07:43 +08:00
morluto 74ed67da7f refactor(investigation): share immutable evidence reads 2026-10-23 08:20:42 +08:00
morluto b7f8bbb49f refactor(analysis-view): unify request and source ownership 2026-10-23 07:57:14 +08:00
morluto 6b7aa124ed fix(native): own matching source validation and admission
Separate source corruption and selected-role errors from unexpected derivation failures. Normalize coordinates before body membership and conflict checks, reuse parsed requests, and account for outer observation arrays before validation copies.
2026-10-23 07:27:15 +08:00
morluto 0281d10130 fix(mobile): share authenticated inventory projection sources
Authenticate inventory requests once before family projection, remove obsolete schema aliases and raw facades, and preserve unresolved occurrence references from partial pages without fabricating content identities.
2026-10-23 06:40:25 +08:00
morluto 0b17cb621d refactor(javascript): reuse parsed semantic query contracts 2026-10-23 06:16:22 +08:00
morluto db2a3174f6 fix(reconstruction): share authenticated obligation ledger sources 2026-10-23 06:15:38 +08:00
47353e494d feat(config): make the bridge request ceiling configurable (#6376)
* fix(reconstruction): dedupe residual unknown ids with uniqueSorted

Current main calls uniqueBy, which is not defined, so typecheck fails.
Residual unknown ids are strings, and the adjacent lists already use
uniqueSorted.

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

* feat(config): make the bridge request ceiling configurable

REA_BRIDGE_MAX_REQUEST_BYTES replaces the hard-coded 16 MiB Hopper and
Cutter request payload ceiling. The default stays 16777216 bytes. Values
outside 7464 to 268435456 bytes are configuration_invalid.

Part of #6176.

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

* fix(cutter): keep cleanup ownership when a sent command's reply is oversized

An authenticated command may already have changed the Cutter document
when its reply exceeds the frame ceiling. Return the existing unknown
execution, with the ceiling diagnostic in the message, so receipt
cleanup still waits. A frame that overflows before the command is sent
remains a resource constraint.

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

* style: format the bridge request ceiling sources

oxfmt --check failed on the five files that set and test
REA_BRIDGE_MAX_REQUEST_BYTES. Formatting only; an oversized Cutter
reply still stays an unknown execution.

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

* fix(cutter): exclude the LF from the response frame budget

REA_BRIDGE_MAX_REQUEST_BYTES counts payload bytes. The Cutter response
reader and authenticated encoder were reserving one byte for the
terminating LF, so an exact-limit response was rejected. A reply that
exceeds the ceiling after the command was sent still stays an unknown
execution.

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

* fix(bridge): measure escaped JSON before allocating the request

A command of U+0001 characters can expand sixfold. JSON.stringify of
that expansion can throw past Node's string ceiling before the payload
check. Count the escaped size within REA_BRIDGE_MAX_REQUEST_BYTES
first and refuse it as a resource constraint.

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

* fix(config): derive the bridge request maximum from JSON expansion

A 256 MiB ceiling still lets a shorter U+0001 command expand past Node's
536870888-character string limit. The maximum is now
floor((536870888 - 296) / 7) = 76695798, the longest such command whose
authenticated frame fits in one string. Requests are still measured
before JSON.stringify.

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

* fix(bridge): size the Cutter frame deadline and send the measured Hopper payload

The authenticated Cutter command is encoded after the challenge, so a
one-second read rejected an in-budget frame. That read now allows one
second per mebibyte of the ceiling and never waits longer than 25
seconds. Hello stays at one second. Hopper writes the payload it already
measured instead of serializing the same request again.

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

* fix(config): bound the bridge request maximum by the serialized string

The ceiling already measures escaped JSON, so dividing Node's string limit by the seven-byte expansion rejected encoded payloads that still fit. The maximum is 536870887, one byte under that string ceiling, leaving room for the framing LF.

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

* fix(hopper): serialize a bridge request when it reaches the wire

The queue kept each encoded frame, plus the caller's params, until Hopper finished the active request. A raised ceiling made a few waiting calls enough to exhaust the heap. The FIFO now keeps the params, and the frame is encoded only for the request being written.

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

* fix(cutter): hash the bridge proof prefix and payload separately

An exact-limit Cutter frame plus the HMAC prefix exceeds Node's single-string ceiling, so the client threw before sending an admitted command. The prefix and payload are now separate HMAC updates, including the response proof.

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

* fix(cutter): keep recovery fields when a status path exceeds the frame

A long current_file made the status reply miss the ceiling, and the fallback then dropped session_id and the completion receipts. The status now omits the path and version and still returns the session, connection proof, and up to 64 receipts. That required recovery frame is smaller than the 7464 floor.

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

* fix(cutter): report an unknown path when status identity does not fit

A bounded status kept the new document generation and omitted the path, so the client filled in the descriptor's older path. The observed path is sent when it fits. When it does not, current_file is null and is not replaced with that stale path.

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

* fix(cutter): count structured replies by their encoded JSON size

The response guard charged 16 bytes per primitive and two bytes per list slot, so a dense array under the configured ceiling was replaced with output_truncated. Numbers, booleans, null, and container punctuation now use their JSON lengths.

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

* fix(cutter): stream authenticated responses in bounded chunks

A fitting reply was signed from retained raw bytes, escaped bytes, and a
joined frame, so a ceiling near 512 MiB could exhaust Cutter before send.
Measure and sign JSON pieces, then write the same frame in 64 KiB chunks.

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

* fix(bridge): keep Cutter admission from stalling or hiding sessions

Read an unauthenticated frame off the accept loop. A command reply that
does not fit keeps completion and generation and reports an oversized path
as null. Discovery reports a ceiling mismatch instead of an absent session.
The maximum HMAC proof is opt-in, and both bridges reject non-ASCII digits.

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

* fix(cutter): bound frame scans and client response copies

Search only newly read bytes for the framing LF. Unescape each socket
chunk into one response string, check the proof, and then parse it.

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

* fix(cutter): keep completion, admission, and dense replies bounded

Report completed_connection only through connections that have
terminated. Cap connection threads and keep the listener up if one
fails to start. Batch scalar JSON and fail discovery when any ceiling
mismatches.

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

* fix(cutter): hash requests in parts and scan escapes once

Update the client-proof prefix and request separately. Stop the response
decoder at the first quote or backslash instead of rescanning the tail.

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

* fix(cutter): confirm live ceiling mismatches and omitted output

A stale descriptor from a previous ceiling is not a discovery mismatch
until its hello proves a live authenticated bridge. A response discarded
for the frame ceiling after dispatch reports output_truncated.

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

* test(cutter): close the unauthenticated ceiling-mismatch fixture

The listener that fails the hello proof is destroyed after its challenge
so discovery cleanup does not wait on that socket.

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

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: morluto <morluto@users.noreply.github.com>
2026-10-23 06:11:21 +08:00
Cassie_FoxandClaude Sonnet 5.5 0f09630645 fix(scripts): keep the first proxy error in the package-update verifier (#6362)
The registry fixture sent response headers before reading the upstream body,
then always wrote a 500 on error. A body failure hit ERR_HTTP_HEADERS_SENT,
which hid the real error and crashed the verifier with its helper still running.
The fixture now reads the body first, destroys a response that already started,
and the verifier rethrows the first fixture error.

Refs #6038.

🤖 Generated with Claude Code (Sonnet 5.5) 🦊

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-23 06:10:39 +08:00
371f3d91ae fix(managed): retain type discovery after member limits (#5312)
fix(managed): retain types after member limits

Admit type identity and a bounded recovery envelope before member decoding so callers can discover smaller selections after capacity exhaustion.

Fixes #5146

Co-authored-by: eco-null <eco-null@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-23 06:07:03 +08:00
8b478d0f15 fix(native): preserve Mach-O install-name whitespace (#4979)
* fix(native): preserve whitespace in otool load-command strings

otool prints dylib install names and rpaths as `<key> %s (offset %u)`
with exactly one separator space around the recorded string. The parser
trimmed the value and stripped the offset suffix with `\s+`, so a valid
install name such as "  leading.dylib  " became "leading.dylib" in both
the dependency inventory and the raw load-command fields.

Parse these offset-suffixed string fields by their exact format and
strip only the single-space offset suffix.

Fixes #3384

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

* test(native): verify exact Mach-O install names through CLI and MCP

---------

Co-authored-by: Bhaaraadwaj <bharadwajdaya@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-23 06:02:37 +08:00
Yaash Singhh 15ec8e98db fix(adb): improve shell output parsing accuracy (#5001) 2026-10-23 05:58:31 +08:00
Syed Musa Raza NaqviandClaude Opus 5.5 c2fe0c6818 fix(hopper): preserve an explicit display number zero in the X11 helper (#5214)
`start_xvfb` treated `--display-number 0` as omitted because it used the
option's truthiness, so an explicit `:0` was replaced by a random display
and its collision check ran against that random display instead.

Select the random display only when the option is absent, and add a
fixture-backed regression test covering explicit zero, an occupied
explicit zero, explicit one, and omission.

Fixes #5189

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 05:58:16 +08:00
Dayvisonanddayvison2b b5442df02d fix(mcp): describe function comparisons in behavior tool (#5251)
Co-authored-by: dayvison2b <dayvison2b@users.noreply.github.com>
2026-10-23 05:56:35 +08:00
Syed Musa Raza NaqviandClaude Opus 5.5 81b94cebec fix(scripts): remove the ADB verifier's pushed probe file from the device (#5256)
* fix(scripts): remove the ADB verifier's pushed probe file from the device

The verifier pushed `/data/local/tmp/rea-verify-<timestamp>.txt` for its
transfer roundtrip but never deleted it, so every successful run left a
probe behind while reporting that all checks passed.

After a successful no-overwrite push, the verifier now removes that exact
destination with the provider-reported adb client and confirms its absence
through `list_adb_directory`. Removal runs even when the pull fails, and a
removal failure is reported as a failed check instead of a pass.
`pull_adb_file` still retains device originals.

Fixes #5190

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

* test(scripts): cover ADB probe cleanup after pull failure and leftover probes

Add verifier cases where the pull returns an error or throws after a
successful push, and where `adb shell rm` exits 0 but the listing still
shows the probe. Pull failures must still remove the probe, and a leftover
probe must fail the run.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 05:56:19 +08:00
berceandmorluto c4b7a2e003 fix(setup): keep client and skill setup when the Hopper install fails (#5120)
* fix(setup): keep client and skill setup when the Hopper install fails

A failed Hopper package install returned from runSetup before any client
registration or skill install ran, so one optional provider's package
conflict (zlib-ng-compat on Arch and CachyOS) cost every other approved
change. Record the failure instead, apply the remaining plan without a
Hopper launcher, and report needs_human with the install's code and
remediation.

Fixes #5113

* fix(setup): keep the Hopper failure when a client write or the skill install fails too

* fix(setup): keep client and skill setup when the Hopper install fails

A failed Hopper package install returned from runSetup before any client
registration or skill install ran, so one optional provider's package
conflict (zlib-ng-compat on Arch and CachyOS) cost every other approved
change. Record the failure instead, apply the remaining plan without a
Hopper launcher, and report needs_human with the install's code and
remediation.

Fixes #5113

* fix(setup): keep the Hopper failure when a client write or the skill install fails too

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-23 05:55:53 +08:00
morluto 7af1b20686 refactor(firmware): remove unused memory map schema declarations 2026-10-23 05:48:40 +08:00
morluto 2775135254 fix(managed): own source validation and reuse parsed evidence 2026-10-23 05:48:40 +08:00
6d73515bf1 perf(javascript): prune typed-array element reference escapes (#5139)
* perf(javascript): prune typed-array element reference escapes

* fix(javascript): retain references from typed-array element properties

---------

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 05:41:50 +08:00
0344d578b2 fix(apple): classify keyed-archive objects by declared superclasses (#5228)
* fix(apple): classify keyed-archive objects by declared superclasses

Interface Builder keyed archives record each object's superclass chain
in `$classes`, but the decoder classified objects by class-name
substrings alone. UINavigationController, UITabBarController and
UISearchController contain "control" but not "viewcontroller", so they
were reported as controls.

Classify keyed-archive objects by UIKit/AppKit view-controller, control
and view bases in their declared chain first, falling back to the name
heuristic only when the chain names none. The decoder now reads
`$classes` through one helper shared with NSArray detection.

Fixes #5218

* test(apple): cover each declared keyed-archive base with neutral class names

Neutral names classify only through the archived superclass chain, so each
UIKit and AppKit base entry and the control-before-view ordering now has a
case that fails without it.

* perf(apple): parse each keyed-archive class chain once

Objects sharing one class descriptor reparsed and scanned its whole
`$classes` array per object, so 20,000 objects sharing a 20,000-name chain
took about 16 seconds to decode. Cache each descriptor's chain as a set by
archive UID; the same archive decodes in about 3.5 seconds, matching main.

---------

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 05:41:13 +08:00
gongfppandgongfpp 2a3e162246 fix(browser): account for late script metadata (#6358)
Pin admitted script identities and metadata before selected source reads. After the final CDP read, count unique additional retained IDs as dropped script inventory so incomplete captures stay unknown without extending source reads or inventing budget failures. Cover producer and comparison behavior through registered MCP tools.

Co-authored-by: gongfpp <22683417+gongfpp@users.noreply.github.com>
2026-10-23 05:38:30 +08:00
Ahmed 3984d12a30 fix(js): resolve literal URL from Request constructor in fetch calls (#6364)
* fix(js): resolve literal URL from Request constructor in fetch calls

* fix(js): gate Request URL extraction on fetch and require new operator
2026-10-23 05:38:17 +08:00
CyberSproutandmorluto ee0ec027db feat(firmware): attribute hardware MMIO peripheral memory regions in native disassembly (#5348)
* feat(firmware): attribute hardware MMIO peripheral memory regions in native disassembly

- introduce hardwareMemoryMap domain model supporting standard ARM Cortex-M, RISC-V, MIPS, x86, and ARM64 physical memory region classifications (Code, SRAM, Peripheral MMIO, System Control Space, External Device)
- distinguish RAM/ROM and address-space segments from MMIO (isolate ARM External RAM / Device and MIPS kseg0/kseg1 uncached mirrors and boot ROM as non-MMIO)
- add hardwarePeripheralAttribution to map analyzed instruction references to hardware peripheral blocks and summarize peripheral register interaction profiles with numerical offset ordering
- expand ReaGhidraBridge.java decoder_context to capture ARM TMode execution register alongside MIPS ISA_MODE for native instruction inspection
- add focused unit and boundary tests across hardwareMemoryMap.test.ts, hardwarePeripheralAttribution.test.ts, GhidraFunctionValues.test.ts, and ghidraCompactReferencesMcp.test.ts verifying region bounds, MMIO isolation, numerical sorting, 32-bit/64-bit overflow rejection, ARM TMode MCP preservation, and bridge contract bindings
- make fake Ghidra installation host platform-aware on Windows in GhidraLanguageOverride.test.ts

* test(ghidra): inspect ARM and Thumb context in the Java bridge

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-23 05:37:58 +08:00
b1ff86832c fix(windows): align path handling across Hopper, Electron, and ADB (#5235)
* fix(hopper): keep macOS and Linux launcher paths POSIX

* fix(hopper): keep Linux and macOS bundle paths POSIX

Linux package archives and macOS app-bundle launcher paths describe the target host, not the host running the test. Build them with path.posix so exercising these installers from Windows produces the same paths as the target platform.

* fix(electron): follow Windows symlink targets with native separators

readlink returns backslash-separated targets on Windows. Splitting only on '/' kept a whole relative target as one component, so nested links were not followed and a relative target could pass the framework-root check without being walked. Split on both separators on Windows and add a regression for an escaping target.

* test(windows): align host-specific path and lifecycle expectations

Compare generated skill and provider-command paths with native joins, bind ADB device-path validation to an explicit platform, and prepare simulated POSIX Ghidra install roots on Windows. Skip POSIX-only Hopper lease, permission-bit, and symlink-parent cases, assert Windows' unsupported Hopper state instead, and cover the Windows Ghidra native-source admission step before snapshot replay. Launch Node child seams with SystemRoot available and expect Windows close-failure resource retention.

* test(windows): close review coverage gaps

---------

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-23 05:36:22 +08:00
arjen e83e3ceadd docs(installation): document targeted npx dependency recovery (#5353) 2026-10-23 05:24:19 +08:00
Dori bd2a84ccba fix(apple): preserve line terminators in dyld path expansion (#5363)
Signed-off-by: 李永祺 <doribelove@gmail.com>
2026-10-23 05:24:02 +08:00
Muhammad Hamza 6b8f4f7528 fix(native): admit universal Mach-O files without a host slice (#5501)
* fix(native): admit universal Mach-O files without a host slice

FAT Mach-O admission selected only the host's slice and rejected a valid
container whose slices are all foreign, so list-architectures returned
target_unavailable instead of the container's inventory. A thin foreign
image was already admitted.

Prefer the host's slice and otherwise select the first declared supported
slice. Remove the Ghidra installation probe's re-parse under other host
architectures, which existed only to work around the rejection.

Fixes #5404

* fix(native): select the first valid foreign FAT slice

Without a host slice, FAT selection took the first declared slice even when
it was too short for its Mach-O header, rejecting a container whose later
foreign slice is valid. The Ghidra decompiler probe then reported such a
foreign-only decompiler as unknown rather than incompatible. Select the first
foreign slice that can hold its header; a malformed host slice still fails.

* fix(native): select foreign FAT slices from every declaration

Foreign-slice fallback chose among architecture families after keeping only
each family's first declaration, so an undersized first arm64 subtype hid a
valid later arm64 subtype. Select from all declarations, and report the
deduplicated families separately. The host-slice rule is unchanged. Move the
FAT selection tests into their own block.

* fix(native): admit only unambiguous foreign FAT families

With no host slice, a family declared more than once, such as arm64 and
arm64e, has no provider-neutral choice: IDA analyzes the recorded slice while
Hopper prefers its own subtype. Select only families declared exactly once and
keep ambiguous foreign-only containers rejected, as on main.

* fix(ghidra): decide FAT decompiler compatibility from validated families

Slice selection now refuses a foreign-only FAT table that declares one family
more than once, such as arm64 and arm64e, and the decompiler probe reported that
refusal as unknown, so an x64 host accepted an arm64-only decompiler. When
selection fails, read the table's validated CPU families and report the
decompiler incompatible when they exclude the host. Selection and host
exclusion now have separate owners in binaryTarget.ts.
2026-10-23 05:23:34 +08:00
STDquantum 146dcf46f5 fix(browser): capture the full visual viewport (#5621)
* fix(browser): capture the full visual viewport

* fix(browser): preserve surface geometry and high-DPI screenshot bounds

* fix(browser): reject stale screenshot captures
2026-10-23 05:17:03 +08:00
morluto 1d5357aaad fix(reconstruction): own coverage validation and reuse parsed facts
Authenticate source bundles through their shared owner and retain workspace lookup facts for evaluation. Preserve source integrity failures and precise caller constraints while allowing unexpected generated-output failures to propagate. Carry the typed closure result through the service and CLI without redundant JSON validation.

Validation: 242 focused tests, source checks, docs conformance, test discovery and duplication gate passed. Six public SDK regressions fail on the baseline. No analysis provider execution is claimed for the coverage workflow.
2026-10-23 05:15:50 +08:00
Lewis WangandClaude Opus 5.5 861e17a3a7 fix(native): admit thin arm64_32 Mach-O targets for static inspection (#5788)
* fix(native): admit thin arm64_32 Mach-O targets for static inspection

Target admission mapped Mach-O CPU types only to x86, x86_64, arm and
arm64, so a thin arm64_32 object was rejected as "unsupported Mach-O
architecture" before list-architectures, inspect-macho or
inspect-signature could run, although the same slice inside a FAT file
was inventoried.

Recognize CPU_TYPE_ARM64_32 as its own Mach-O-only architecture with a
dedicated thin target variant, so ELF, PE and raw-binary paths cannot
carry it. Add its slice policy so 32-bit dispatch metadata refuses it
like other 32-bit Mach-O images instead of reporting a CPU mismatch.
Ghidra, Hopper and IDA have no verified arm64_32 lane and decline it
explicitly; Hopper's declined families now share one predicate.

Fixes #5713

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

* ci(apple): run the thin arm64_32 CLI regression on macOS

The regression compiles an arm64_32 object with Apple clang, so it skips
on the Ubuntu jobs. List it in the macOS Apple artifact job and its
change scope so the job runs it and changes to it select that job.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 05:14:42 +08:00
Lewis WangandClaude Opus 5.5 5a311d8afe fix(mcp): report Linux-only integrations as unsupported_host off their hosts (#5793)
* fix(mcp): report Linux-only integrations as unsupported_host off their hosts

Session discovery folded the host check into each optional integration's
enabled flag, so on macOS or Windows the Linux-only packet, signal,
firmware, pwntools, EVM and Wakaru tools reported provider_missing.
Their execution guards refuse those hosts whatever is configured, so
installing a provider there could never make them available.

The server now records, per integration, when the host is outside its
verified host and no service is injected. Discovery checks that before
configuration and reports unsupported_host with the existing
remediation. Injected services and supported hosts behave as before.

Fixes #5730

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

* refactor(mcp): share provider host checks with session discovery

Discovery restated each Linux-only integration's host rule in
createServer while its provider kept a separate execution check, so the
two could drift apart again. Each provider now exports its host
predicate, uses it in its execution guard, and composition hands the
same predicate to createServer for both the host restriction and the
enabled flag.

Observed host restrictions now also apply when an embedder supplies its
own availability policy, as provider load failures already do, because
no policy lets the default providers pass their host checks.

The MCP regression moves to tests/process-global and emulates darwin
arm64, linux arm64 and linux x64 through the real createServer, so CI on
Linux x64 exercises the unsupported-host branch. It also covers a
configured policy and the matching execution refusals.

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

* fix(mcp): keep caller host restrictions beside observed ones

The observed host restriction replaced a configured policy's whole
hostUnsupported map. For an injected service the observed value is
false, so a caller's own restriction was lost and the tool was reported
available. Combine both per integration: either the caller or the
observed host can mark it unsupported.

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

* test(pcap): expect unsupported_host for packet tools off Linux

The host-independent PATH case asserted provider_missing on every host.
Off Linux the packet tools are unavailable because of the host, so
discovery now reports unsupported_host there; Linux still reports
provider_missing for an unusable PATH.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 05:14:26 +08:00
STDquantum 7695466081 fix(cli): preserve Unicode and text across token windows (#5814)
* fix(cli): preserve Unicode and text across token windows

* fix(cli): avoid redundant token window formatting
2026-10-23 05:14:05 +08:00
Chris Tigga c717c8a3ec fix: preserve unknown JSON omission counts (#5905)
* fix: preserve unknown JSON omission counts

* fix: preserve unknown counts for unselected JSON

* fix: preserve unknown counts for uppercase JSON
2026-10-23 05:13:51 +08:00
RRFZ 37cbeff9f3 fix(plist): recognize OpenStep files with leading comments (#6394) 2026-10-23 05:13:13 +08:00
0d596fa8f7 docs(limits): describe how to design admission recovery (#6368)
* fix(reconstruction): dedupe residual unknown ids with uniqueSorted

Merged obligation candidates called uniqueBy, which is not defined, so
the project did not typecheck. String ids already use uniqueSorted.

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

* docs(limits): describe how to design admission recovery

Add a short contributor guide for named defaults, settings, recovery
text, and the agent recovery ladder, and mirror that ladder into the
authored skill.

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

* fix(hopper): pass distribution readers into install-host tests

systemLinuxHopperInstallHost now requires the caller's distribution
reader. The Linux install tests still passed only the environment, so
typecheck failed.

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

* docs(limits): name the parser doctor shares with startup

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

* test: align pcap, storage, and regex fixtures with current contracts

Source and portable CI failed on fixtures that still described the
previous pcap client identity, legacy storage fingerprints, and the
old regex deadline sentence.

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

* docs: link repository experiments outside the built site

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: morluto <morluto@users.noreply.github.com>
2026-10-23 05:10:36 +08:00
42e1a17d6e fix(setup): preserve concurrent client configuration edits (#4855)
* fix(setup): preserve concurrent client configuration edits

* fix(setup): normalize configuration transaction queue paths

* fix(setup): report concurrent snapshot read conflicts

* test(setup): isolate Pi workflows from installed Hopper

* test: align inherited fixtures with current analysis contracts

* test(ida): assert search recovery instead of diagnostic prose

* fix(setup): share configuration queue keys across directory aliases

Resolve parent-directory aliases for regular and missing configuration
paths so a directory symlink and its real directory serialize setup
instead of racing the same file.

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

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: morluto <morluto@users.noreply.github.com>
2026-10-23 05:06:13 +08:00
morluto 3afec59523 test(process): publish compiler readiness atomically and verify native identity
Prevent polling readers from observing truncated readiness JSON in compiler fixtures. Retain deterministic cancellation controls and strengthen the real Swift compiler case to run native identity inspection, verify parent/group facts, and check stable identity across reads.

Validated all 32 Darwin tests with the real host toolchain. A delayed write reproduces the old JSON race; delayed atomic publication passes. No CI timeout or retry changes.
2026-10-23 05:03:15 +08:00
bea6b98e84 test: prefer structured assertions over recovery prose (#6379)
* test: prefer structured assertions over recovery prose

Lock regex, IDA, Hopper/Kuna, MCP budget, capture-heap, and pcap
failures on error codes and reported limits instead of full recovery
sentences.

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

* test: keep decoder heap negatives off the Windows reserve

"512 MiB" also appears in the Bun Windows reserve sentence. The
negative check stays on "512 MiB (source setting)", which is the other
adapter's heap.

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

* test: keep the one-retry cap and deadline recovery tokens

The lost-connection guidance must say once only, so an unlimited
"whenever it is safe" rewrite fails. Deadline recovery still has to
name set, restart, the floor, and the maximum constant.

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

* test: keep heap, MCP, pcap, and preload recovery tokens

Codex still wanted the set/restart/floor/maximum heap guidance, MCP
default/floor/maximum bounds, Wireshark install guidance, and the
dropped-preload attribution limit as separate tokens.

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

* test: keep shared-context and transport recovery tokens

Shared-context capture must still exclude requests without a known root
or descendant frame. Shutdown must end the REA process before starting
again, and a failed HTTP listen must offer another port or stopping the
process that holds it.

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

* test: keep handoff, input-check, and budget format tokens

Skill and server guidance must still tell the user to restart the MCP
client. Process failures must still say to check the input files. Invalid
MCP phase budgets must still say positive decimal integer and ms.

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

* test: bind budget floors and active deadline sources

The phase-budget minimum is the start of "from N to M ms", so a digit
inside the maximum no longer satisfies it. Capability text must contain
the active "N ms (source ...)" pair, not the default number and a heap
source mentioned apart.

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

* test: bind heap MiB sources and pcap host tokens

A configured 256 MiB request must appear as "256 MiB (source setting)",
so a byte-sized 256 no longer passes. Unsupported pcap hosts must still
name Linux, util-linux, Windows, and owned Job Objects as separate tokens.

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

* test: bind the selected decoder heap to MiB and source

The two-adapter limitation must contain "256 MiB (source setting)".
The exhaustion message binds that capacity as "256 MiB V8 old-generation
heap (source setting)", so a byte-sized 256 no longer passes.

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

* test: bind doctor client seconds and prlimit absolute guidance

Doctor recovery must name startup_timeout_sec of the policy's seconds,
so a hardcoded 60 no longer passes while the policy stays 30. A relative
prlimit must say the value must be absolute and tell the user to check
REA_TSHARK_PRLIMIT_COMMAND.

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

* test: assert integrity codes for rehashed evidence and coverage

Independently rehashed process Evidence and a tampered digest project
evidence_integrity_mismatch. An unsupported comparison operation stays
invalid_request. Unsupported inventory coverage status is an integrity
failure whose reason carries the coverage.status schema issue.

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

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: morluto <morluto@users.noreply.github.com>
2026-10-23 04:47:39 +08:00
morluto 46217c69bc refactor(rizin): share validated command requests across admission and execution
Resolve raw input at the application boundary and reuse its facts in both MCP admission paths. Keep cleanup, mutation retirement and execution ordering while retaining structured correction details for rejected commands.

Validation: 186 focused tests, source checks, docs conformance and duplication gate passed. The new compiled CLI regression fails on the main baseline. Provider process controls use deterministic fixtures; no real Rizin verification is claimed.
2026-10-23 04:29:35 +08:00
morluto 3e87889bfb test(process): isolate child startup and assert cleanup recovery values 2026-10-23 04:29:25 +08:00
746a41937d fix(lifecycle): retain Android and Kuna descriptors when close fails (#6363)
* fix(android): retain snapshot descriptors when close fails

Preserve the stable-file diagnosis or completed copy facts when an APK
or engine snapshot close rejects, and retry that exact owner before
workspace retirement.

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

* fix(kuna): retain span descriptors when close fails

Preserve completed span text or the short-read diagnostic when close
rejects, and keep session close incomplete until that handle is released.

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

* fix(android): attribute snapshot cleanup to the descriptor that failed

A finished copy can be nested under another descriptor's rejected close.
Walk that chain, keep the snapshot hash on the partial observation, and
name every descriptor that is still open.

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

* fix(kuna): gate span admission until a retained close releases

Overlapping requests can pass the entry cleanup check before any lease
exists. Hold each span open through its close so the next open retries
that descriptor instead of admitting another one.

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

* fix(android): preserve nested snapshot facts when both closes fail

Walk nested close failures so a finished copy and a changed-APK diagnosis
survive when the source descriptor also rejects close. Attribute cleanup to
each descriptor that failed, and classify a nested close cancellation.

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

* fix(kuna): release recovered span leases before the next open

Serialize span admission and close without holding the queue across the byte
read. Recheck the caller signal after that wait, and drop a recovered lease
from the resource scope as well as the span map.

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

* fix(android): retain a source descriptor when admission close fails

openRegularFile can reject after the descriptor opens. If that close also
fails, keep the owner on the snapshot cleanup map instead of dropping it
when adoption never runs.

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

* fix(kuna): keep cancellation when a completed span close fails

Pass the caller signal into span finalization. If the read already finished
and close then rejects, report cancellation as the primary error and keep
the span text with the retained descriptor.

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

* style: format Android and Kuna close-ownership changes

CI format:check rejected the four files touched by the close-ownership fixes.

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

* fix(android): keep the snapshot hash inside a nested cancellation

A destination close that succeeds after abort throws RegularFileCancelled
with the finished copy. When the source close then rejects, recover that
copy so cancellation keeps the digest and file-snapshot-hash observation.

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

* fix(android): keep the snapshot hash on a direct close cancellation

A finished copy whose descriptor closes successfully after abort is a
top-level RegularFileCancelled. Attach its digest and file-snapshot-hash
observation to that cancellation.

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

* fix(kuna): preserve the caller abort reason after a successful span close

A completed read whose close then succeeds throws a cancellation wrapper.
Return AnalysisCancelledError with the caller's abort reason instead of
that wrapper.

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

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: morluto <morluto@users.noreply.github.com>
2026-10-23 03:25:24 +08:00
morluto 93a384db77 fix(evidence): classify derived failures at their owning boundary
Parse caller selections and authenticated Evidence at their owners, reuse the parsed function and comparison facts, and preserve unexpected implementation failures. Remove downstream error guessing and redundant validation wrappers.

Verify with 526 affected tests, source checks, docs conformance, test discovery and duplication checks. Seven public SDK regressions fail on the main baseline. No real-provider execution was performed.
2026-10-23 03:22:34 +08:00
7091be291c test: repair remaining bulk-merge regression fixtures (#6375)
fix: repair main after bulk merge

Restore typecheck after #3826 called uniqueBy, and align the red fixtures
with the merged pcap, browser, IDA, and Hopper contracts.

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: morluto <morluto@users.noreply.github.com>
2026-10-23 03:21:05 +08:00
3571002422 fix(setup): offer Hopper installation only where setup can install it (#4642)
* fix(setup): offer Hopper installation only where setup can install it

On a host where automated Hopper installation is ineligible (Windows,
macOS below 12 or with an unknown version, or an unsupported or
unidentified Linux distribution), an unhealthy setup without a Hopper
launcher ended by advising "Rerun with --yes --install-hopper", and the
same run then refused that request. The eligibility rule existed twice
(the discovery proposal and the explicit-install gate, with separate macOS
parsers that disagreed on malformed versions), and the final advice
consulted neither.

Make setup discovery the single owner of Hopper install eligibility. The
explicit gate refuses with the unchanged copy through hopperInstallRefusal,
and the final advice offers --install-hopper only where the host is
eligible; ineligible hosts fall back to doctor remediation. hostRemediation
now checks only the Node.js runtime. An empty or unparseable macOS version
is now refused by the explicit gate instead of installing.

Fixes #3944

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

* chore: drop merge-only formatter changes

* test: align inherited fixtures with current analysis contracts

* test(ida): assert search recovery instead of diagnostic prose

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-23 03:17:34 +08:00
86655eac66 fix(hopper): keep automated Linux setup off non-x86_64 hosts (#3422)
* fix(hopper): keep automated Linux setup off non-x86_64 hosts

Every pinned Linux Hopper package (deb, rpm, Arch) is x86_64-only, but
hopperSetupSupported was derived from os-release alone. Setup therefore
planned and confirmed an amd64 package install on arm64 Ubuntu 24.04 and
the other tested distributions.

Classify installer support with the host architecture where the
distribution is parsed, so setup planning, preflight, installation and
package verification share one decision. Setup now takes its Linux
distribution from the doctor host, as it already does for the macOS
version, and the supported-host remediation text has a single owner.

Fixes #3419

* fix(hopper): install Linux Hopper from the setup host's distribution facts

systemLinuxHopperInstallHost read os-release with process.arch on its own,
while setup planning used the doctor host's injected architecture. Both
agree in production, but a doctor host composed with another architecture
could plan one answer and install with another.

The install host now takes its distribution reader from the caller, and
systemSetupHost passes the doctor host's, so planning, preflight and
installation share one host-fact owner. The unused default host on
installLinuxHopper, the other ambient reader, is removed.

* fix(verify-package): require Hopper readiness only where Linux setup installs it

packageSetupSupport required doctor Hopper readiness on every Linux host
whose host check passed, including installer-untested distributions and,
after the architecture gate, arm64 hosts where setup never provides
Hopper. On Linux, readiness now also requires automated installer
applicability; macOS is unchanged.

This narrows the readiness assertion from #2973 on hosts outside the
installer matrix, such as Kali and ubuntu-24.04-arm.

---------

Co-authored-by: DavidFB-creator <220631175+DavidFB-creator@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-23 02:39:05 +08:00
Canandeco-null 536f5ce38b fix(browser): preserve source-map media types (#4032)
Co-authored-by: eco-null <eco-null@users.noreply.github.com>
2026-10-23 02:38:56 +08:00
RODRIGO DE SOUZA FARIAandClaude Opus 5 b6e6d7cd1a fix(ida): ignore released worker rows when verifying close (#4234)
* fix(ida): ignore released worker rows when verifying close

Upstream idb_list can observe the owning worker mid-teardown and return a
row whose identity is already cleared - empty session_id, owned false,
is_active false - while input_path still names the private copy. The
post-close check matched that row through its private-input arm and threw
"IDA worker remains registered after close.", failing a run whose analysis
had completed and whose idb_close was already acknowledged, and retaining
the private workspace.

Such a row establishes no live session, so it no longer blocks release. A
genuine peer on the same input keeps a session ID and is still reported,
and the session-ID arm is untouched.

Measured against IDA Pro 9.2 on Windows: 11 of 12 stock runs failed this
way; 6 of 6 passed with this change and retained no workspace.

Refs #3056

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

* fix(ida): suppress released rows only for worker backends

The blanked-identity exception reached a gui row as well, and a gui
session belongs to an IDA instance REA does not control, so a same-input
gui binding could stop blocking release. The observed teardown shape
covers workers only, so require backend "worker" before suppressing.

Adds a regression test keeping a blank-identity gui row on the same
private input reported as a blocker, and moves the three blanked-identity
tests into their own describe to stay within the per-block line limit.

Refs #3056

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

* refactor(ida): state the released-row predicate as equality

`owned` and `is_active` are strict booleans in databaseListSchema, so
`!== true` suggested a third possible value that cannot occur. Say what
the row actually asserts.

No behaviour change; the blanked-identity tests cover both directions.

Refs #3056

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

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-10-23 02:38:48 +08:00
Rohit Ghumareandmorluto 6714fb1d02 fix(javascript): preserve ESM path intent and artifact confinement (#2700)
* fix(javascript): preserve legacy ESM directory intent

* fix(javascript): normalize legacy ESM URL directory requests

* fix(javascript): retain invalid package URL evidence

* test(apple): bound cold archive fixture compilation

* test(release): scope preflight schema to selected jobs

* fix(javascript): preserve package URL admission and target facts

* fix(javascript): validate package target structure before URL lookup

* fix(javascript): preserve surrogate identity in structural package URLs

* fix(javascript): isolate structural package URL identities

* fix(javascript): resolve paths after opaque ancestors normalize away

* fix(javascript): confine legacy URLs and retain target provenance

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-23 02:34:28 +08:00
92a9591a41 refactor(pcap): give TShark selection and version admission one owner (#3407)
* refactor(pcap): give TShark selection and version admission one owner

The pcap and packet-capture families wrapped the same caller-supplied
TShark with diverging rules. inspect_pcap_* accepted an absolute
REA_TSHARK_COMMAND or tshark from PATH, while inspect_packet_* and the
server availability policy required an absolute REA_TSHARK_COMMAND and
parsed the version banner with a separate regex.

PcapConfiguration now owns the rule: selectWiresharkTool selects an
absolute environment path or the bare name on PATH and refuses relative
values; locateWiresharkTool resolves a PATH selection to the first
absolute PATH entry holding an executable file; parseWiresharkVersion
admits Wireshark 4.x banners for both providers. TsharkCaptureProvider
and the session availability policy consume these facts instead of
reinterpreting REA_TSHARK_COMMAND, so all four tools accept and refuse
the same configurations. inspect_packet_* still resolves, hashes and
rechecks the located executable, and records selection_source in its
tool Evidence.

Host support and resource bounds are unchanged; unifying them is the
next milestone in #3403.

Refs #3403

* fix(pcap): execute the TShark the shared locator selects

resolvePcapTools still returned bare tool names for PATH selections, so
the operating system could run a tool from a relative PATH entry while
the packet tools skipped that entry and selected a different install.

On POSIX hosts, resolve PATH selections through locateWiresharkTool and
run that absolute executable, failing with the existing tshark_missing or
capinfos_missing code when no absolute PATH entry holds the tool. Both
selections are validated before PATH is consulted, so a relative
environment value still reports its own configuration error. Windows
keeps PATH/PATHEXT resolution in the process launcher.

Refs #3403

* chore: restore main's copies of files the merge hook reformatted

The pre-commit oxlint --fix touched two files this PR does not change; keep them identical to main.

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

* fix(pcap): share selected tool identity verification

* perf(pcap): retain compact executable identity

* fix(pcap): report missing selected tools clearly

* fix(pcap): read historical capture evidence

* fix(pcap): preserve executable read failures

* test(pcap): cover version refusal and packet-tool availability selection

Address review follow-ups on the shared TShark owner:

- Send a 3.x TShark banner and a non-Wireshark banner through the packet
  provider, pinning the capability-unavailable reason for each and that
  only the version probe ran before refusal.
- Cover the session availability rule for inspect_packet_capture over
  MCP: a PATH-only install and an absolute REA_TSHARK_COMMAND are
  available on Linux, a relative REA_TSHARK_COMMAND is refused even when
  PATH holds TShark, and no absolute PATH entry reports provider_missing.
- Rename the locator test's missing absolute PATH entry so it no longer
  shadows node:path relative.

Refs #3403

* fix(pcap): name the configured path when a selected tool is missing

Resolving the selected executable before launch replaced the runner's
"Cannot run selected tool <path>" refusal with a message that kept the
configured path only in diagnostics, so the public CLI no longer told the
caller which REA_TSHARK_COMMAND or REA_CAPINFOS_COMMAND value to fix and
the pcap CLI boundary test failed. Include the configured command in the
capability-unavailable message and assert it there.

Refs #3403

* test(captures): give the late-cancellation fixture a TShark selection source

The service fixture added on main in 719d56ebf predates the required tool.selection_source field, so its report failed output validation before reaching the late-cancellation path the test covers.

Refs #3403

* fix(pcap): report a probe's pending cleanup when a post-run check fails

When a tool probe returned with its owned process still awaiting cleanup,
a failed post-run executable re-read or identity check threw before run()
reported that cleanup. Probes have no snapshot to carry it, so callers
saw an ordinary error with cleanupIncomplete false while the provider
kept the process and refused the next request.

After the command returns, combine such a failure with the probe's
ProviderCleanupError (cancellation and other command failures keep their
own cleanup outcome). Classify request failures through one
analysisFailure owner shared by run() and executeAdmitted(), and let the
availability probe rethrow any cleanup-incomplete failure instead of
reporting the tool unavailable.

Refs #3403

* fix(pcap): keep a failed executable re-read's cleanup live with the probe's

The combined post-run failure was bound only to the probe's pending
process cleanup. When the executable re-read had also failed to close its
file, the classified failure owned that cleanup, but the combined error's
live cleanupResources omitted the file and could clear cleanupIncomplete
once the process stopped. Bind both the classified failure and the probe
cleanup as dependencies.

Refs #3403

---------

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-23 02:34:14 +08:00
HawkForge 2425493e10 fix(native): keep export names ending in a carriage return (#3457)
nm -j and dyld_info -exports end each row with LF. Splitting on \r?\n and
trimming rows treated a carriage return at the end of a symbol name as part
of the line terminator, so the distinct exports "trailing\r" and
"trailing" merged into one entry with the wrong address. Split rows on LF
only and keep trailing text in export rows.

Fixes #3390
2026-10-23 02:33:56 +08:00
Haoran Xu c592ea6bac fix(opencode): override inherited disabled flags (#3469)
Set disabled to false in generated OpenCode V2 registrations so a
lower-priority disabled entry cannot keep approved setup stale.

Cover layered JSON/JSONC refreshes, idempotent setup and doctor readiness,
exact backups, preserved lower settings, malformed-config refusal, and
the exclusion of disabled registrations from automatic maintenance.
Update the native V2 registration expectations.

Fixes #3318
2026-10-23 02:33:38 +08:00
Dragoloneandmorluto 740f107ff9 fix(apple): stop counting single-object NIB connectors as ownerless (#3502)
* fix(apple): stop counting single-object NIB connectors as ownerless

The NIBArchive projection counts every object whose class name contains
"Connector" and has no resolvable NSSource as an ownerless connection
(#2484). AppKit also archives connector classes that annotate one object
and never encode NSSource: tooltips (NSIBHelpConnector), accessibility
attributes (NSNibAXAttributeConnector) and user-defined runtime
attributes (NSIBUserDefinedRuntimeAttributesConnector). Each of them
marked the document's connection coverage partial with
connection_unresolved, raised connection_count, set graph.truncated and
added the "declare no resolvable owner" limitation, although no
connection was dropped. The keyed-archive producer does not treat these
classes as connections.

Skip AppKit connectors that encode no NSSource key. A connector whose
NSSource is encoded but nil or malformed, and every UIKit runtime
connection, is still counted as ownerless.

Across 3924 NIBArchive files shipped with macOS system apps and Xcode,
688 reported this false partial coverage (for example Preview's
PaperKitWindowController.nib, whose only other connector is one outlet);
none do after the change.

* test(apple): keep unusable AppKit connector sources counted as ownerless

Only an absent NSSource marks a single-object annotation. Cover an AppKit outlet connector whose NSSource is nil or a non-reference value, which must still be reported as an unresolved, omitted connection.

* test(apple): split unusable connector source cases into their own block

Keep the connector describe block within the max-lines-per-function limit.

* fix(apple): keep unknown NIB connectors unresolved

* fix(apple): preserve unknown AppKit NIB connectors

* fix(apple): share annotation connector semantics

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-23 02:33:26 +08:00
f33b7bd617 fix(website): wrap mobile navigation at enlarged text sizes (#3598)
* fix(website): keep header navigation on one row and grid it on phones

The header grid gave the navigation only the space left beside the brand
caption and social controls, so six links wrapped onto a second line in
Greek, German, Spanish, Portuguese, Vietnamese and English at common
desktop widths.

- Size the navigation to its content and let the brand column absorb the
  remaining width; a container query hides the caption only when it no
  longer fits, replacing the fixed 851-1250px breakpoint.
- Move the navigation to its own row at 1100px and below, before one
  line can no longer hold it in any edition.
- On phones, lay the six links out as two rows of three content-sized
  columns, with a tighter gap below 340px.

* fix(website): wrap mobile navigation at narrow widths

---------

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-23 02:33:08 +08:00
Haoran Xuandmorluto 4a90059fe2 fix(hermes): detach aliased server tables before editing (#3785)
* fix(hermes): detach aliased server tables before editing

Copy aliased parent mappings before setting or removing a Hermes
registration. Resolve references against the original document and remove
anchors from the private copies to preserve existing sources and consumers,
including shadowed anchor names.

Cover YAML 1.1 and 1.2 setup, refresh, doctor readiness, repeated setup and
uninstall, along with comments, exact backups and invalid-alias refusal.

Fixes #3320

* test(setup): isolate Hermes flows from host Hopper detection

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-23 02:32:48 +08:00
윤종근 aa521379dd fix(website): align the language picker chevron (#3805)
* fix(website): align the language picker chevron

Replace the font-dependent down glyph with a fixed-size SVG mask that
inherits the button color and aligns within its existing flex layout.
Rotate the chevron when the native details menu is open.

Refs #3804

* fix(website): preserve the chevron in forced colors
2026-10-23 02:32:29 +08:00
5d3762456f fix(reconstruction): retain semantic unknowns in obligation ledgers (#3826)
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 02:32:11 +08:00
Anurag SinghandAnurag Singh 1f9d1b5de1 fix(browser): reject empty capture containers beyond the 64-level nesting budget (#3975)
The shared HAR/qlog JSON parser checked depth before pushing a container
using `frames.length > 64`, which admitted a 65th container. Over-deep
scalars were only rejected because literal values ran the same check, so
an empty array or object at level 65 passed while `[0]` at the same level
failed.

Admit a container only while its own level (root = 1) stays within the
budget and drop the literal-value check, so empty and scalar-bearing
containers share one boundary.

Fixes #3960

Co-authored-by: Anurag Singh <211784907+Anurag-5ingh@users.noreply.github.com>
2026-10-23 02:31:53 +08:00
pablodiazjorge 02e2c9cd7f fix(providers): report provider-selection diagnostics when the selected provider refuses its profile (#4103)
* fix(providers): report provider-selection diagnostics when the selected provider refuses its profile

An explicitly selected provider that refuses the target during profile resolution now yields the target_unsupported selection error with its candidates and rejections, keeping the refusal's message and, when present, its specific recovery.

Fixes #3930

* fix(providers): keep refusal evidence and cleanup ownership on selection failures

A refusal with unconfirmed cleanup is returned unchanged; otherwise the selection failure carries the refusal's captured output, diagnostics, partial observation and cause. Registry tests cover the copied recovery and the cleanup guard.
2026-10-23 02:31:23 +08:00
kuxiaowo 3f35eacad2 fix(website): handle UTF-8 output and slash refs in link audits (#4123) 2026-10-23 02:31:11 +08:00
Chip Li 1ba178befb fix(process): preserve terminal cell widths during normalization (#4142) 2026-10-23 02:30:54 +08:00
dogan 02a0e72f70 fix(sqlite): honor selected-table reply budgets (#4216) 2026-10-23 02:30:36 +08:00
b4bc6cf3ee fix(captures): scope HAR HTTP interpretation to entries (#4351)
* fix(captures): scope HAR HTTP interpretation to entries

* test(snapshot): preserve Evidence-scoped native workflow unknowns

* test(process): assert owned snapshot cancellation outcomes

* test(process): preserve snapshot race causes and owned cleanup

* test(cli): scope runtime cleanup provider injection

CLI boundary tests share a module graph. The cleanup fixture's module mock
cannot replace a provider factory already captured by the real command
registration, leaving its close count at zero after another test imports it.

Inject a typed provider factory into the registration instance, defaulting
to the existing production factory. Both commands still create their own
provider at execution time. Remove the redundant runtime context wrapper
and the global mock state. Keep the real child CLI parity case and verify
that neither command completion nor JSON output precedes awaited cleanup;
retain the close-count, cleanup error and completed Evidence assertions.

The unchanged test passes alone but fails with the command module preloaded.
Both modes pass after the correction. Seven related files pass 28 tests;
check and docs:check also pass on Node24.18.0/npm11.16.0 macOS arm64.

* test(artifacts): use valid ASAR bytes for signature control

* test(mcp): isolate snapshot filesystem fault injection

* test(process): distinguish unacquired resources in failure progress

* chore: restore main's copies of files the merge hook reformatted

The pre-commit oxlint --fix touched two files this PR does not change; keep them identical to main.

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

---------

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 02:30:06 +08:00
1684c73d4f fix(artifacts): reconcile ARM32 Mach-O slice names by subtype (#4352)
* fix(artifacts): reconcile ARM32 Mach-O slice names with subtypes

* test(snapshot): preserve Evidence-scoped native workflow unknowns

* test(process): assert owned snapshot cancellation outcomes

* test(process): preserve snapshot race causes and owned cleanup

* test(cli): scope runtime cleanup provider injection

CLI boundary tests share a module graph. The cleanup fixture's module mock
cannot replace a provider factory already captured by the real command
registration, leaving its close count at zero after another test imports it.

Inject a typed provider factory into the registration instance, defaulting
to the existing production factory. Both commands still create their own
provider at execution time. Remove the redundant runtime context wrapper
and the global mock state. Keep the real child CLI parity case and verify
that neither command completion nor JSON output precedes awaited cleanup;
retain the close-count, cleanup error and completed Evidence assertions.

The unchanged test passes alone but fails with the command module preloaded.
Both modes pass after the correction. Seven related files pass 28 tests;
check and docs:check also pass on Node24.18.0/npm11.16.0 macOS arm64.

* test(artifacts): use valid ASAR bytes for signature control

* test(mcp): isolate snapshot filesystem fault injection

* test(process): distinguish unacquired resources in failure progress

---------

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 02:29:48 +08:00
b5c47f5255 fix(browser): retain bundle default origin scope (#4413)
* fix(browser): retain bundle default origin scope

* test(browser): cover bundle URLs without origins

* fix(browser): retain validated bundle target origin

* fix(browser): validate recorded origins canonically

---------

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-23 02:29:29 +08:00
Adolanium 7e8ec0e456 fix(browser): keep omitted JSON request bodies out of complete shape coverage (#4476) 2026-10-23 02:29:10 +08:00
amyanger 7436ac8e20 fix(javascript): drop built-in effects for visibly replaced globals (#4565)
The semantic collectors admitted Node timers, fetch, WebSocket, JSON.parse
and global coercions from the identifier spelling alone, so a source that
replaced those globals (`setTimeout = () => null`) still produced resolved
built-in effects. Require the existing isUnmodifiedGlobalReference guard,
which the static analyzer already uses for fetch and WebSocket, before
admitting these effects.

The guard treats any visible possible write as a replacement without
evaluating order or reachability, so computed global writes and guarded
polyfills also remove the resolved effect. The workflow docs now say so.

Fixes #3367
2026-10-23 02:28:53 +08:00
GaneshandGanesh2325 de00fdc17d fix(firmware): state that Unblob link targets are extraction-relative (#4573)
Co-authored-by: Ganesh2325 <184471511+Ganesh2325@users.noreply.github.com>
2026-10-23 02:28:35 +08:00
Vinícius Andrade 75575f2006 fix(javascript): keep directory-form HTML URL references distinct from files (#4646)
* fix(javascript): keep directory-form HTML URL references distinct from files

Signed-off-by: Vinícius DS Andrade <95101505+viniciusdsandrade@users.noreply.github.com>

* test(javascript): cover directory-form HTML URL references

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>
2026-10-23 02:28:05 +08:00
JG Tromp db8556e724 fix(ida): supervise regex inventory matching (#4862) 2026-10-23 02:27:36 +08:00
pablodiazjorgeandmorluto e3a00bb5f9 fix(setup): tell MSIX Claude Desktop's process view from any private roaming file (#4903)
* fix(setup): tell MSIX Claude Desktop's process view from any private roaming file

Some profiles keep writing the real %APPDATA%\Claude, so the MSIX package never gets a private LocalCache\Roaming\Claude folder and setup and uninstall refused Claude Desktop for good. Compare that folder first and, when it has no file, a bounded set of files from the package's other private roaming folders, which the package redirects in the same way. A package with no private roaming file still refuses, now with the manual edit as the remedy.

Fixes #4278

* fix(setup): address review findings on the MSIX roaming probe

Name the configuration setup would write, and both package files for uninstall, in the unknown-view advice. Bound the roaming walk by 1024 inspected entries and a queue no longer than the directories it can still list. Count ENOENT at the AppData path as a direct view only while the private file still exists.

* fix(setup): bound the roaming walk's directory listings by its entry budget

Read at most the names the walk's budget has left, with opendirSync in production, so a crowded package-private folder is never enumerated whole before the budget applies.

* fix(setup): recheck the private probe before accepting a mismatched target

A private file deleted between the two lookups no longer shadows its AppData path, so a redirected view can reach an ordinary copy there. Count a different target, like ENOENT, as a direct view only while the private file still exists.

* fix(setup): repeat the AppData lookup before trusting a rechecked probe

A private file deleted and recreated between the lookups passed the existence recheck on stale evidence. Look the AppData path up again after the recheck: a match with the private target is a redirected view, an agreeing non-match counts as direct, and a probe whose lookups disagree decides nothing.

* fix: bound Claude roaming folder probes

* fix(setup): charge enumerated Claude folder names

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-23 02:27:18 +08:00
Saim Shafique af42f856ec fix(hopper): shut down the Linux demo helper orderly so its X lock is removed (#4966)
* fix(hopper): shut down the Linux demo helper orderly so its X lock is removed

Teardown SIGKILLed the helper's process group, so run_launch's finally
never ran stop_process on Xvfb and each session orphaned its
/tmp/.X<n>-lock. Later startups count orphans as address_collision.

Ask the helper to stop first: closing its stdin trips OwnerLifetime,
and its finally stops Hopper and Xvfb with SIGTERM, which removes the
lock. Anything still alive after the grace period is reaped by the
token-verified group cleanup.

Fixes #4921

Co-authored-by: Saim Shafique <128062316+sx4im@users.noreply.github.com>

* fix(hopper): notice signal-exited demo helpers during orderly shutdown

A helper that already died from a signal keeps exitCode null, so the
orderly-shutdown path treated it as still running and sat out the full
grace period before the group cleanup. Check signalCode alongside
exitCode in both the cleanup guard and the exit wait, and add a
regression test for an already signal-exited helper.

Also hardens the ignored-shutdown test: it now proves the fixture
started (waits for its lock), asserts the helper was reaped by the
group SIGKILL, and bounds the wait by the configured 500 ms grace
instead of a vacuous 30 s ceiling.
2026-10-23 02:26:58 +08:00
Devulapalli Naga Sri Vaishnaviandmorluto 809f3b7b5b fix(browser): ignore scenario storage enumeration order (#5035)
* fix(browser): ignore scenario storage enumeration order

* perf(browser): sort storage entries without serialized copies

* style(browser): format storage normalization comparator

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-23 02:26:41 +08:00
NIKHIL 9a21cd78ce fix(javascript): preserve inherited aliases past erased class fields (#5040) 2026-10-23 02:26:24 +08:00
berce be4498d394 fix(gdb): accept GDB's built-in p, s, d and t aliases (#5124)
The one-shot output-route check read the first word as an abbreviation,
so `p 1`, `s` and `d 1` matched python, source and define, and `t 1`
matched tty, and were refused before GDB started. GDB resolves its
built-in aliases first, so resolve the four that shadow those prefixes
(print, step, delete, thread) in one helper both checks share.

Fixes #5098
2026-10-23 02:26:06 +08:00
rodricksz4h5andgensokyo-dev 5d3ae66e63 fix(javascript): match unchanged module exports across application versions (#5196)
* fix(javascript): match unchanged module exports across application versions

An export binding's identity is keyed within its whole application root (container_sha256), so it changed whenever any sibling file changed, and no match tier paired export nodes: every export of an unchanged file was reported removed and added, and its module fell to unknown. The exact module-source-digest tier now also pairs an export binding by its own file's bytes (identity artifact_sha256) and exported name, as it already pairs a source module by source_sha256. Exports of edited files stay unmatched.

* style(javascript): format the version-matching export test

---------

Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
2026-10-23 02:25:43 +08:00
Dery Ferdikaandmorluto 26573c3aa4 fix(hopper): bound bridge request frames like the Cutter bridge (#5262)
* fix(hopper): bound bridge request frames like the Cutter bridge

The Hopper bridge read each request with an unbounded readline, so a peer
that never wrote a newline kept Hopper allocating for as long as it kept
writing. The Cutter bridge already refuses a frame above MAX_FRAME_BYTES;
the Hopper bridge had no request ceiling at all.

Read the request line with a 16 MiB bound and refuse a line that fills the
limit without a terminator, using the existing invalid_request diagnostic
before closing the connection. Such a line cannot be answered or
resynchronized, because its remainder would be read as the next request.
The response direction is unchanged, so large decompilation results still
flow, and every dispatchable method carries small bounded parameters.

The stalled-line case keeps its connection open instead of half-closing
it, so the test pins the exact ceiling: a bound one byte larger waits for
a byte the peer never sends and fails instead of passing quietly.

Validation: npm run check:fast (typecheck, lint) passed uncached, and the
new boundary test passed against the real bridge over socket pairs at the
frame limit and above it. Removing only the read bound makes the same test
fail, and widening the bound to one byte past the limit fails it too. The
Hopper boundary and bridge-packaging suites report the same 17 failing
files and 126 failing tests before and after this change on this Windows
host; those failures are POSIX-only probes (Unix socket directories,
os.statvfs) unrelated to this diff. Native macOS Hopper and real-client
traffic were not run.

* fix(bridges): exclude line delimiter from payload limit

* test(hopper): cover partial request at EOF

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-23 02:25:24 +08:00
곽현 b2048d46d1 fix(process): retain unresolved descendants during lineage observation (#5270)
* fix(process): retain unresolved descendants during lineage observation

* fix(process): preserve lineage across ancestor generation changes

* fix(process): avoid retaining replacements of exited candidates

* test(cli): avoid unused cache writes in module trace acceptance

* fix(process): retire replaced lineage candidates

* fix(process): preserve uncertain owners and validate launcher scope
2026-10-23 02:25:04 +08:00
8ecbb74b00 fix(wasm): refuse unretained summary delivery before provider work (#5274)
* fix(wasm): refuse unretained summary delivery before provider work

inspect_wasm_artifact with detail summary ran the WASM provider before
checking whether the server retains Evidence. On a fixed server without
session retention a successful provider run was followed by
capability_unavailable, and a failing provider run replaced that
diagnostic with the provider error, hiding the guidance to select
detail complete.

Check retention right after input validation, as the EVM, pcap and
network-capture handlers do, so the refusal precedes any provider work
and keeps its typed diagnostic. Complete delivery is unchanged. The
fixed-server boundary test now injects a vi.fn provider and asserts it
stays idle for refused summaries while complete delivery still runs it.

Fixes #5100

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix(wasm): validate caller paths before the summary retention preflight

Review follow-up for #5274. The retention preflight ran ahead of the
semantic input checks, so a relative path, glue path or candidate path
with detail summary on a fixed server returned capability_unavailable
instead of its field-specific invalid_request.

WasmArtifactService now exposes that parse-and-absolute-path step as
validate(); inspect() uses the same method, so the checks stay at the
owning boundary, and the MCP handler validates before deciding on
retention. The composition descriptor and server typing expose validate.

The fixed-server boundary test sends relative path and glue_paths with
detail summary and asserts invalid_request with both issue paths and no
provider call; it failed on 418a66d7d with capability_unavailable.

docs/mcp-contracts.md now claims the before-analysis refusal only for
inspect_artifact, inspect_wasm_artifact, inspect_macho and
decode_interface_builder, which enforce it; inspect_binary_layout and
analyze_javascript_application still analyze first and are left to a
separate change.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* refactor(wasm): inspect validated artifact input without reparsing it

Follow-up for #5274, matching the review outcome on #5288. The MCP
handler validated the request and then called inspect(), which ran the
same validator again, so every call parsed and cloned its input twice.

WasmArtifactService now separates the raw entry point from execution:
inspect() validates raw input once and delegates to the new
inspectValidated(), which runs the inspection for input that validate()
already admitted. The handler validates, runs the retention preflight,
then calls inspectValidated(); the composition descriptor and server
typing expose it. The CLI keeps the raw inspect() entry point; behavior
and diagnostics are unchanged.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix(wasm): keep cancellation ahead of raw artifact input validation

Follow-up for #5274, matching the review outcome on #5288. Separating
validate() from inspectValidated() moved the raw inspect() entry point's
validation ahead of its abort check, so a direct call with an
already-aborted signal and malformed input returned AnalysisInputError
instead of the AnalysisCancelledError the pre-change service gave.

The raw entry point now checks the signal before validating, as before,
while inspectValidated() keeps its own guard for the MCP handler path.
A unit test pins the precedence with no provider call.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-23 02:24:45 +08:00
Vishal Tambiandgensokyo-dev 9c81191b1d fix(ghidra): treat format-specific disabled analyzers as not applicable (#5425)
* fix(ghidra): treat format-specific disabled analyzers as not applicable

* test(json): assert composed cancellation cleanup for staged output

---------

Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
2026-10-23 02:24:16 +08:00
Yann Robertoandgensokyo-dev 220414682e fix(setup): preserve explicit clients with all-detected selection (#5508)
* fix(setup): preserve explicit clients with all-detected selection

* test(filesystem): align staged cancellation cleanup assertions

---------

Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
2026-10-23 02:23:58 +08:00
Wadeandgensokyo-dev d11fca46fc fix(cli): print compact mcp doctor JSON when stdout is redirected (#5514)
rea mcp doctor rendered json with Formatter.format, which always indents, so
redirected --json, --full-output and validation errors stayed indented. The
dispatcher now passes the stdout TTY state and the doctor renders json and
jsonl through jsonOutputParts like the other commands.

Fixes #5435

Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
2026-10-23 02:23:39 +08:00
5c8d5b2a8a fix(hopper): keep an observed launcher exit over a later cancellation (#5816)
* fix(hopper): keep an observed launcher exit over a later cancellation

`#connect` projected every interrupted wait as `#startupInterruption`
without consulting the launcher the owner already held, so a launcher that
exited 3 before the caller aborted was reported as `HopperCancelledError`.

An exit or signal the owned process already holds is now projected as the
physical startup failure first; a cancellation observed before any exit is
unchanged, and successful exits keep their previous behaviour.

Fixes #5429

* fix(hopper): arbitrate startup exits through process owner

---------

Co-authored-by: XiaoBinGan <XiaoBinGan@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-23 02:23:16 +08:00
amyanger 87657c6297 fix(model): admit safetensors header lengths that resemble pickle markers (#5918)
A safetensors length prefix such as 640 encodes as 80 02 00 ..., which the
provider read as a pickle PROTO marker and refused before the safetensors
parser ran. The parser that owns the length prefix now makes the call: a
pickle-like prefix is safetensors only when byte 8 is '{' and the declared
length is within the safetensors header ceiling. Real pickles stay refused
and are never deserialized.

Fixes #5895
2026-10-23 02:22:58 +08:00
Vinícius Andrade af73951405 fix(artifacts): refuse thin Mach-O extraction before inventory (#6152)
* fix(artifacts): refuse thin Mach-O extraction before inventory

Extraction admitted both Mach-O formats and fell through to the universal
slice reader for a thin file, which yields a slices/<arch> entry that the
root-only inventory never recorded. The writer then reported an integrity
mismatch and recommended an integrity policy retry that cannot succeed.

Only mach-o-universal has an extraction reader, matching the inventory
reader selection. A thin Mach-O now receives the typed unsupported-target
refusal before any inventory or output work.

Refs #6098

* test(artifacts): cover the thin Mach-O extraction refusal

A thin arm64e Mach-O image is refused with the typed unsupported-target
error and no output directory is created.

Refs #6098

* fix(artifacts): keep embedded packs behind a thin Mach-O extractable

Refusing every thin Mach-O before inventory also refused PyInstaller
onefile executables and embedded Godot packs whose bootloader is a thin
Mach-O. Inventory classifies those roots as pyinstaller or godot-pck, and
createReader selects that reader before the Mach-O fallback, so they were
extractable on the parent revision.

A thin Mach-O now takes the same embedded-pack probe as ELF and PE: a
detected pack proceeds to inventory, and a plain image keeps the
unsupported-target refusal.

Refs #6098

* test(artifacts): cover a thin Mach-O PyInstaller onefile extraction

Refs #6098
2026-10-23 02:22:42 +08:00
Vinícius Andrade ab2119e9b8 fix(native): keep truncated empty class-import scans partial (#6153)
* fix(native): keep truncated empty class-import scans partial

An explicitly present empty objc_class_import_addresses list with
objc_class_import_addresses_complete false reported binding coverage as
not_requested with a null reason, discarding the caller's declaration that
the candidate scan was truncated. Only an omitted list, or an empty list
declared complete, is not a request; a truncated empty scan now reports
partial coverage with the existing record-budget reason and zero examined
and decoded addresses.

Refs #6086

* test(native): cover empty truncated class-import candidate scans

Refs #6086
2026-10-23 02:22:23 +08:00
Adolanium 6da3839b57 fix(process): capture MCP process scenarios with the server environment (#6173) 2026-10-23 02:22:03 +08:00
amyanger 54ea293e7c fix(model): keep host I/O failures distinct from unavailable model targets (#6175)
Model inspection turned every coded host error into a selected-target
refusal, so EMFILE at open or EIO at header read on a valid model came
back as target_unavailable with advice to check the path. Callers were
told to reselect a model that was fine.

Only codes that describe the selected path (missing, not a directory,
symlink, name too long, non-regular, changed) keep that refusal, as the
Go provider already does. Other host errors reach the existing
execution_failure fallback with the path and reason.

Fixes #6004
2026-10-23 02:21:50 +08:00
xjh 25a0f3c3b8 fix(pcap): locate parent validation issues within analysis-view source (#6195) 2026-10-23 02:21:31 +08:00
Dragolone 6b55802df6 fix(wasm): test glue literals for a .wasm suffix in linear time (#6237)
associateWasmGlue tested every glue string literal with
/\.wasm(?:[?#].*)?$/iu. Each .wasm? or .wasm# occurrence scanned to the
next line terminator and failed at $, so a multi-line literal with many
such occurrences took quadratic time on the event loop (about 2.6 s for
192 KB, within the 8 MiB glue limit). Because . cannot cross a line
terminator and $ only matches at the end, test the literal's last line
only; results are unchanged.
2026-10-23 02:21:12 +08:00
Christopher Pruijsen 4b4babf1e7 fix(doctor): read each client's own activation key (#6331)
* fix(doctor): read each client's own activation key

Doctor treated both `disabled: true` and `enabled: false` as a disabled
registration for every client except Grok. The installed clients each
read only one of them: Codex and Kimi Code honor `enabled` and still
load a server marked `disabled`, while Devin honors `disabled` and
still loads a server marked `enabled: false`. Doctor reported those
loaded registrations as stale, so `rea doctor --client codex` failed
with config_drift and setup offered to "repair" a working entry.

Skip the key each of these clients ignores. Other clients keep the
existing rule.

* fix(tests): resolve the Devin config root per platform in activation cases

The client activation cases wrote Devin's mcp_config.json under the
XDG root on every platform, but devinDirectory resolves APPDATA (or
~/AppData/Roaming) on Windows. Follow the client's per-platform root
so the cases exercise the file doctor actually reads there.
2026-10-23 02:20:55 +08:00
Abel YagubyanandAbelo9996 26bc77ce56 fix(browser): keep refresh guidance for a missing CDP target (#6340)
Co-authored-by: Abelo9996 <Abelo9996@users.noreply.github.com>
2026-10-23 02:20:37 +08:00
gongfppandgongfpp 7aee876d81 fix(browser)!: preserve storage value code units (#6341)
Keep ordinary UTF-8 digests and secret redaction, separate malformed UTF-16 preimages, and record storage fingerprint provenance. Compare legacy hashed snapshots as unknown without treating encoding metadata as a target change. Cover producer boundaries and real owned-browser CLI/MCP workflows.

BREAKING CHANGE: legacy hashed browser storage remains readable but comparisons return unknown until both sides are recaptured.

Co-authored-by: gongfpp <22683417+gongfpp@users.noreply.github.com>
2026-10-23 02:20:16 +08:00
morluto 49c804b7ec fix(evidence): preserve bundle comparison failure ownership 2026-10-23 02:13:49 +08:00
Mohamed Ayoub Eleuch 295e7efddf fix(analysis-view): preserve artifact parent schema diagnostics (#6361)
* fix(analysis-view): preserve artifact parent schema diagnostics

* fix(artifacts): locate nested Evidence validation failures
2026-10-23 01:59:49 +08:00
morluto 9a426033ff refactor(core): import shared contracts from their owners 2026-10-23 01:57:31 +08:00
6dc0f60faa refactor(process): share paired prlimit ceiling ownership (#6352)
* refactor(process): share paired prlimit ceiling ownership

Route firmware, Wakaru, and pcap paired ceilings through
LinuxResourceLimits without changing their byte or CPU budgets.
Keep EVMole's soft-only limits as a dated architecture-guard exception.

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

* test(pcap): admit capture limits without host prlimit

The ceiling test only checks the shared argument prefix, so it uses a
temporary executable instead of /usr/bin/prlimit.

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

* style(pcap): format the capture ceiling test

oxfmt rejected the object literal wrapping in PcapNativeBound.test.ts.

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

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: morluto <morluto@users.noreply.github.com>
2026-10-23 01:46:35 +08:00
morluto 44a4c1ba87 refactor(native): centralize procedure and metadata input contracts 2026-10-23 01:43:23 +08:00
Ammar JamilandClaude Opus 5.5 0d5c7b6d79 fix(windows): report missing native launch controls as unavailable capabilities (#6277)
* fix(windows): report missing native launch controls as unavailable capabilities

An admitted Windows native artifact may predate the optional protocol stdin
or Job Object memory limit controls. WindowsOwnedProcess correctly refused
such launches, but threw a plain Error, so providers projected the refusal
as execution_failure with generic `rea doctor` advice and left the package
remedy in nested diagnostics.

Introduce RuntimeControlUnavailableError as the shared owner of expected
host-control refusals. PrivateRuntimeRootUnavailableError and the new
WindowsNativeControlUnavailableError (which records the missing control)
extend it, and the six provider adapters that hand-built the same
AnalysisCapabilityUnavailableError now project any subtype through one
helper. The refusal still precedes every native launch.

Fixes #6050

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

* refactor(errors): drop capability imports superseded by the shared helper

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 01:41:25 +08:00
gongfppandgongfpp 77c72b96aa fix(process): propagate comparison contradictions (#6298)
Co-authored-by: gongfpp <22683417+gongfpp@users.noreply.github.com>
2026-10-23 01:39:26 +08:00
Wade 510838bd70 fix(evidence): report contradictory unknown input as invalid input (#6302) 2026-10-23 01:38:51 +08:00
MeskineMorty 49bb6c84b0 fix(pyinstaller): report changed admission as integrity (#6325)
Map RegularFileChangedError from reader admission to an integrity failure instead of the generic I/O and archive-open diagnostic. This preserves the changed-source condition and the existing stable-artifact recovery guidance.
2026-10-23 01:38:22 +08:00
NIKHIL 5fbea61e5a fix(javascript): report path data records as route candidates (#6360)
* fix(javascript): report path data records as route candidates

A string `path` or `route` property was reported as an exact, observed
route endpoint whatever object held it, so data records such as
`{ type: "set", path: "updatedAt" }` became routes while a real
`app.get("/x")` stayed a low-confidence candidate.

Keep the property finding, but mark it as a candidate unless its object
has router fields (`component`, `element`, `Component`, `children`, or a
typed `_$` record), using the same shape check that already selects
automatic route roots. Route candidates get a route-specific limitation.

Fixes #6349

* fix(javascript): scan each route record's keys once

`#property` read the enclosing object's keys for every `path` or `route`
property, so an object repeating those keys was scanned quadratically.
Cache the record shape per object and share it with `#object`.
2026-10-23 01:37:28 +08:00
791f6328b0 fix(mcp): escalate transient failures before asking for a restart (#6353)
* fix(mcp): escalate transient failures before asking for a restart

Give dropped connections and startup failures one bounded retry, then a
scripted handoff to the user. Point timeout recovery at a named request
field or REA_* setting instead of telling agents those limits are fixed.

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

* fix(process): stop unexpected command failures from recommending a retry

The catch can already have launched the target, and the projection stays
non-retryable. Tell the user to check the inputs and run doctor instead.

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

* fix(mcp): limit retries to safe calls and recommended settings

A dropped connection may already have changed the target, and a bad
catalog rejects the same startup every time. Retry only a repeatable
call, and change a setting only when the remediation recommends it.

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

* test(mcp): match recovery guidance across wrapped lines

Startup handoff capitalization and markdown wrapping still carry the
same sentences. Compare the prose with whitespace collapsed.

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

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: morluto <morluto@users.noreply.github.com>
2026-10-23 01:32:22 +08:00
Raj Aryan e28c11f87d fix(rizin): enforce UTF-8 recent output byte limit (#6314) 2026-10-23 01:29:54 +08:00
OrdoAbChao b9ca37b51f fix(reconstruction): classify residual graph failures as invalid input (#6321) 2026-10-23 01:28:58 +08:00
SathishM 09416de0cd docs(testing): note Windows source-checkout suite expectations (#6323)
Document that the general source suite runs on Linux in CI and that Windows CI selects a curated lane, so Windows contributors compare failures against the base revision instead of attributing POSIX-only failures to their change.

Fixes #6308
2026-10-23 01:28:46 +08:00
morluto 888b2afc0d fix(web): preserve script export failures through cancellation 2026-10-23 01:16:58 +08:00
morluto 106779208d refactor(core): use canonical file outcomes and unknown schema 2026-10-23 01:16:58 +08:00
7501911f17 fix(lifecycle): retain exact cleanup owners when close or prelaunch fails (#6357)
* fix(hopper): retain file-offset reads when descriptor close fails

A rejected source close was classified as a source or access failure and
dropped the descriptor, so client close reported success. Keep the
completed snapshot or the earlier non-regular-source failure, report the
close as typed cleanup, and retry that exact handle.

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

* fix(native): retain LLDB prelaunch cleanup ownership

Preparation failures were rewritten as an LLDB exit, and discovery
failures dropped the resolver supervisor. Preserve compiler facts and
the foreign helper marker, and retry only the tracer-owned resolver
before another trace is admitted.

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

* fix(hopper): bind combined close failures to both owners

A descriptor close and a session close that fail together now stay incomplete until each owner resolves.

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

* fix(native): retain resolver preparation cleanup ownership

LLDB discovery keeps compiler facts and the foreign helper marker when tool resolution fails during preparation.

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

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: morluto <morluto@users.noreply.github.com>
2026-10-23 01:06:38 +08:00
morluto a6b9312577 test(javascript): assert the shared cancellation contract 2026-10-23 01:00:58 +08:00
morluto b43daa166f fix(analysis): retain typed outcomes through final cleanup 2026-10-23 01:00:58 +08:00
fd5cd1b058 test(acceptance): run loader ownership once through stdio MCP (#6354)
Keep every competing-loader source on the shared stdio session and compare
three renderer outcomes with the compiled CLI. Retire the Early, Middle, and
Late shards, and seed their historical duration onto the consolidated file.

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: morluto <morluto@users.noreply.github.com>
2026-10-23 00:50:47 +08:00
morluto 304c978368 fix(pcap): preserve completed Evidence on late cancellation 2026-10-23 00:32:35 +08:00
gensokyo-dev 54f51f0d7d fix(environment): preserve UUID renewal ownership
Fixes #6343. Normalize renewal identities once, preserve source spelling, and refuse aliases the shared generators cannot reproduce.

Assisted-by: Kogasa (gensokyo-skills:kogasa-surprise-testing)
2026-10-23 00:26:16 +08:00
morluto 566a9a1551 fix(javascript): preserve failure provenance and cleanup ownership 2026-10-23 00:25:26 +08:00
Edgar Arakelyan 957bcfa149 feat(environment): preserve view-specific written clock spellings
Share clock rendering between analysis and local serving. Preserve exact sub-millisecond facts, bound learned format state per field, and charge retained output to the projection budget.

Assisted-by: Kogasa (gensokyo-skills:kogasa-surprise-testing)
2026-10-23 00:12:26 +08:00
morluto 71dc6fe5bd refactor(core): consolidate internal aliases and startup budget ownership 2026-10-23 00:11:27 +08:00
morluto 5bda0a7dc3 fix(entropy): retain completed file outcomes through cleanup 2026-10-23 00:06:00 +08:00
morluto 6d977db1b5 fix(config): remove duplicate capture heap floor export 2026-10-23 00:05:50 +08:00
Edgar Arakelyan 7ec9b6e56e feat(environment): model application-assigned write values
Serve renamed create members and evidence-gated UUID regeneration. Preserve every copied text occurrence and withhold cross-field scalar UUID relations the independent generators cannot reproduce.

Assisted-by: Kogasa (gensokyo-skills:kogasa-surprise-testing)
2026-10-22 23:57:36 +08:00
morlutoandCursor Agent 2127095e3b docs: prefer the pinned Node.js on Cloud Agent PATH (#5624)
Cursor Cloud can resolve an older Node.js before /usr/local/bin. Tell agents to prefix PATH so npm and node use the toolchain environment setup installs.

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2026-10-22 23:54:40 +08:00
584937c761 feat(mcp): make MCP startup phase budgets configurable (#6304)
* feat(mcp): make MCP startup phase budgets configurable

REA_MCP_INITIALIZE_BUDGET_MS, REA_MCP_TOOLS_LIST_BUDGET_MS,
REA_MCP_FIRST_CATALOG_BUDGET_MS, and REA_MCP_DOCTOR_DEADLINE_MS keep
today's defaults and refuse values outside 1 to 2147483647 ms. Codex's
30 second startup figure stays client guidance.

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

* fix(mcp): enforce the first-catalog budget while phases are pending

The packaged startup probe applied each phase timeout on its own and
checked REA_MCP_FIRST_CATALOG_BUDGET_MS only after both requests
finished. A shorter first-catalog budget now clamps the in-flight
request and cancels it from one deadline measured at startup.

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

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: morluto <morluto@users.noreply.github.com>
2026-10-22 23:54:01 +08:00
306ed79932 feat(captures): make the HAR/SAZ/qlog decoder heap configurable (#6299)
* feat(captures): make the HAR/SAZ/qlog decoder heap configurable

REA_CAPTURE_DECODER_OLD_GENERATION_MIB keeps the 192 MiB default and
refuses values outside 8 to 16384 MiB instead of falling back.

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

* fix(captures): defer decoder startup and share one parsed heap

Invalid REA_CAPTURE_DECODER_OLD_GENERATION_MIB threw while CLI commands
were registered, so doctor and help never ran. Build the capture service
only when inspect-web-network-capture runs.

Resolve the heap once in composition and pass that fact to each Node
adapter. Limitation and memory-failure reports use the selected adapter's
heap, so a decoder built with a different environment does not describe
another limit.

Mock captureDecoderProcessMemoryLimitBytes in the Node adapter test so a
Windows Bun host still exercises the unavailable-bound refusal.

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

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: morluto <morluto@users.noreply.github.com>
2026-10-22 23:52:21 +08:00
Edgar Arakelyan 5375ff0f9a feat(environment): extend evidence-gated write coverage
Retain bodiless writes, proven omitted fields, identity-free projections and digit-group sequences. Preserve late projection provenance and current omission facts, and check absence statuses before replay shortcuts.

Assisted-by: Kogasa (gensokyo-skills:kogasa-surprise-testing)
2026-10-22 23:46:52 +08:00
9512a80804 feat(process): make the regex worker heap request configurable (#6301)
* feat(process): make the regex worker heap request configurable

REA_REGEX_WORKER_HEAP_REQUEST_MIB keeps the 128 MiB default and refuses
values outside 8 to 16384 MiB. A positive Node --max-old-space-size still
overrides the request and is not clamped by the setting.

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

* fix(process): set the regex worker heap floor to the measured startup bound

On Node 22.23.4 a 5 MiB old-generation request aborts the regex worker
during startup, and 6 MiB starts it and returns a match. The setting floor
is that measured bound.

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

* fix(process): raise the regex worker heap floor to 7 MiB

6 MiB passes configuration but returns ERR_WORKER_OUT_OF_MEMORY for a
trivial match on supported Linux Node 24 and Node 26.0.0. 7 MiB returned
that match on the measured 22, 24, and 26 lines. A clean-process test
runs the production worker at the floor.

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

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: morluto <morluto@users.noreply.github.com>
2026-10-22 23:43:13 +08:00
Matt Van Horn 3d472bde23 fix(ghidra): reject JDK_JAVA_OPTIONS argument-file platform overrides
Fixes #6212. Parse effective argfile syntax once, admit bounded regular files with retained cleanup/cancellation ownership, freeze validated bytes for subsequent helpers and analysis launch, and share existing private-runtime writing. Resolves the continuation, FIFO and mutable-source review findings.

Verified 97 related tests (3 explicit skips), full static/docs checks, two independent local Codex reviews and 12 real installed-JDK17 grammar/property controls. Real Ghidra, JDK25 and native Windows qualification remain unavailable. Used the explicitly authorized maintainer bypass: current GitHub CI remained queued and Greptile rerun was still in progress; author-readiness metadata was not fabricated.

Assisted-by: Kogasa (gensokyo-skills:kogasa-surprise-testing)
2026-10-22 23:00:50 +08:00
morluto 8761d4251f fix(python): preserve pyc file outcomes and cancelled Evidence 2026-10-22 22:55:22 +08:00
morluto 2846cc0570 fix(input): classify JSON failures at their producing boundary 2026-10-22 22:44:39 +08:00
morluto 30fef57f75 fix(browser): identify scenario timeouts by Playwright error type 2026-10-22 22:26:39 +08:00
Matt Van Horn 5d7d6ad9ab fix(doctor): interrupt Hopper display readiness on cancel
Fixes #6227. Forward caller cancellation to private-display selection and preserve its exact cause after joined selector finalization. Covers active, late-finalization, healthy and pre-aborted controls. Verified 3 x 14 source tests, 22 compiled process/CLI boundaries, full static checks and independent local Codex review. Greptile passed the current head. Used the explicitly authorized maintainer bypass: the current GitHub rerun remained queued and author-readiness metadata was not filled. Original-head code CI passed; no human-review attestation was manufactured.

Assisted-by: Kogasa (gensokyo-skills:kogasa-surprise-testing)
2026-10-22 22:22:22 +08:00
morluto 2589c0074d fix(mcp): attribute doctor timeouts to owned deadline facts 2026-10-22 22:13:31 +08:00
morluto e780148305 fix(cutter): preserve producer failure and cancellation facts 2026-10-22 22:07:11 +08:00
edccb430c4 feat(environment): compare an environment with probe captures of its application (#5699)
* chore: restack environment fidelity onto main

Keep probe comparison, paging, and tied-create behavior on main after #5600. Capture parsing keeps field-path errors and records whether a probe kept request and response bodies.

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

* fix(environment): keep served collection order with the fidelity checks

The tied-create collection order lives with the checks that match encoded parents. The write simulation reads field checks from that attribution, and taint uses the same segment match.

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

* fix(environment): translate an object's keys only when it is keyed by learned ids

Differ.object mapped every key through the probe's id map, so a field named
like a learned id was compared with another field, and a real rename could
pass as a mapped key. Keys now translate only for an object keyed by
learned ids as learning reads one: a key both sides share needs every key
evidently an id, and otherwise each entry is an entity or each key evidently
an id. Any other object compares field by field.

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

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: morluto <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>
2026-10-22 21:52:01 +08:00
Edgar Arakelyan 293832bafc feat(environment): answer list filters, search, sorting and paging from the state
Learn supported filters, plain search, sorting and paging from retained list recordings and evaluate them through shared local-state workflows.

Preserve whole-membership admission, Evidence, query normalization, counter representation and omission. Use successful retry coverage consistently for confirmation and pending-write cleanup. Unsupported semantics remain replayed or unknown.

Validated 623 adjacent source tests, exact-head CI, repeated regressions, real retained GitHub CLI/MCP pagination, actual owned HTTP fixture sessions and Codex task/review runs. Third-party application-write fidelity and exhaustive scale/provider qualification remain unverified; details and measurements are recorded in the PR.

Refs #4933, #6262.

Assisted-by: Kogasa (gensokyo-skills:kogasa-surprise-testing)
2026-10-22 12:41:32 +00:00
FerroLx e93e3b1e7d feat(apk): inspect signing declarations with retained fact queries 2026-10-22 11:48:09 +00:00
2c3dd946d5 feat(python): inspect CPython pyc headers and marshal code objects (#4826)
* feat(python): inspect CPython pyc headers and marshal code objects

Add inspect_python_pyc / inspect-python-pyc to read one explicit local
CPython 3.7–3.13 .pyc without executing bytecode or recovering source.

* chore(python): drop unused PythonPycHeader export

Knip reported the type as unused after inspect_python_pyc landed.

* fix(python): bound pyc expansion and decode marshal like CPython

- Count a nested code object reached through TYPE_REF once per copy the
  report carries, against max_objects and max_file_bytes, before counting
  or validating the tree
- Charge long digits to max_string_bytes, reject out-of-range and
  unnormalized digits, build the value in one pass, and report integers
  above 256 bits by bit length only
- Decode TYPE_UNICODE with surrogatepass semantics and reject other
  ill-formed UTF-8
- Map only 3.11 magics 3494-3495 to the py311 layout
- Report non-finite float and complex parts as inf, -inf or nan
- Allow an empty co_filename
- Require bytes for co_linetable and co_exceptiontable
- Bind cleanup-failure errors to their retained descriptor leases

* fix(python): list pyc code objects once instead of a recursive tree

Report each distinct code object once in code_objects, top-level first,
with nested_code as indexes. A child shared through TYPE_REF is no longer
copied per reference, and the MCP output schema is no longer recursive.
Names repeated through TYPE_REF still count against max_file_bytes for
each copy the report carries.

* fix(python): bound shared pyc constants and reject malformed tables

- Cache byte-string digests so a buffer shared through TYPE_REF is
  hashed once instead of on every reference.
- Charge every reported co_consts entry, including tables shared
  through TYPE_REF, against max_objects before summaries are built.
- Resolve the inspect-python-pyc path with absoluteCallerPath so
  link/../file.pyc reads the file the filesystem selects.
- Reject TYPE_NULL inside marshal sequences, as CPython does.
- Reject a co_localspluskinds length that does not match
  co_localsplusnames in the parser and the report schema.

* fix(python): bound shared pyc names and read CPython marshal graphs

Charge each reported code-constant name, keep integer bit lengths on the
decoded value, and list code objects nested inside constant containers.
Accept self-referential lists and dicts, marshal version 0 text floats,
empty code names, the original string byte count, and Python set and dict
lengths. Pass the CLI cancellation signal into command cleanup.

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

* fix(python): bound pyc key expansion and shared nested code

Charge set and dictionary keys before a shared tuple doubles them, list each
nested code object once per owner, and accept code objects as CPython keys.

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

* fix(python): correct pyc equality, counts, and nested walks

Summarize code objects only after the marshal graph is complete, and
collect nested code with an iterative walk that shares wrapper results
instead of copying every descendant list. Keep tuple boundaries and the
original string bytes in equality keys, and reject negative CPython counts.

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

* fix(python): bound shared pyc names and keep the first dict key

Charge every copied name-table entry against the object budget before
building the report array, including empty strings that add no bytes.
When dictionary keys compare equal, keep the first key object and
replace only its value, matching CPython.

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

* fix(python): walk each pyc dictionary child list once

Keep a container's child array on the nested-code frame so a wide
dictionary is not copied on every step of the synchronous marshal walk.

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

* fix(python): reuse stored pyc dictionary children when combining

The nested walk keeps each container's child list on its frame.
Combining nested code rebuilt that list, and the 8,000-key regression
still accepted the old per-key copy. Use the stored list, and require
that dictionary to finish well under the copy's runtime while still
reporting a nested code object.

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

* fix(python): keep distinct pyc code objects and reject corrupt ones

Encode each name table's length and entry boundaries in the equality
key so a different name list cannot share a memo key. Walk tuple and
frozenset keys on an explicit stack so a long TYPE_REF chain does not
overflow. Treat code objects CPython rejects for an odd co_code or an
undersized local table as malformed instead of complete coverage.

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

---------

Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: morluto <morluto@users.noreply.github.com>
2026-10-22 19:00:08 +08:00
Edgar Arakelyan 4043e71f6c feat(environment): list every entity on a list's whole lines (#5787)
Derive whole-collection membership per checked list line while preserving
recorded membership, counters, source Evidence and independent write claims.

Integrate current main without losing its shared path decoder. Keep later or
ambiguously positioned pages recorded rather than inventing earlier entities.

Validation: 543 focused/adjacent tests, nine README checks, complete static/docs
checks, repeated regression runs, and real retained GitHub-label CLI/MCP
load/reset workflows. An actual Codex session returned the correct facts;
its over-strict grader failure and same-trace semantic verification are recorded.

Evidence and limitations:
https://github.com/morluto/rea/pull/5787#issuecomment-6338446674
Tracking: #6262; feature scope: #4933.

Assisted-by: Kogasa (gensokyo-skills:kogasa-surprise-testing)
2026-10-22 18:34:56 +08:00
morluto 932fbb7d84 fix(artifacts): recover only from owned destination case collisions 2026-10-22 18:17:44 +08:00
morluto a4a548718d fix(erlang): derive budget recovery from parser-owned facts 2026-10-22 18:07:08 +08:00
morluto 3a42a9e356 fix(artifacts): preserve owned file failure and cancellation facts 2026-10-22 18:01:23 +08:00
morluto 46cc5ce056 fix(contracts): resolve local schema references within their resource 2026-10-22 17:53:35 +08:00
Muhammad Hamza 3b2d2863da fix(managed): refuse non-PE inputs as unsupported targets (#6287)
The managed static provider refused a readable non-PE executable with
AnalysisCapabilityUnavailableError, the same type as its unimplemented
operation guard. inspect-managed-artifact, inspect-managed-members and
inspect-managed-native-boundaries therefore reported capability_unavailable
/ unsupported_provider and omitted the selected path.

Return AnalysisUnsupportedTargetError with the operation, path and observed
format instead. Unimplemented operations keep the capability error.

Fixes #6076
2026-10-22 17:50:42 +08:00
ithis0623-pngandithis0623-png aff5e23ecf fix(environment): refuse UNC pathname in file:// URI before fileURLToPath (#6272)
When a file:// URI contains leading slashes representing a UNC path (such as file:////remote.example.test/share/state.json), Node's fileURLToPath on Windows throws ERR_INVALID_FILE_URL_PATH because it expects a drive letter or Windows UNC format. This was caught and misreported as 'a file:// uri names a local absolute path' instead of the intended 'a file:// uri names a path on this host, not a network share'.

Testing UNC_PATH against url.pathname before calling fileURLToPath ensures that UNC paths are consistently recognized and refused as network shares across platforms.

Co-authored-by: ithis0623-png <ithis0623-png@users.noreply.github.com>
2026-10-22 17:50:25 +08:00
Abel YagubyanandAbelo9996 2d91e2d578 fix(il2cpp): refuse a seed path under a non-directory as caller input (#6271)
Co-authored-by: Abelo9996 <Abelo9996@users.noreply.github.com>
2026-10-22 17:48:27 +08:00
Abel YagubyanandAbelo9996 690a63abd6 fix(process): name the pattern field in default invalid-regex errors (#6270)
Co-authored-by: Abelo9996 <Abelo9996@users.noreply.github.com>
2026-10-22 17:48:12 +08:00
Abel YagubyanandAbelo9996 117ded88f0 fix(kuna): name the rejected address in list_strings input errors (#6269)
Co-authored-by: Abelo9996 <Abelo9996@users.noreply.github.com>
2026-10-22 17:47:53 +08:00
bfba99827e feat(erlang): inspect BEAM IFF chunks and tables (#4833)
* feat(erlang): inspect BEAM IFF chunks and tables

Replay the Erlang BEAM inspection workflow, including table bounds,
OTP 28+ atom layouts, the shared absolute-path policy, and empty-path
CLI coverage, without carrying GitHub workflow edits.

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

* fix(erlang): use publicInputStrictObject for BEAM tool input

inspect_erlang_beam advertised a public input built with bare
z.strictObject, so later shared input-policy changes would miss it.
Result and internal objects stay on their own constructors.

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

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: morluto <morluto@users.noreply.github.com>
2026-10-22 17:46:15 +08:00
44827a5020 docs(ko): preserve established OTP and provider terminology (#6156)
* docs(i18n): correct Korean Aegis analysis guidance

* docs(ko): align OTP and provider terminology

---------

Co-authored-by: deuxdoom <188646+deuxdoom@users.noreply.github.com>
Co-authored-by: deuxdoom <deuxdoom@gmail.com>
2026-10-22 17:42:00 +08:00
morluto 055d1a9e1b fix(pe): reject unsupported certificate name encodings
Remove the Latin-1 fallback and the assumed UTF-8 decoding of GeneralString so unsupported name encodings cannot silently corrupt issuer or subject text. Document the limitation and cover the T.61 acute-accent example through the public CLI.

Validation: 30 focused parser/CLI/MCP tests passed; npm run check and npm run docs:check passed. Three new parser regressions fail on the original decoder. Full suite and real historical certificates were not exercised.
2026-10-22 17:25:24 +08:00
ca4ca65c5e feat(pe): inspect Authenticode certificate tables (#4830)
* feat(pe): inspect Authenticode certificate tables

Add inspect_pe_certificates for IMAGE_DIRECTORY_ENTRY_SECURITY
WIN_CERTIFICATE entries and structurally decoded PKCS#7 signer
and X.509 identity fields. The workflow reads a stable PE image
without executing it or verifying signatures, chains, timestamps,
or Authenticode digest coverage.

* fix(pe): bound and validate certificate DER decoding

- Cap decoded DER values with a shared node budget so sibling fan-out
  cannot exhaust memory before the PKCS#7 check
- Require SignedData version, encapContentInfo and a final signerInfos
  SET in RFC 5652 order, and a signature OCTET STRING in each SignerInfo
- Decode OBJECT IDENTIFIER arcs as unbounded base-128 values, including
  a multi-byte first combined arc
- Decode UniversalString names as UTF-32BE and reject invalid values
- Share PE header admission with the resource layout so a section table
  that does not fit SizeOfHeaders is rejected

* docs: list PE certificates in the translated READMEs

* fix(pe): cap OBJECT IDENTIFIER length and arc size

A crafted type-2 certificate with a 262,144-byte contentType OID stalled
decodeOid for about 20 seconds, because one arc kept growing a BigInt
before the signedData check ran. decodeOid now rejects an OID longer than
256 bytes and an arc longer than 32 bytes before decoding it. Certificate
OIDs, including 2.25 UUID arcs, fit under both caps.

* fix(pe): reject truncated certificate tables and PKCS#7

SignedData now requires one SignerInfo with a non-empty signature and a
complete issuerAndSerialNumber. Certificate names share a 64 KiB decoded-byte
budget and are copied once. A security directory that ends in a short
WIN_CERTIFICATE header fails instead of reporting a complete table. The
certificate CLI forwards the caller abort signal into cleanup.

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

* chore(ci): keep fork workflow files unchanged

The certificate restack follows current main. Workflow files stay at the
fork tip so this branch update does not rewrite .github/workflows.

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

* fix(pe): charge serial and validity text to the decoded budget

Certificate and signer serial numbers and validity times now count against
the shared decoded-text budget before they are converted, so a crafted
certificate cannot push megabytes of text into the hashed evidence. The
budget error now names certificate text rather than only names.

Restore the CI workflow to match main: the CI_TIMING_TIER input, the
tier-named timing artifacts and their publication conditions, the Windows
typecheck, and the path and snapshot suites in the curated Windows lane.

* fix(pe): require certificate fields and preserve signer key ids

Reject X.509 records that omit subjectPublicKeyInfo or signatureValue,
return subjectKeyIdentifier bytes as bounded hex, and advertise an absolute
path in the PE certificate input schema.

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

* fix(pe): reject extra PKCS#7 wrappers and invalid UTF-8 names

ContentInfo and its signedData wrapper no longer ignore trailing values, and
directory strings must round-trip as UTF-8 instead of becoming U+FFFD.

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

---------

Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: morluto <morluto@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-22 17:20:07 +08:00
morluto 31dc035041 fix(domain): reject malformed base64 before byte-range checks 2026-10-22 17:16:25 +08:00
gensokyo-dev 21f9af9d93 fix(ci): isolate packed Python syntax checks from ambient imports (#6293)
fix(ci): isolate packed Python syntax checks from ambient imports

Keep caller-local modules and site startup hooks from replacing the
standard-library AST/tar readers or executing during package validation.

Assisted-by: Reimu (gensokyo-skills:reimu-incident-triage)
Assisted-by: Kogasa (gensokyo-skills:kogasa-surprise-testing)
2026-10-22 17:13:07 +08:00
c7efd3a388 feat(config): make the regex matching deadline configurable (#6267)
* feat(config): make the regex matching deadline configurable

REA_REGEX_MATCHING_DEADLINE_MS defaults to 5000 ms and refuses values
outside 1..2147483647. Recovery and capability text name the effective
deadline, its source, and how to change the setting.

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

* fix(setup): preserve the regex matching deadline in registrations

Setup validated REA_REGEX_MATCHING_DEADLINE_MS but left it out of the
selected registration environment, so a caller override never reached
the launched server and matching fell back to 5000 ms.

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

* test(config): restore a stubbed regex matching deadline on failure

Kuna deadline assertions now unstub the environment in finally, so a
thrown construction or expectation cannot leave the setting stubbed.

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

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: morluto <morluto@users.noreply.github.com>
2026-10-22 17:10:51 +08:00
morluto 66aacc6fba refactor(environment): reuse the owned path segment decoder 2026-10-22 17:00:49 +08:00
morluto 79dea89f1f fix(evaluation): own transcript result parsing and error certainty 2026-10-22 17:00:42 +08:00
gensokyo-dev 7d0efef62d feat(javascript): retain queryable static route inventories (#6019) 2026-10-22 16:54:14 +08:00
Kaoru0822andClaude Opus 5.5 933af60eba feat(native): inspect PE and ELF export tables without an engine (#6278)
* feat(native): read PE and ELF export tables

Add a host-independent export-table reader on the shared native
container layer. PE images report each used export-address-table slot
with its ordinal, one row per name, its address or forwarder text, and
the export directory identity; the PE image reader now exposes its data
directories. ELF images report defined default/protected global, weak
and unique .dynsym symbols with type, binding and .gnu.version_d
version (hidden versions kept distinct, unreadable version tables
"unknown"). Unreadable entries are counted in issues and make coverage
partial; an ELF without section headers is unsupported.

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

* feat(native): inspect native export tables through CLI and MCP

Expose the reader as inspect_native_exports / inspect-native-exports:
one stable, digest-bound read of an explicit file with a caller-selected
input budget, typed failures for malformed, unsupported, oversized and
cancelled reads, and observed Evidence bound to the file path and
SHA-256. Mach-O inputs point to inspect_macho.

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

* docs(native): describe portable PE and ELF export inventories

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

* fix(native): bound export inventories and read header-backed PE tables

Address review of the export-table reader:

- Charge every retained row and its name, forwarder and version text
  against shared 262144-row and 16 MiB allowances before storing it, so
  repeated long names or PE aliases cannot exhaust memory.
- Map PE RVAs through the headers as well as sections; the PE image
  reader now exposes its SizeOfHeaders mapping.
- Keep ELF exports with version "unknown" when .gnu.version lies outside
  the file, and report an empty version-definition name as unresolved.
- Reject a .dynsym without its mandatory null entry as malformed.
- Decode the ELF section directory once.
- Correct the docs on which gaps drop rows and which keep them.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 01:30:05 -07:00
Kaoru0822andClaude Opus 5.5 04fd41b288 fix(electron): bind each contextBridge helper invocation to its own API (#6284)
Wrapper substitution keyed contextBridge findings only by the inner
exposure call's range, so every helper invocation replaced the previous
one while unioning its members: the last key survived with every
invocation's members. Substituted members also listed only immediate
object keys, dropping nested paths that direct syntax retains.

Record substituted exposures per site and per world and key, and replace
the site's syntax observation once after traversal. Invocations exposing
one key at one site describe one API covering each alternative object; a
traced invocation whose key, object or world stays unresolved or guarded
keeps the site's own unresolved observation. Enumerate member paths
through resolved objects with the direct syntax depth bound, which also
retains the depth-limit unknown that substitution previously dropped.

Fixes #5871
Fixes #5870

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 01:29:39 -07:00
Kaoru0822andClaude Opus 5.5 002e1b68c6 fix(ghidra): skip the Java probe without an installation (#6282)
Setup and doctor inspect every provider. With no GHIDRA_INSTALL_DIR and no
Ghidra launcher on PATH, the Ghidra inspection still ran the host `java` from
PATH. Its result was discarded because the configuration check already
rejects Ghidra, but its owned-process cleanup could still fail closed. In CI,
that cleanup reported cleanup_incomplete for an unrelated live process whose
/proc/<pid>/environ was unreadable, so `rea setup --client hermes` exited 1.

Run Java only for a selected installation, matching the existing rule for a
relative JAVA_HOME. Hermes setup tests now report the CLI JSON when an exit
code assertion fails.

Fixes #6200

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 01:20:12 -07:00
Kaoru0822andClaude Opus 5.5 73b756430f fix(javascript): read utilityProcess.fork options from the argv slot (#6280)
Electron's utilityProcess.fork(modulePath[, args][, options]) treats a
non-array object in the second argument as the options and ignores the
third. The static extractor only read the third argument, so
`fork("./worker.js", { serviceName })` lost the declared name while
reporting exact, complete coverage, and an object in the second slot let
an ignored third argument supply the name.

Move the optional-argv options selection that child_process already used
into the shared expression-shape owner and reuse it for utility
processes. Unproven argv shapes, spreads and identifier options remain
unknown with partial coverage; nullish options or a nullish serviceName
declare no name, matching Electron's runtime checks.

Fixes #6208

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 01:17:40 -07:00
Kaoru0822andClaude Opus 5.5 2452efa921 fix(ci): make Windows PowerShell observers cheap and cold-start tolerant (#6279)
The private-runtime ACL observer spent most of its time in PowerShell
machinery: Get-Acl module autoload, an Update-TypeData guard that updates the
session type table, and a Where-Object/ConvertTo-Json pipeline. On starved
hosted runners this pushed it past the 15s deadline, and one run never reached
the first script statement within 15s.

Read the descriptor through DirectorySecurity with SID-typed GetOwner and
GetAccessRules, check every compared identity is a SecurityIdentifier, keep
Get-Acl/Access/NTAccount/Translate out of the observer with a static check, and
build the JSON by string. Route the snapshot digest, readonly-children and
sharing-reader observers through the same cmdlet-free helper. Record process
uptime at script start, and give observers a 60s deadline that bounds a hung
child while covering cold powershell.exe start. Asserted facts are unchanged.

Fixes #6275

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 01:03:44 -07:00
Kaoru0822andClaude Opus 5.5 b6007e0a58 fix(process): keep capture evidence when an admitted root's ancestor changes (#6264)
Final filesystem observation reconstructed root admission from the initial
snapshot's root entries, so an initially absent root (no entry) whose ancestor
became a regular file failed capture with ENOTDIR after the child completed.

A returned initial snapshot now admits every selected root for the run. One
tagged path lookup classifies ENOENT and ENOTDIR as observed absence and other
unavailable lookups (EACCES, ELOOP, ...) as entry_unavailable omissions that
keep the root incomplete. Selected roots that are obstructed or denied before
launch still fail capture.

Fixes #6213

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 00:28:26 -07:00
Kaoru0822andClaude Opus 5.5 c9b4cae9dc feat(managed): project call and field-access edges into the application graph (#6260)
project_managed_application_graph dropped the call_edges and
field_accesses that member Evidence already decodes, so the graph was a
containment tree that could not answer who calls a P/Invoke wrapper or
which methods touch a field, while reporting complete-within-inputs.

In-module call/callvirt/newobj/jmp sites now become one calls edge per
caller and target, and field loads, stores and address-takes become one
accesses edge per method and field, each with site and per-opcode counts
and member Evidence links. MemberRef and MethodSpec targets, unknown
targets, ldftn/ldvirtftn loads, recursive self-calls (the graph has no
self-loops), MemberRef fields and sites whose node is absent are counted
by reason in limitations, and the graph is partial with that
omitted_count. Add the provider-neutral accesses relation.

Graph Evidence projected before these counters existed still parses,
with null calls and field_accesses summaries.

Fixes #4056

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 00:26:56 -07:00
Kaoru0822andClaude Opus 5.5 86357bad54 fix(setup): keep comments around REA's table in Grok Build TOML (#6261)
The shared TOML server-table editor kept comment lines immediately only
for the Codex dialect. For Grok Build it queued them with blank lines,
then discarded the queue when an REA table header followed or while it
skipped REA's table, so a comment introducing the stale REA table and a
trailing file note after it were lost on setup and uninstall.

Every comment line is now kept in place for both TOML dialects; only
blank lines that separated a removed REA table go with it. The Codex
wrapper's terminal line-break restoration moves into the shared editor,
so removing a final REA table keeps the file's last line break for
Grok Build too.

Fixes #6207

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 00:26:40 -07:00
Kaoru0822andClaude Opus 5.5 1639189f26 fix(javascript): keep encoded URL slashes inside one runtime path segment (#6259)
A url-prefix runtime mapping decoded the whole pathname suffix at once, so
`child%2Fpart.js` became the nested artifact path `child/part.js` and produced
a medium-confidence location match and an inferred observed_as edge.

Decode each URL path segment separately at the mapping boundary. A segment
that decodes to contain a slash, like a malformed or non-UTF-8 escape, has no
artifact path, so the runtime script stays unknown with
no-authorized-location-mapping. Encoded spaces, Unicode, %25 and the literal
%5C backslash name keep matching.

File-root and URL-prefix mappings now share one tri-state translation
(outside, untranslatable, translated) for matching and load-state scope.
Static nodes beneath a prefix whose runtime script or worker could not be
translated stay unknown instead of becoming not-observed-in-capture.

Fixes #5878

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 00:26:12 -07:00
Kaoru0822andClaude Opus 5.5 c083fedaf4 fix(cli): exclude declared sensitive values from parser diagnostics (#6258)
* fix(cli): exclude declared sensitive values from parser diagnostics

Incur rejects malformed command tokens and output-flag values before the
web capture service applies --sensitive-value declarations, so its
diagnostics repeated declared literals.

Read declarations from the tokens Incur leaves for command parsing,
independently of parse success. Commands accepting the option get an
outermost middleware that excludes declarations from parse, validation
and thrown failures while keeping type, code and exit status. Rejected
--format/--token-limit/--token-offset values are replaced by a
literal-free value Incur still rejects. The option definition and the
Incur builtin-flag rules now have one owner each.

Fixes #6252

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

* fix(cli): redact whole rejected output-flag diagnostics

Incur builds its rejected --format/--token-limit/--token-offset message
from fixed text and the value, outside any middleware. Substituting only
the value left declarations that match the fixed text, such as "token"
in "--token-limit", in the diagnostic.

Report that first rejection at the serve boundary instead, redacting the
whole message as parser failures are, while keeping the UNKNOWN code and
Incur's toon or TTY rendering. Incur's message is now mirrored beside its
acceptance rule, and the value-substitution path is removed.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 00:22:16 -07:00
Kaoru0822andClaude Opus 5.5 c02945bc44 feat(native): decode Swift nominal type declarations from __swift5_types (#6257)
* feat(native): decode Swift nominal type declarations from __swift5_types

Fill swift_decls from the nominal type context descriptors that survive
stripping: kind (class, struct, enum, actor), own and qualified name,
module, generic flag, anonymous-context flag, metadata accessor and
descriptor location, with evidence citing the type record. Unresolved
parent contexts keep qualified_name null and the entry partial.

One shared reader walks each context once; class vtable recovery now
consumes its descriptors instead of re-reading the records, so a type
record that cannot be read keeps both facets partial. Remove the
never-populated swift_decls fields that descriptors do not carry.

Fixes #5341

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

* fix(native): bound Swift context walks and decode vtables in record order

Address review of the nominal-type reader:

- Cache intrinsic context depth (distance to the module) so a cached
  parent cannot bypass the 32-context bound, and never cache a stop that
  depends on where the walk began. Bound qualified names to 4096
  characters so nesting cannot multiply name storage.
- Visit each descriptor as soon as its kind and name are read, so a
  class vtable spends the record budget in record order instead of after
  every later declaration.
- Report an accessor outside executable mapped code as null and keep the
  declaration partial.
- Forward a name failure to vtable coverage only when the record is a
  class or its kind is unknown.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 00:19:15 -07:00
Matt Van Horn 2edf3b23c1 feat(elf): inspect PT_NOTE and SHT_NOTE tables (#4837)
Inspect selected ELF32/ELF64 PT_NOTE and SHT_NOTE declarations without executing
the artifact, retaining independent table identities, source ranges, descriptor
digests, GNU build-id/ABI fields and explicit coverage limitations.

Integrate mvanhorn's implementation and hardening with current main while
preserving byte-entropy registration and the Windows curated regression suite.
The maintainer follow-up uses ordinary merges/pushes, not history rewriting.

Executed validation: 96 focused/shared/docs tests, complete static/docs checks,
eleven real CLI/MCP pairs including GCC ELF32 and Clang big-endian PowerPC64,
and two actual Codex task traces checked against their Evidence. Full costs,
grader correction and verification limits are recorded in PR #4837.

Refs #6262.
2026-10-22 15:02:23 +08:00
Kaoru0822andClaude Opus 5.5 c8ed328538 fix(call-path): count only expanded edges as explored (#6256)
build_call_path summed the out-degree of every reached function, so a
terminal goal's callees counted as explored even though the forward
search never expands the goal. Count explored edges and depth at the
expansion boundary inside the search, drop the post-hoc summary and the
now-unused out-degree helper, and reuse the schema-owned explored type.

Fixes #5906

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 23:32:35 -07:00
Kaoru0822andClaude Opus 5.5 ef5ab1f1f2 fix(apple): decode NIBArchive booleans with AppKit's polarity (#6255)
The NIBArchive value decoder read type tag 4 as true and 5 as false, the
reverse of what ibtool writes and AppKit's decoder returns, so every
compiled-nib boolean in decode-interface-builder output was inverted. The
test fixture encoder shared the reversed mapping, hiding it in round trips.

Name the value-type tags once and use them in both the read and skip
paths, fix the fixture encoder, pin raw tags in a unit test, and make
verify:interface-builder compare both polarities with ibtool --export.

Fixes #5541

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 23:32:21 -07:00
Kaoru0822andClaude Opus 5.5 14877a069b fix(nativeaot): preserve COPY bytes when hydration output grows (#6254)
The DEHYDRATED_DATA COPY writer evaluated the output buffer before the
reservation that could replace it, so a COPY that grew the hydration
buffer wrote its payload into the superseded buffer. The overlay kept the
advanced length and hashed uninitialized bytes while the report claimed
complete recovery.

Reservation now returns a view of the retained buffer, and every writer
(COPY, zero fill, relative and absolute fixups) writes only through that
view, so no caller can hold a stale buffer across growth.

Fixes #5882

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 23:32:07 -07:00
7aa1b51282 feat(diagnostics): report a byte entropy profile for one local file (#2331)
* feat(diagnostics): report a byte entropy profile for one local file

The diff adds the MCP tool inspect_byte_entropy and the CLI command
inspect-byte-entropy, both implemented by ByteEntropyService. One
absolute regular file is read offline into observed Evidence: file-wide
SHA-256 and Shannon entropy, then fixed blocks and adjacent runs for an
optional window, using a power-of-two block size from 256 to 65536
(default 4096), with high at 7.2 or above and low at 1.0 or below. A
window past EOF, a missing path, or a non-file is an input error; a size
or identity change during the read is an artifact-changed error; empty
input reports entropy 0; cancellation is honored before and during the
read. Summary detail retains the complete series and returns file
entropy, counts, and the ten longest runs for inspect_analysis_view,
while the registered effect remains read-only Evidence and the
diagnostics docs record the operation and its errors.

* fix(diagnostics): flag zero-size files as not shown empty

Generated files such as Linux /proc entries report size 0 while holding
content, so inspect_byte_entropy read nothing and returned an empty-looking
report. A file that reports size 0 now carries a limitation that the result
does not show the file is empty. The result stays a success because the
tool reads the declared size, which the file-stability checks depend on.

* fix(mcp): reject unsupported diagnostic summaries before inspection

* fix(ci): repair integrated checks and Hopper test baseline

* test(entropy): use shared MCP schema verification after merge

* fix(catalog): list inspect_byte_entropy under its own provider

Adding inspect_byte_entropy to BINARY_DIAGNOSTICS_TOOL_CONTRACTS put it
under pwntools-elf in the generated product catalog, because the
pwntools group took every binary diagnostics tool except
inspect_pe_resources. The service reports provider byte-entropy.

Add a BYTE_ENTROPY_PROVIDER identity beside PE_RESOURCES_PROVIDER, use
it in ByteEntropyService, give it its own catalog group, and name the
pwntools tools (inspect_binary_layout, inspect_recorded_crash)
explicitly so a new diagnostics tool is not attributed to pwntools by
default. The catalog test pins both groups.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KFTE4BHbGYzgf8Uhzcppdh

* fix(cli): resolve the byte-entropy path with absoluteCallerPath

main replaced the node:path resolve import in binaryDiagnosticsCommands.ts
with absoluteCallerPath (#4067), so the byte-entropy command's
resolve(args.path) no longer type-checks once the PR is merged with main.
Use absoluteCallerPath like the other commands, and add
src/filesystem/callerPath.ts matching main so the branch builds on its own.

* fix(diagnostics): retain entropy read cleanup ownership

* docs(diagnostics): explain entropy output capacity

* fix(diagnostics): preserve entropy cancellation outcomes

* fix(diagnostics): close the entropy input and warn on large reports

Public inspect_byte_entropy input now uses publicInputStrictObject before
detail is added, so unknown keys stay rejected on the advertised schema.
The tool description warns that complete output can become very large and
points callers to a shorter length or a larger block_size.

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

* fix(diagnostics): keep byte-entropy registration mergeable with main

Register inspect_byte_entropy beside layout inspection and reject
unretained summaries before the file read, without editing workflows.

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

* fix(diagnostics): register byte entropy with the shared tool options

Use toolRegistrationOptions so the handler typechecks on this branch and
on main, and still refuse unretained summaries before the file read.

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

---------

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: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: morluto <morluto@users.noreply.github.com>
2026-10-22 14:19:12 +08:00
538017d1d2 feat(environment): apply evidence-gated write effects to the served state (#5600)
* feat(environment): render more read tools from the state and describe responses

Keep main's environment serving and fold in coverage for non-entity reads,
response summaries, and MCP tool annotations.

Squashed rebase of #5352 onto main.

* fix(environment): keep rendered documents and quoted samples inside their budgets

A recorded body of exponential numbers can render past 256 KiB and turn a
working read into a state size error, so that document stays a recorded replay.
Description samples are measured after JSON quoting, and the replay-fallback
catalog check uses a tool count the longer response descriptions still separate.

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

* feat(environment): apply evidence-gated write effects to the served state

Serve write tools whose later reads confirm the effect, and keep main's
state-file ownership and part-wise reply measurement.

Squashed rebase of #5600 onto the rebased #5352 tip.

* fix(environment): bound and validate write state history

* feat(environment): render more read tools from the state and describe responses

Keep main's environment serving and fold in coverage for non-entity reads,
response summaries, and MCP tool annotations.

Squashed rebase of #5352 onto main.

* fix(environment): keep rendered documents and quoted samples inside their budgets

A recorded body of exponential numbers can render past 256 KiB and turn a
working read into a state size error, so that document stays a recorded replay.
Description samples are measured after JSON quoting, and the replay-fallback
catalog check uses a tool count the longer response descriptions still separate.

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

* feat(environment): apply evidence-gated write effects to the served state

Serve write tools whose later reads confirm the effect, and keep main's
state-file ownership and part-wise reply measurement.

Squashed rebase of #5600 onto the rebased #5352 tip.

* fix(environment): bound and validate write state history

* fix(environment): bound additional collection history

* fix(environment): credit sent fields, negative sequences, and encoded targets

A later overwrite copied surviving derived fields into the sent-member
claim, so a members-only check rejected an update whose sent field still
matched. Serving floored sequences at zero and stored missing history as
zero, so a collection sequence learned from negative ranks skipped the
next value. An absence after a delete forgot the resolved collection, and
an encoded read could not observe an update spelled with another
collection encoding.

Regression tests cover each case.

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

* test(environment): cover resolved encoded update identities

* fix: validate timed update observations

* fix(environment): share write overlap and check clocks on earlier calls

Observation search skips a read begun before the write ended. That
comparison now uses the same overlap as the check path.

A clock learned on a later call was not checked against earlier calls
that kept the field. Serving would stamp those calls too, so the plan
stays unserved. A clock learned earlier is still refused when a later
call does not advance it.

Regression tests cover an overlapping empty update and a published_at
clock that one recorded call leaves unchanged.

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

* fix: preserve partial update evidence

* fix(environment): let an unmodelled write explain only the list members that differ

A failed whole-list read now reports which members alone account for the
difference. An unmodelled write excuses the read's writes only when it came
after each such member's last check and pending writes, so an older change
elsewhere no longer hides a newer write the list shows undone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 7b6aab9d7191e600051693f2c192afcfc743d083)

* fix(environment): explain added fields, tie creates to serving's collection, and bound write descriptions

An update explains a field the entity lacked before when the observing
view had shown the entity, so a server-filled field is not dropped. A
create is tied to the held collection serving picks for its path. Write
descriptions and body refusals quote member names and sample them within
a byte bound. The request body is indexed once per explanation, so copied
and slugged fields no longer rescan it.

Replays 07aca54e onto the restacked branch. Its rule that a read begun
before a write ended teaches nothing is already here (fb6785dc), so only
its test is kept.

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

* docs(environment): state how list reads weigh unmodelled writes, added update fields and quoted members

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

* refactor(environment): keep the observing view helpers with the observations

shownBefore and the item-or-list fields check move to the observe module,
keeping the write effects within the line limit.

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

* fix(environment): resolve unmodelled writes onto the held collection

An unmodelled path kept the request's collection spelling. An archive under
the plain spelling of a collection held encoded marked only that plain
collection unknown, so a later update was blamed for the archive's fields
and dropped. Name that entity the way a modelled write does.

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

* fix(environment): confirm no write through volatile fields or overwritten replacements, and bound the served clock

A members-only check no longer compares volatile fields, which render
from the read itself. A document replacement credits the members it
wrote or removed at its object, so a later merge that overwrites them
takes its confirmation away. A recorded time past the range of a date
gives no served clock instead of throwing.

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

* fix(environment): leave unknown entities unchecked by failed reads, volatile fields and unchecked calls

A failed item read that is no absence leaves an unknown entity unknown, so
a later usable read still checks its writes. A read showing an unknown
entity again checks no write on a volatile field. An update of an unknown
entity takes its recorded outcome and adds nothing to a built plan. A later
update that does not answer with its entity is observed only by a read
whose view shows every learned clock; without one it is unmodelled.

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

* fix(environment): fill a create's path fields from the collection serving selects

The gate built a later create's path fields from the recorded request path,
while serving fills them from the held collection it selects for that path,
so a read showing the other spelling's parent could confirm a create serving
cannot reproduce. Rules still learn from the recorded path.

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

* fix(environment): name unmodelled writes as modelled ones, count fields updates write, and keep later members' types

An unmodelled write below an item path names its entity through itemTarget,
and one at or above a collection resolves the held collection, as modelled
writes do, so an encoded spelling no longer hides the entity it changed.
An update's answer counts the fields its rules write, other than increments,
as available, so a field only the update writes no longer makes it
incomplete-entities. A member a later call first stores keeps that call's
JSON type in the body's kinds, which the body check falls back to.

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

* fix(environment): let a document replacement overwrite everything under its object

A replacement claims only the members it wrote or removed, but it now
overwrites every earlier effect under its object, so a merge that wrote
the object it replaces loses its confirmation.

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

* fix(environment): start the served clock from the records whose times a date can hold

A record timed past the range of a date no longer leaves the served clock
without a start, which the store read as zero and stamped writes in 1970;
the clock starts after the latest record a date can hold.

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

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: morluto <morluto@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 14:15:35 +08:00
morluto b8c1b45316 fix(views): centralize retained-result validation errors 2026-10-22 14:14:35 +08:00
morluto 407a718050 refactor(browser): share scenario transaction identity indexes 2026-10-22 14:04:28 +08:00
morluto 51be954c4b refactor(domain): reuse canonical ordering and digest owners 2026-10-22 14:04:28 +08:00
Kaoru0822andClaude Opus 5.5 c537c52c96 fix(environment): report capture payload failures at their field paths (#6247)
Authenticated capture Evidence whose operation is supported but whose
normalized_result fails its capture schema fell through to a generic
evidence.operation refusal, discarding the schema issues. Each recognized
operation now refuses with the projected schema issues beneath
evidence.normalized_result and retains the schema failure as the cause;
only an operation no capture schema owns blames evidence.operation.

The view Evidence parser had the same conflation: a wrong operation, a
wrong kind and a malformed payload of the requested kind all blamed
normalized_result.kind. It now reports role.operation, the kind, or the
payload's own field paths respectively.

Fixes #6119

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 22:22:45 -07:00
Kaoru0822andClaude Opus 5.5 6f96d7a3fa fix(capture): report scenario path search denial as access denied (#6244)
Electron and process scenario path admission caught every realpath or
stat failure as an invalid_value input issue, so a correct path beneath a
directory without search permission returned invalid_request with
schema-correction recovery instead of a host access denial.

Admission now classifies EACCES/EPERM from path resolution and metadata
reads as the existing AnalysisAccessDeniedError (selected path,
system_code, filesystem-read boundary). Missing, looping or otherwise
unresolvable paths and reachable files without execute permission keep
their input-error semantics. Electron reports invalid selections before a
denial on another field. Process PATH search still continues past a
denied directory; only an explicitly selected executable reports denial.

Fixes #6117

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 22:22:15 -07:00
郭立 Guo Li 7590b50801 perf(schema): reduce retained integer validator allocations (#4360)
Construct integer formats directly while preserving bounds, refinements, metadata and current shared schema owners. Keep Incur-dependent CLI numeric coercion declarations unchanged.

Verify canonical advertisements, 126 numeric boundary cases, 974 actual CLI parser cases, broad source tests and real public-artifact SDK workflows. Record 72 normal tasks and 12 separate owned-inspector heap diagnostics with whole-family resource bounds; distinguish SDK spans and asymmetric audit overhead. Generate numerical report sections from receipts and reject stale tables through documentation conformance checks.

Closes #6236. Current-head CI and Greptile pass; actionable review findings are addressed. Codex review was unavailable due repository usage limits.
2026-10-22 05:11:08 +00:00
Kaoru0822andClaude Opus 5.5 bd5d189a8f docs(javascript): state that duplicate unmapped content stays ambiguous (#6238)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 21:20:02 -07:00
Kaoru0822andClaude Opus 5.5 89268f4a45 fix(ci): run the lockfile-pinned Skills CLI in package verification (#6235)
* fix(ci): run the lockfile-pinned Skills CLI in package verification

The repository skill stage fetched skills@1.7.2 and its unlocked
dependencies from the npm registry on every run, taking 5-120 s and
intermittently exceeding the 120 s stage timeout on main and PRs.
Pin skills as a devDependency and run its locked bin directly.

Fixes #6234

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

* fix(ci): lock the Skills CLI outside the root dependency tree

skills 1.6+ requires Node >=22.20.0, so pinning it as a root
devDependency made `npm ci` fail under engine-strict on the supported
Node 22.19.x. Keep the root install unchanged and give the verifier its
own package.json and lockfile, installed with `npm ci --prefer-offline`
in the verification workspace. The lockfile still removes all registry
metadata resolution and pins every transitive dependency.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 21:19:45 -07:00
Kaoru0822andClaude Opus 5.5 3637205622 feat(native): let callers raise the Apple dispatch metadata input budget (#6233)
* feat(native): let callers raise the Apple dispatch metadata input budget

inspect_native_dispatch_metadata refused any Mach-O over a fixed 64 MiB
before decoding, and its recovery said there was no override. Most large
macOS binaries exceed it.

max_input_bytes (CLI --max-input-bytes) now selects the budget, default
64 MiB and at most 1 GiB, through the shared input contract, the enhanced
workflow and the native provider. Results record input_admission with the
effective budget, its source and the retained bytes. A refusal names the
observed size, the budget and its source, and the budget to select or, past
1 GiB, a deep analysis provider.

Fixes #3917

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

* fix(cli): describe --max-input-bytes in inspect-native-dispatch-metadata help

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

* test(native): cover max_input_bytes forwarding through CLI and MCP

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 21:19:33 -07:00
Kaoru0822andClaude Opus 5.5 b4f9f0deac fix(pcap): report workspace allocation failures by their system cause (#6229)
* fix(pcap): report workspace allocation failures by their system cause

Pcap workspace allocation and snapshot writes mapped every filesystem
error, including ENOTDIR and EACCES from mkdtemp, to a file-size resource
constraint with null limits and worker file-size advice. Flutter's owned
snapshot storage had the same errno-blind mapping, while TShark and sigrok
carried duplicate copies of the correct split.

Move the private-workspace failure projection into one shared owner:
ENOSPC/EDQUOT/EFBIG stay file-size resource constraints with their system
code and path; descriptor exhaustion and every other failure become
provider workspace failures carrying stage, workspace path, failing path,
system code and cause-specific recovery.

Fixes #6123

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

* fix(flutter): locate owned storage failures by their workspace

mapOwnedIoFailure always passed a null workspace path, so a descriptor
write or read failure without its own errno path lost every location.
Thread the owned root through: allocation keeps null (no workspace yet),
snapshot failures use the allocated root, and package-reader system
errors propagate to the request owner, which maps them against the
snapshot root instead of mapping early without it.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 21:19:18 -07:00
Kaoru0822andClaude Opus 5.5 ec0e663cca fix(browser): keep target detail when attributing CDP failures (#6223)
Authorize CDP targets with the caller's operation instead of a fixed
inspect_web_page label, and reuse the authorization owner's derived
origin scope. When a browser observation failure is attributed to a
different operation, keep its producer detail, remediation, cleanup,
diagnostics, partial observation and original cause rather than
rebuilding it from the reason alone. Both the CDP browser and CDP
Electron providers use the same domain-owned relabel.

Fixes #6122

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 21:19:00 -07:00
Kaoru0822andClaude Opus 5.5 26356dc34a fix(process): record withheld unrelated Linux processes as unverified (#6220)
* fix(process): record withheld unrelated Linux processes as unverified

The token sweep after an owned command (for example the Ghidra Java probe
run by setup and doctor) failed cleanup when an unrelated same-UID process
started during the sweep and Linux denied its /proc/<pid>/environ. Setgid or
setuid programs (mismatched real/saved IDs) and non-dumpable processes are
withheld by the kernel exactly like another account's processes, which were
already recorded as unverified.

Derive the kernel restriction from the same stat-bracketed procfs read that
already supplies the effective UID: all Uid/Gid IDs from status, and the
root ownership procfs reports for a non-dumpable process's environ. Keep that
fact through exit settlement. Related candidates still fail closed.

Fixes #6204

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

* test(process): define caller credentials on hosts without geteuid

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 21:18:47 -07:00
Kaoru0822andClaude Opus 5.5 b928963675 feat(native): inspect a universal Mach-O slice in Go and Rust metadata readers (#6219)
* feat(native): inspect a universal Mach-O slice in Go and Rust metadata readers

inspect_go_binary and inspect_rust_binary refused universal Mach-O files and
offered no slice selection, so hosts without lipo could not inspect them.

Both now accept architecture (x86, x86_64, arm, arm64). One selector reads
REA's own FAT table, validates the slice range, alignment and header CPU,
and returns the thin image with its macho-selected-image-v1 identity. The
universal file stays the artifact; results add selected_slice and keep
every source range a file offset. Missing, absent, duplicate or misplaced
selections fail on architecture and list the slices present. Subject
format mach-o-universal now has one owner shared by the services and the
retained Go/Rust views.

Fixes #4102

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

* fix(native): reject damaged universal files and self-contradicting slice reports

A FAT table whose slice could not be read was treated as a non-universal
file, so callers were told to omit architecture. Its reason now fails on
path. Go and Rust reports with a selected slice must be Mach-O and name the
slice's CPU family, through one shared agreement check. The CLI
architecture flag is described in help.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 21:18:35 -07:00
Kaoru0822andClaude Opus 5.5 8451b961ad feat(artifacts): make ASAR header admission configurable (#6209)
* feat(artifacts): make ASAR header admission configurable

ASAR header/index admission was a fixed 16 MiB with no override, so
operators with large Electron indexes could only extract the archive.

REA_ASAR_MAX_HEADER_BYTES now sets it (default 16777216, maximum
268435456). One owner resolves the effective admission for the
classification probe, inventory, ASAR reader and Electron file scope,
including the Electron header cache budget. Startup and doctor validate
the setting; admission refuses an invalid value instead of falling back.
Recovery and error details name the effective value and its source.

Fixes #6176

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

* fix(artifacts): resolve ASAR header admission from the caller's environment

The admission read process.env, so composition roots that select their own
environment (createDirectAnalysis, createServer, CLI, optional observation
factories) could not raise or lower it, and an ambient value leaked into
callers that left it unset.

Nothing reads process.env now. ArtifactProvider and extraction resolve the
admission from their environment; JavaScript analysis parses it from the
environment its server or CLI caller passes and refuses an invalid value;
the Electron and V8 providers receive it from their factories. Inventory,
nested ASAR readers, classification probes and Electron page authorization
carry that one value. A header cached by Electron under a larger admission
is refused for a smaller one.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 21:18:22 -07:00
Kaoru0822andClaude Opus 5.5 4a4bbab3c3 fix(cli): keep committed evidence bundle receipts after cancellation (#6221)
Summary delivery with --evidence_output wrote the bundle, then let the
command's close/cancellation check replace the result. A SIGINT/SIGTERM
during staging cleanup after commit returned typed cancellation without
the receipt, and a persistent staging cleanup failure after commit was
rewrapped as a provider cleanup error that dropped published_path.

Every --evidence_output command now selects its view first and publishes
through publishEvidenceDelivery, the owner inspect-managed-members already
used. Post-commit cancellation keeps {path, bytes} under
details.diagnostics.evidence_bundle; incomplete cleanup keeps
details.published_path and bytes; a cleanup retry that succeeds returns
the normal receipt; pre-commit cancellation creates nothing and names
the command's operation.

Fixes #6095

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 20:32:26 -07:00
Kaoru0822andClaude Opus 5.5 e5d2805b42 fix(javascript): keep unmapped captured runtime scripts unknown (#6214)
A captured script whose bytes matched no static digest was reported as
high-confidence unmatched even when no automatic or caller-declared
mapping applied to its location in any static layer. The matcher now
computes mapping presence once while locating candidates and shares one
no-candidate classification between the captured-source and source-free
branches, so an unmapped location stays unknown with
no-authorized-location-mapping and is not counted as unmatched.

Fixes #6061.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 20:32:10 -07:00
morluto 6f28b3633b Merge remote-tracking branch 'origin/main' into codex/legacy-cleanup-followup-20261022 2026-10-22 11:11:42 +08:00
morluto c0eb612780 refactor(web): share retained text artifact metadata 2026-10-22 11:11:42 +08:00
morluto 27dc9735e4 refactor(managed): reuse the member comparison input contract 2026-10-22 11:11:42 +08:00
morluto 51e0df97e9 refactor(javascript): own application node merging in the graph domain 2026-10-22 11:11:42 +08:00
Kaoru0822andClaude Opus 5.5 292fd532c9 fix(ghidra): retry retained language catalog closes before admission (#6210)
The language catalog kept failed .ldefs closes in a module-global set that
only a later catalog read retried, and the provider returned a cleanup error
bound to no owner. Registry admission retries provider close before profile
resolution, so the retained handle was never retried and the original error
stayed incomplete forever.

Read definitions through the provider's caller-owned inspection scope with the
shared regular-file owner used by the seed and extension readers. A failed
close is retained as an exact lease in that scope, provider close and the next
catalog admission retry it, and the translated error is bound to the lease so
confirmed release reopens the same registry.

Fixes #6133

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 20:10:25 -07:00
Kaoru0822andClaude Opus 5.5 aa7d960ef5 fix(process): settle owned cleanup with the launcher's own exit proof (#6205)
* fix(process): settle owned cleanup with the launcher's own exit proof

When owned process-group verification fails while the supervised
launcher is still mid-exit, the supervisor now waits up to the kill grace
for its own child-exit observation and verifies once more after it
arrives. Verification is not repeated when the exit was already observed
before cleanup started, so there is no new evidence and the original
failure is kept. Nothing is signaled based on the wait.

Fixes #6134

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

* test(process): bound Hopper session cleanup attempts by exit evidence

The fake Hopper launcher exits after its shutdown acknowledgement. When
the supervisor first observes that exit during a failed verification, it
verifies once more, so the first close makes one or two attempts depending
on timing. The test now asserts that bound. Later session retries, made
after the exit was already observed, make exactly one attempt each.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 20:10:07 -07:00
郭立 Guo Li 0b7213c890 perf(mcp): defer analysis runtime after catalog discovery (#4424)
Serve build-recorded default compact and explicit full catalogs, then materialize canonical analysis handlers on first use. Keep transport, Evidence delivery, profile identity and caller choices consistent. Share sealed MCP server ownership between eager and deferred lifecycle paths.

Validate 125 focused cases, schema/contract repeats, negative controls, builds/static/docs checks and latest-main integration. Record 72 real SDK task trials with independent Rust metadata oracles and whole-family resource bounds; disclose shifted first-RPC cost and unchanged catalog tokens. Qualify the real Rizin small-target debug lifecycle separately. Automated review findings addressed; Codex review unavailable due repository usage limits.
2026-10-22 03:07:31 +00:00
morluto 63fe0eb5db refactor(electron): reuse retention and capture contracts 2026-10-22 10:57:27 +08:00
morluto ddce4bb446 refactor(javascript): centralize source coordinate operations 2026-10-22 10:57:27 +08:00
morluto dd3497e133 refactor(javascript): reuse argument binding lookup 2026-10-22 10:46:25 +08:00
morluto 4f54379831 refactor(json): reuse shared parser at CLI and shape boundaries 2026-10-22 10:46:25 +08:00
morluto d77bc0e8a0 refactor(cli): share analysis snapshot option contract 2026-10-22 10:33:38 +08:00
morluto 527ade245a refactor(views): derive layout summary fields from source contract 2026-10-22 10:33:29 +08:00
morluto dbcec4cf56 refactor(javascript): share result binding projection and facts 2026-10-22 10:33:18 +08:00
morluto 34fd7bdf7d refactor(reference): share resource failure projection 2026-10-22 10:19:28 +08:00
morluto 81ab0ddc95 refactor(managed): derive verification declaration facts from their owner 2026-10-22 10:19:16 +08:00
morluto 306ca984bc refactor(electron): share observed process and window schemas 2026-10-22 10:19:07 +08:00
morluto dd46a13dec refactor(config): share HTTP listener port contract 2026-10-22 10:18:57 +08:00
morluto ce5bc8cb01 refactor(domain): consolidate projection and verification digests 2026-10-22 10:11:02 +08:00
morluto b88bdfccd2 refactor(evidence): share profile and completion digest owner 2026-10-22 10:10:58 +08:00
morluto 34aa22d0aa refactor(javascript): use shared canonical identity primitives 2026-10-22 10:10:55 +08:00
morluto 719d56ebfe fix(evidence): preserve provider execution provenance across workflows 2026-10-22 10:02:31 +08:00
morluto 87c8086fc1 refactor(managed): reuse canonical JSON digest owner 2026-10-22 09:52:58 +08:00
morluto eab4e47916 refactor(ghidra): share session startup observation checks 2026-10-22 09:52:49 +08:00
morluto db87917daa refactor(native): use canonical deadline defaults 2026-10-22 09:52:40 +08:00
morluto 183b4d4e7e refactor(managed): share metadata token schema 2026-10-22 09:33:58 +08:00
morluto 00819258a7 refactor(browser): separate bundle state from AST inspection 2026-10-22 09:33:58 +08:00
morluto c465ff5a81 refactor(evidence): share canonical ASCII ordering rules 2026-10-22 09:21:54 +08:00
morluto 1ac7ce3f3b refactor(native): share call observation outcome schema 2026-10-22 09:21:54 +08:00
morluto 55f5c8db69 refactor(kuna): centralize installation cancellation projection 2026-10-22 09:21:54 +08:00
morluto 5b0373db11 refactor(browser): map artifact failures at one boundary 2026-10-22 09:21:54 +08:00
morluto 73be86c39e fix(android): decode guardian output as UTF-8 streams 2026-10-22 09:11:44 +08:00
morluto 8dbe92a4a3 refactor(reference): centralize source inventory rules 2026-10-22 09:11:44 +08:00
morluto 5a28a147f1 fix(hopper): preserve streamed Unicode lease owner identities 2026-10-22 09:11:44 +08:00
morluto 13a8640204 fix(browser): observe fetch settlement after synchronous abort 2026-10-22 09:03:51 +08:00
morluto 09b29436f4 refactor(snapshot): share persisted execution projection 2026-10-22 09:03:51 +08:00
morluto 421d527bfe Merge remote-tracking branch 'origin/main' into codex/legacy-cleanup-followup-20261022 2026-10-22 08:52:56 +08:00
morluto 24bd2b989a refactor(browser): derive comparability once per capture pair 2026-10-22 08:52:56 +08:00
morluto b2fad362f2 fix(browser): stop expired work and release deadline resources 2026-10-22 08:52:56 +08:00
morluto 247c8f4eab refactor(javascript): own property capture translation 2026-10-22 08:52:56 +08:00
morluto adfde012ec refactor(readiness): share canonical stage validation 2026-10-22 08:52:56 +08:00
Jay-Alexander Elliot 57fd3ea5d8 feat(cli): summarize managed members with durable Evidence paging (#5167)
Fixes #5148.

Add opt-in durable managed member summaries and authenticated paging while preserving complete default output, original Evidence, cleanup ownership and queued cancellation.

Verified with bounded real Newtonsoft/Redux/Rust tasks, independent oracles, 556 focused tests and the expanded domain gate; paired token estimates and latency tradeoffs are recorded in experiments/analysis-efficiency/managed-cli.md.
2026-10-22 00:43:30 +00:00
morluto d8948efb81 refactor(mcp): bind Electron provider failures at registration 2026-10-22 08:40:36 +08:00
morluto 3966c92c71 refactor(ghidra): derive reference facts from shared schema 2026-10-22 08:40:36 +08:00
morluto 1fbfbf74ef refactor(gdb): own command record snapshots and stream groups 2026-10-22 08:40:36 +08:00
morluto 2604059961 refactor(process): share decoded NDJSON framing 2026-10-22 08:35:20 +08:00
morluto 6b44e35bec refactor(managed): own successful PE inspection identity 2026-10-22 08:35:20 +08:00
morluto 20bf7d957f refactor(setup): reuse desired registration per document snapshot 2026-10-22 08:35:20 +08:00
morluto c9f95eaf90 refactor(javascript): own graph Evidence envelope validation 2026-10-22 08:27:39 +08:00
morluto a2805bc934 refactor(managed): share artifact and token identity schemas 2026-10-22 08:27:39 +08:00
morluto b7529304f0 refactor(browser): centralize shared CDP observation facts 2026-10-22 08:27:39 +08:00
morluto 22c4b528d2 Merge remote-tracking branch 'origin/main' into codex/legacy-cleanup-followup-20261022 2026-10-22 08:20:30 +08:00
morluto 1158dc2a94 refactor(process): own worker frame wait lifecycle 2026-10-22 08:20:30 +08:00
morluto 2765fc12bb refactor(browser): share composed artifact cleanup ownership 2026-10-22 08:20:30 +08:00
gensokyo-dev 18669641e9 test(browser): select a bounded service-worker setup deadline (#6186) 2026-10-22 08:16:34 +08:00
morluto cdd718ffb0 refactor(inputs): own absolute path validation in shared contracts 2026-10-22 08:13:46 +08:00
morluto c5c5ff45aa refactor: import shared limits and projection helpers from their owners 2026-10-22 08:13:46 +08:00
morluto 5da3b7c393 refactor(asar): centralize read failure and cancellation semantics 2026-10-22 08:13:46 +08:00
morluto 2451e187be test(prompts): isolate capture admission regression 2026-10-22 07:51:42 +08:00
morluto 5e6529521c Merge remote-tracking branch 'origin/main' into codex/legacy-cleanup-followup-20261022 2026-10-22 07:49:01 +08:00
morluto 7cec547ecc fix(prompts): suggest only admitted process capture evidence 2026-10-22 07:49:00 +08:00
morluto c1a0561bf3 refactor(mobile): retain inventory sources at the projection boundary 2026-10-22 07:48:39 +08:00
morluto fafc1881b5 refactor(managed): return singular admitted evidence records 2026-10-22 07:48:39 +08:00
morluto a59abe932e refactor(evidence): centralize comparison identities and source recording 2026-10-22 07:45:47 +08:00
morluto 452e3ccad1 refactor(javascript): own source range validation once 2026-10-22 07:45:47 +08:00
8413ec08f7 fix(process): report the effective regex heap and uncertain signal retry (#6099)
* fix(process): report the effective regex worker heap

Node gives --max-old-space-size, including NODE_OPTIONS, precedence over
the worker's 128 MiB resourceLimits request. Name that effective
old-generation heap and stop saying the request has no runtime override.
The matching deadline stays a fixed cap.

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

* fix(process): keep SIGXCPU and SIGXFSZ retries uncertain

The signal does not prove the retained rlimit was exhausted, so an
unchanged retry may succeed. Project that retry as possible instead of
saying it fails the same way.

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

* fix(process): leave percentage heap overrides unestablished

--max-old-space-size-percentage takes precedence over the absolute flag
in either order. Node computes that MiB from startup memory, which a
later read does not recover, so the effective heap stays unknown instead
of the 128 MiB request or an absolute flag.

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

* fix(process): read launch NODE_OPTIONS, not process.env

A preload can rewrite process.env.NODE_OPTIONS after V8 has applied the
original flags, and those flags are not copied into execArgv. Use the
initial environment block when it can be read, and leave the effective
heap unestablished when that provenance cannot be recovered.

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

* fix(process): leave env-file heap overrides unestablished

--env-file and --env-file-if-exists can supply NODE_OPTIONS when that
variable was absent from the initial environment. The file is not in
/proc/self/environ, so a missing entry is not the 128 MiB request.
An initial NODE_OPTIONS is still parsed, because Node does not let the
file replace it.

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

* fix(process): leave --max-heap-size old generation unestablished

A positive --max-heap-size or --max_heap_size sets the total heap, not the
old generation, and can stay below the 128 MiB worker request when an
old-space flag is also present. The effective old generation stays
unestablished while that value is in effect. Zero or empty clears it.

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

* fix(process): leave config-file heap options unestablished

--experimental-config-file and --experimental-default-config-file can
apply a heap percentage from a file that is not launch provenance. The
effective old generation stays unestablished when either flag is present,
including beside an absolute --max-old-space-size.

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

* fix(process): read launch exec argv from the command line

process.execArgv can be cleared after V8 applies --max-old-space-size.
Linux recovers those tokens from /proc/self/cmdline and leaves the heap
unestablished when that vector cannot be separated from the program entry.
Heap probes compare pinned absolute limits instead of a percentage of host memory.

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

* fix(process): leave a title-overwritten command line unestablished

process.title and --title replace /proc/self/cmdline with the title.
A buffer without a program entry is not an empty option list, so the
heap stays unknown instead of the 128 MiB request. The command-line
probe does not read /proc off Linux.

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

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: morluto <morluto@users.noreply.github.com>
2026-10-22 07:40:14 +08:00
morluto 603a7d022c refactor(process): share capture evidence admission 2026-10-22 07:37:21 +08:00
morluto b2dc45006d refactor(cli): share registered command traversal 2026-10-22 07:37:21 +08:00
morluto 1828cf87a0 refactor: remove redundant contract and syntax definitions 2026-10-22 07:37:21 +08:00
morluto a73eac190b refactor: remove obsolete forwarding APIs and unify CI contracts (#6181)
* refactor: remove obsolete internal forwarding APIs

* refactor(ci): centralize failure classification contracts
2026-10-22 07:30:54 +08:00
0e5c511c01 fix(electron): follow absolute symlinks that stay inside the framework (#3587)
* fix(electron): follow absolute symlinks that stay inside the framework

An absolute symlink target is now passed to relativeInside(root,
target), and a null result still ends the walk as an escape. When the
target lies inside the root, resolved is cleared and the root-relative
pieces are queued on pending, which replaces the path walked so far with
that absolute target. Later hops stay under the hop limit and the
existing lstat containment checks. Provider tests now cover an absolute
Versions/Current that stays on in-framework version B and matches the
relative bundle's resolved path and SHA-256, an absolute Current outside
the framework, and an absolute in-framework link whose next hop leaves
the framework; those last two still surface an escape error.

Fixes #3380

* test(electron): cover absolute framework symlink cycles

---------

Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-22 07:21:37 +08:00
2e6b38cef0 feat(environment): render more read tools from the state and describe responses (#5352)
* feat(environment): render more read tools from the state and describe responses

Keep main's environment serving and fold in coverage for non-entity reads,
response summaries, and MCP tool annotations.

Squashed rebase of #5352 onto main.

* fix(environment): keep rendered documents and quoted samples inside their budgets

A recorded body of exponential numbers can render past 256 KiB and turn a
working read into a state size error, so that document stays a recorded replay.
Description samples are measured after JSON quoting, and the replay-fallback
catalog check uses a tool count the longer response descriptions still separate.

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

* test(environment): pin the replay card this branch serves

The replay-only card test pinned main's 11,131-byte card. This branch's descriptions name each tool's recorded responses, so the card it serves is 12,818 bytes; the pin and its name follow.

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

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: morluto <morluto@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 07:08:44 +08:00
d7ca93cc2c fix(artifacts): name the ASAR header admission budget (#6172)
Keep the 16 MiB ASAR header/index ceiling fixed and report that budget,
the lack of a runtime override, and that source selection does not bypass
header admission.

Refs #5910

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: morluto <morluto@users.noreply.github.com>
2026-10-22 06:33:09 +08:00
Afloat 82e4e032df fix(artifacts): complete positional ZIP reads (#4414)
fix(artifacts): complete ZIP positional reads
2026-10-22 06:13:24 +08:00
Abdul Samad Siddiquiandmorluto ca2ca16146 fix(dotnet): reject unfinished CIL prefix chains (#4416)
* fix(dotnet): reject unfinished CIL prefix chains

* test(dotnet): cover unfinished prefixes in fat method bodies

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-22 06:13:10 +08:00
xjh 14c4c2a1d7 fix(managed): preserve observed method header differences (#3814) 2026-10-22 06:12:55 +08:00
Hemang VarshneyandHemang Varshney 4842f23765 fix(source): retain CommonJS requires spelled as exact no-substitution templates (#4511)
* fix(source): retain CommonJS requires spelled as exact no-substitution templates

Admit expression-free template arguments at the CommonJS require gate and
read their single cooked quasi through the shared exact-literal reader.
Scope is the require path only: static ESM template declarations keep their
parse failures and dynamic import() keeps the explicit unknown/partial
<dynamic-import> relationship with no invented target.

Refs: #4382

* fix(source): admit TypeScript-wrapped exact require arguments via the shared literal reader

Address review: the require gate matched the outer AST node, so
runtime-transparent TS wrappers (as const, !, satisfies) around exact
strings or expression-free templates were still silently omitted even
though readExactJavaScriptLiteral unwraps them. Remove the gate and let
the shared exact-literal reader own argument exactness; computed
arguments remain silent and dynamic import() behavior is unchanged.

Refs: #4382

---------

Co-authored-by: Hemang Varshney <218273197+hemv-857@users.noreply.github.com>
2026-10-22 06:12:42 +08:00
7721d59e1a fix(adb): normalize absent installer package sentinels (#4073)
* fix(adb): report a null installer declaration as an absent installer

Package Manager prints its String fields by concatenation, so a package
without a recorded installer shows `installerPackageName=null` in
`dumpsys package`. The projection treated only an empty value as absent
and forwarded the text "null" as a present installer identity, which a
consumer cannot tell apart from a package literally named "null" (#3943).
`versionName` is printed the same way and had the same gap.

The parser now maps the empty value and the `null` sentinel of both
String fields to the nullable result's null at the producing boundary;
a reported installer such as com.android.vending is kept unchanged.

Validation: parser unit tests for the sentinel, empty and missing
installer lines and the absent version name, plus a provider-level
regression through the inspect_adb_package operation; three of them fail
on main and pass here. The issue's four CLI controls through
`rea inspect-adb-package` against a stub adb return JSON null for the
sentinel, empty and missing cases and keep com.android.vending. Typecheck,
lint and module boundaries pass.

Closes #3943

* fix(adb): keep the null sentinel handling to the installer field

Review feedback on #4073: a manifest can declare the literal string
"null" as android:versionName, and dumpsys prints it exactly like an
absent value, so mapping versionName to null would turn a real version
into absence. Only installerPackageName, where no package can carry that
name, treats the printed null as the absent installer. The test for the
version name now pins the retained text.

* test(adb): establish permission inventory in null-installer fixture

Main now reports partial coverage when the requested-permission
inventory is not established (9534e42ec). Give the null-installer
fixture a requested permissions section so it still checks a
complete projection.

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

---------

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 06:11:49 +08:00
76dbc32f39 fix(environment): bound state reads and retain cleanup ownership (#6158)
* feat(environment): extract the initial state a capture's reads observed

Add the environment-state analysis view. From the parents the environment
manifest accepts, it extracts the data the capture's reads observed before
the first related write: entity types with an identity field (proved by a
path parameter or inferred from an identity name), entities with their
recorded values and provenance, documents for written non-entity paths, and
a render plan per read tool that is kept only when it reproduces the tool's
latest initial recordings from the state; every other read tool is a
recorded view with the reason. The Evidence is always inferred.

Response-body decoding moves to a domain owner shared with replay, exact
JSON parsing moves to the owner of the exact-number policy, the manifest and
state views share parent admission and record grouping, and
projectAnalysisView takes its accumulators as one object.

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

* fix(environment): bound state at redacted writes; infer identity only from items

A scenario write whose URL was redacted has no request line, so it was
dropped and reads after it counted as initial state. Such a successful
write now joins the timeline with an empty path, whose root relates to
every type, so it ends every type's state; a limitation counts them.

A list whose recordings were all empty passed the "every listed item
carries it" check vacuously and published an identity field nothing
showed; it now needs at least one item, else the view stays recorded
as no-identity.

A value that differed between two request lines of one item tool, such
as a query variant, was called volatile, and the gate then substituted
each recording's own value; only repeats of one request line now show
volatility, so differing lines go through fields-differ.

List membership is compared only while a list can still be the whole
collection, and merged entities are indexed by type and collection once,
so paged captures no longer cost pages times entities (8,000 pages of
40,000 entities: 5.7 s to 0.23 s).

Docs and the tool contract say scenario state keeps the capture's
redaction markers and how redacted writes bound it.

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

* fix(environment): keep first sightings, one list pointer and linear field unions

Entities took the order of the first plan observed, and item plans run
before list plans, so a complete list read before its detail reads came
out reordered and lost "all" membership; each entity now keeps its
earliest recorded sighting.

A list plan reused the first recording's pointer for later recordings,
so a later response that was itself an entity wrapping a list was read
as that list; every recording must now locate the same list.

The field union scanned its ordered list for every key, quadratic in an
object's width; a set now answers membership.

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

* fix(environment): keep post-write values and partial lists out of the initial state

A value that changed between repeats of one request line was named
volatile whenever no write under the type's own root lay between, so a
write under another root (for example POST /api/dashboards/db before a
second GET /api/search) left its effect in the initial state. Any
recorded write between the two observations now leaves every tool of the
type recorded with reason fields-differ instead.

A list was published with membership "all" even when a total or count
envelope member exceeded the number it listed. Such a counter now makes
the membership "recorded".

Also drop a stale doc comment on ObservedEntity.order.

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

* docs(environment): scope item-shaped and deleted written paths out of documents

State documents are held only for list-shaped paths a POST, PUT or PATCH
tool writes. Reads of item-shaped written paths and of paths that are only
deleted stay recorded views; the guide now says so and the spec defers them
to a later increment of #4933.

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

* fix(environment): count writes in flight when a value changes between reads

The volatility check compared write start positions with the two reads,
so a write already in flight when the earlier read started was missed and
its effect was named volatile. A write now counts when its interval
overlaps the span from the earlier read's start to the later read's end.

The check also ran once per changed field and scanned every write. It now
runs once per pair of observations, answered by a binary search over the
writes in timeline order and the latest end among each prefix.

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

* fix(environment): catch cross-tool changes across writes; read only item totals

A value that changed across a write was caught only between repeats of
one tool's request line, so a list read before a write and an item read
after it put the post-write value into the initial state. Changes are
now classified per entity in timeline order after observation: across
any recorded write, a change between repeats of a line or between two
lines of the type rewrites the type (every tool recorded as
fields-differ); with no write between, a repeat's change is volatile.
The classification lives in environmentStateMerge.ts.

Any numeric member whose name contained "total" or "count" counted as
an item total, so total_pages, account_id or page_count made complete
lists partial. Item totals are now members whose name tokens include
total or count and none of page, pages or per; a string of digits counts
as a number; and a set has_more, next or next-page token member also
marks the list partial.

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

* fix(environment): keep post-write observations out of initial state

* fix(environment): retain field provenance and bound state inference work

* feat(environment): serve tool calls from the environment state

`environment-serve --state <state.json>` serves an environment from the
initial state an environment-state view extracted. Read tools whose views
are state-backed answer from a live state store: item views render the held
entity into the recorded envelope, list views render the collection or the
recorded members, and document views return the held value; a missing entity
or one lacking a required field is refused, and requests the state cannot
answer fall back to their recordings with the reason. The /agentenv data
plane implements data/reset, data/add (a rea-state/v1 document as a data
part, base64 bytes, a file:// path or an https URL, validated and replaced
atomically) and data/get, and the card advertises those operations and an
intake declaration. Stage 1 is read-only: write tools are not served in
state mode. Without --state nothing changes.

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

* docs(environment): record verification after merging the state view's review fixes

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

* feat(environment)!: serve the environment state by default

environment-serve derives the environment state at startup by running the
environment-state view in-process on the capture with the manifest's
template rules, so an environment comes with its state and a user supplies
nothing extra. The derived Evidence is the same one inspect-analysis-view
produces by hand. --state is removed; --replay-only serves exactly the
stateless replay of main. When the state cannot be derived, or its catalog
does not fit the discovery budget, the server starts in replay mode, logs
the reason and reports it in the stop summary's `state`. Exported bundles
serve the state by default too: export derives it the same way, so the
verifier's tool count and checked answer match what is served, and the
README describes the served mode.

BREAKING CHANGE: environment-serve answers read tools from the derived
state and does not serve write tools unless --replay-only is given; the
stop summary reports `state` instead of `state_evidence_id`.

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

* fix(environment): bound data-plane replies, keep batch order, cancel intake on stop

- One /agentenv response is held to 64 MiB: replies are measured as they
  are produced and one past the budget becomes a -32000 error stating it.
- Every message of a batch enters the arrival-order queue on arrival, so
  another request's reset never lands between them.
- Stopping the server, or a caller leaving, cancels the active download
  and answers queued calls `cancelled` without running them.
- file:// URIs name only this host: another host or a network share is
  refused before anything is opened.
- A loaded view value may not give an entity another identity.
- List answers refuse a member lacking a field every recording carried,
  as item answers do.

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

* fix(environment): keep bodies too deep for the state's nesting out of the state

A recorded body at the JSON depth limit was accepted by extraction, then
nested a few levels deeper as a document or entity value, so sealing the
state Evidence threw and environment-serve and environment-export stopped
instead of falling back. stateBodyJson, the one rule extraction and
serving share, now treats a body nesting deeper than the limit minus 16
levels as unusable, so its tool stays a recorded view.

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

* fix(environment): measure data-plane replies before building them

83fcc739 bounded one /agentenv response to 64 MiB, but it still
serialized each reply whole before measuring it, so a batch of data/get
calls on a large state built every refused reply in full only to drop
it. Each reply is now measured in bounded JSON parts (the shared
jsonParts encoder), its parts kept only while it fits; a reply past the
budget is counted for its exact reply_bytes but never assembled. A test
checks that a refused read is never handed to JSON.stringify whole and
that its reply_bytes is exact.

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

* fix(environment): release batch replies as they are encoded; judge exact numbers by value

answerBatch kept every settled reply in its pending array until the
batch ended, so a batch alternating data/add and data/get held each
loaded state alive; each reply now leaves the queue as it is encoded.

The exact-number policy judged a number by its recorded spelling, so a
state loaded with 1e20 came back from data/get as
100000000000000000000, which data/add refused, breaking the promise
that data/get output loads back unchanged. A number is exact only when
both its recorded spelling and the one serialization writes are safe,
so integers from 2^53 below 1e21 are refused in any spelling; replay
keeps such bodies as recorded text. A domain test checks that every
accepted number is accepted again once written back.

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

* fix(environment): keep room for refusals and framing in the reply budget

The response budget counted accepted replies only: a budget error that
replaced a reply, and a batch's brackets and commas, were not charged,
so near 64 MiB later refusals pushed the body past the advertised
limit. Every reply still to come now keeps room for its own budget
error, and a batch keeps room for its framing, so a refusal always fits
and the body never passes the budget. Reply ids and which messages are
answered come from one place shared by the replies and the reserve.

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

* fix(environment): refuse a loaded state that data/get would write past the intake limit

data/add measured only the document it was given, while data/get
writes the state back with the members a document may omit (types, the
state Evidence id, view fields and record citations). A document just
under 32 MiB could therefore load and be reported larger than any intake
accepts, breaking the data/get then data/add round trip. replace now
measures the state as data/get writes it, part by part, and refuses it
past the limit, leaving the state unchanged. The limit moves to the
state document module, so the application store can read it; the
intake re-exports it.

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

* fix(environment): keep merged export checks on the shared test graph

Main's export tests installed module mocks for the whole services
project, which reuses one module graph. A tarball read failure then
missed its importer, and a one-shot heap mock could be taken by another
file. Read those through bindings the tests replace for one case.

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

* fix(environment): keep a state file whose close failed owned until it closes

A file:// data/add that could not close its descriptor dropped the handle,
and an admission failure whose cleanup close failed dropped its owner, so
nothing could retry the close or report it.

The read now runs through withOwnedRegularFile, and a failed close is
retained in a server-lifetime ArtifactResourceScope. The next file read
retries it first and answers -32000 while it is still open; inline and
https documents still load. Closing the served environment retries it again
and rejects with the unfinished close.

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

* fix(environment): attempt all state cleanup on close

* fix(environment): bound wide state JSON traversal

* fix(environment): scope cached state reply sizes to requests

* fix(domain): preserve JSON depth diagnostic guidance

---------

Co-authored-by: Edgar Arakelyan <edgar.arakelyan@scale.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: gensokyo-dev <gensokyo-dev@users.noreply.github.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: morluto <morluto@users.noreply.github.com>
2026-10-22 06:06:36 +08:00
Mohith DK f23066f0da fix(reconstruction): fall back to a kind-based title for blank graph labels (#6137)
Fall back to a kind-based obligation title when a graph label is blank

A whitespace-only application observation or semantic node label passed through ?? as the obligation title, and the ledger schema (trimmed, min length 1) then rejected the whole ledger. Use trim() || so a blank label takes the generated title.

Closes #6126
2026-10-22 05:53:42 +08:00
Abel YagubyanandAbelo9996 cbbd317fb5 fix(process): index late scenario events in validation issues (#6160)
Co-authored-by: Abelo9996 <Abelo9996@users.noreply.github.com>
2026-10-22 05:53:22 +08:00
Abel YagubyanandAbelo9996 d54c4345d3 fix(browser): report zero decoded bytes for undecodable gRPC-web text (#6159)
Co-authored-by: Abelo9996 <Abelo9996@users.noreply.github.com>
2026-10-22 05:53:13 +08:00
bdf0be87c6 fix(mcp): name response-byte and startup ceilings in recovery (#6005)
* fix(mcp): name response-byte and startup ceilings in recovery

Name REA_MCP_MAX_RESPONSE_BYTES, its effective value, the 10485760 byte
floor, and process restart when the stdio response budget is hit, and
state that the setting does not raise Node's string ceiling. Name fixed
MCP startup phase budgets when those deadlines fire, and keep Codex's
30 second startup figure as client guidance.

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

* fix(mcp): distinguish string ceilings and keep dossier recovery

Name REA's reserved string limit separately from Node's ceiling, restore
detail:summary discovery for an oversized complete evidence bundle, and
apply the doctor deadline sentence when binary_session itself times out.

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

* fix(mcp): keep retained SDK errors inside the text budget

Quoted SDK validation diagnostics expand when recovery JSON is escaped
into MCP text. Shorten only that preview so the retained-evidence
reference still arrives as parseable error content.

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

* fix(mcp): keep export advice when a summary exceeds the response budget

An oversized get_evidence_bundle summary no longer repeats detail: summary.
export_evidence_bundle still writes the retained bundle outside one MCP frame,
including when a larger byte budget cannot raise the string ceiling.

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

* style(mcp): format the SDK diagnostic preview return

oxfmt rejected the fitted-preview ternary in EvidenceMcpServer.ts, which failed Static checks and the CI required gate.

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

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: morluto <morluto@users.noreply.github.com>
2026-10-22 05:48:56 +08:00
0155b89953 fix(browser): name web-bundle budgets and recover web deadlines (#6036)
* fix(browser): name web-bundle budgets and recover web deadlines

Name the existing heap, per-script, and total web-bundle phases, and reject a per-script deadline above the total. Promote capture, CDP command, source-map decode, and source-map fetch deadlines at today's defaults. Name DevTools timeoutMs, and state that discovery, cleanup grace, byte budgets, and Electron hook retention have no runtime override.

Refs #5910

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

* fix(browser): apply selected web deadlines to the owning phase

Keep HTTP and browser-socket discovery on the fixed discovery deadline so a
short command_timeout_ms starts at the handshake. Apply scenario
command_timeout_ms to launch startup and the primary connect session, and
keep service-worker restoration on the fixed 5000 ms grace. Omit
command_timeout_ms from recovery text when the operation does not accept it.

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

* fix(browser): retain scenario commands that finish after their deadline

A timed-out scenario command stays owned so cleanup can close a late
browser or context and restore a mutation that lands after the deadline.
Child storage replies use that deadline, scenario recovery names
command_timeout_ms in the capture JSON, and the session test connection
implements beginCleanupGrace.

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

* fix(browser): keep child command timeouts as the public failure

A nested command such as Page.enable that is delivered but never answered
was stored as protocol_error, hiding the command name, selected deadline,
and recovery. Store the typed timeout and assert that public error.

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

* fix(browser): keep runtime cleanup on the fixed grace

Switch runtime restoration to the cleanup grace before the first send so a
short command_timeout_ms cannot fail an otherwise successful observation.
Stop advertising command_timeout_ms on list_browser_targets, which never
bounds a post-discovery command.

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

* test(browser): prove a short command deadline cannot fail runtime cleanup

Drive restoration through the runtime fixture. A cleanup command that
outlives command_timeout_ms still succeeds, and a stalled restoration
names the fixed 5000 ms grace.

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

* fix(browser): name the effective source-map decode deadline

codecBudgetLimitation reported a fixed 20-second deadline after a
caller selected decode_timeout_ms. Result and Evidence limitations
now name that effective millisecond deadline.

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

* fix(browser): separate handshake and target-attach deadlines

A browser-level Target.attachToTarget no longer shares the WebSocket
handshake budget. Blocked service-worker attachment keeps discovery on
its fixed deadline and applies command_timeout_ms to the later handshake
and attach.

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

* fix(browser): name the web-bundle resource each runtime exhausts

Bun ignores the V8 old-space flag. A Windows Job reports the process
commit limit, and other Bun hosts no longer suggest raising max_heap_mb.

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

* fix(browser): cite the browser-socket discovery message budget

WebSocket discovery enforces the 64 MiB protocol budget. Timeout
recovery no longer advertises the 8 MiB HTTP document budget.

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

* fix(browser): bound the blocked service-worker check before discovery

The first registration check has its own fixed 10s deadline. Discovery,
handshake, and Target.attachToTarget stay on their separate deadlines.

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

* fix(browser): name the operand that stopped web-bundle admission

Host heap pressure no longer claims the fixed result budget is exhausted.
A Windows Bun allocation before the commit limit still names that limit.

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

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: morluto <morluto@users.noreply.github.com>
2026-10-22 05:47:38 +08:00
166fb0820f feat(environment)!: serve the environment state by default (#5351)
* feat(environment): extract the initial state a capture's reads observed

Add the environment-state analysis view. From the parents the environment
manifest accepts, it extracts the data the capture's reads observed before
the first related write: entity types with an identity field (proved by a
path parameter or inferred from an identity name), entities with their
recorded values and provenance, documents for written non-entity paths, and
a render plan per read tool that is kept only when it reproduces the tool's
latest initial recordings from the state; every other read tool is a
recorded view with the reason. The Evidence is always inferred.

Response-body decoding moves to a domain owner shared with replay, exact
JSON parsing moves to the owner of the exact-number policy, the manifest and
state views share parent admission and record grouping, and
projectAnalysisView takes its accumulators as one object.

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

* fix(environment): bound state at redacted writes; infer identity only from items

A scenario write whose URL was redacted has no request line, so it was
dropped and reads after it counted as initial state. Such a successful
write now joins the timeline with an empty path, whose root relates to
every type, so it ends every type's state; a limitation counts them.

A list whose recordings were all empty passed the "every listed item
carries it" check vacuously and published an identity field nothing
showed; it now needs at least one item, else the view stays recorded
as no-identity.

A value that differed between two request lines of one item tool, such
as a query variant, was called volatile, and the gate then substituted
each recording's own value; only repeats of one request line now show
volatility, so differing lines go through fields-differ.

List membership is compared only while a list can still be the whole
collection, and merged entities are indexed by type and collection once,
so paged captures no longer cost pages times entities (8,000 pages of
40,000 entities: 5.7 s to 0.23 s).

Docs and the tool contract say scenario state keeps the capture's
redaction markers and how redacted writes bound it.

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

* fix(environment): keep first sightings, one list pointer and linear field unions

Entities took the order of the first plan observed, and item plans run
before list plans, so a complete list read before its detail reads came
out reordered and lost "all" membership; each entity now keeps its
earliest recorded sighting.

A list plan reused the first recording's pointer for later recordings,
so a later response that was itself an entity wrapping a list was read
as that list; every recording must now locate the same list.

The field union scanned its ordered list for every key, quadratic in an
object's width; a set now answers membership.

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

* fix(environment): keep post-write values and partial lists out of the initial state

A value that changed between repeats of one request line was named
volatile whenever no write under the type's own root lay between, so a
write under another root (for example POST /api/dashboards/db before a
second GET /api/search) left its effect in the initial state. Any
recorded write between the two observations now leaves every tool of the
type recorded with reason fields-differ instead.

A list was published with membership "all" even when a total or count
envelope member exceeded the number it listed. Such a counter now makes
the membership "recorded".

Also drop a stale doc comment on ObservedEntity.order.

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

* docs(environment): scope item-shaped and deleted written paths out of documents

State documents are held only for list-shaped paths a POST, PUT or PATCH
tool writes. Reads of item-shaped written paths and of paths that are only
deleted stay recorded views; the guide now says so and the spec defers them
to a later increment of #4933.

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

* fix(environment): count writes in flight when a value changes between reads

The volatility check compared write start positions with the two reads,
so a write already in flight when the earlier read started was missed and
its effect was named volatile. A write now counts when its interval
overlaps the span from the earlier read's start to the later read's end.

The check also ran once per changed field and scanned every write. It now
runs once per pair of observations, answered by a binary search over the
writes in timeline order and the latest end among each prefix.

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

* fix(environment): catch cross-tool changes across writes; read only item totals

A value that changed across a write was caught only between repeats of
one tool's request line, so a list read before a write and an item read
after it put the post-write value into the initial state. Changes are
now classified per entity in timeline order after observation: across
any recorded write, a change between repeats of a line or between two
lines of the type rewrites the type (every tool recorded as
fields-differ); with no write between, a repeat's change is volatile.
The classification lives in environmentStateMerge.ts.

Any numeric member whose name contained "total" or "count" counted as
an item total, so total_pages, account_id or page_count made complete
lists partial. Item totals are now members whose name tokens include
total or count and none of page, pages or per; a string of digits counts
as a number; and a set has_more, next or next-page token member also
marks the list partial.

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

* fix(environment): keep post-write observations out of initial state

* fix(environment): retain field provenance and bound state inference work

* feat(environment): serve tool calls from the environment state

`environment-serve --state <state.json>` serves an environment from the
initial state an environment-state view extracted. Read tools whose views
are state-backed answer from a live state store: item views render the held
entity into the recorded envelope, list views render the collection or the
recorded members, and document views return the held value; a missing entity
or one lacking a required field is refused, and requests the state cannot
answer fall back to their recordings with the reason. The /agentenv data
plane implements data/reset, data/add (a rea-state/v1 document as a data
part, base64 bytes, a file:// path or an https URL, validated and replaced
atomically) and data/get, and the card advertises those operations and an
intake declaration. Stage 1 is read-only: write tools are not served in
state mode. Without --state nothing changes.

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

* docs(environment): record verification after merging the state view's review fixes

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

* feat(environment)!: serve the environment state by default

environment-serve derives the environment state at startup by running the
environment-state view in-process on the capture with the manifest's
template rules, so an environment comes with its state and a user supplies
nothing extra. The derived Evidence is the same one inspect-analysis-view
produces by hand. --state is removed; --replay-only serves exactly the
stateless replay of main. When the state cannot be derived, or its catalog
does not fit the discovery budget, the server starts in replay mode, logs
the reason and reports it in the stop summary's `state`. Exported bundles
serve the state by default too: export derives it the same way, so the
verifier's tool count and checked answer match what is served, and the
README describes the served mode.

BREAKING CHANGE: environment-serve answers read tools from the derived
state and does not serve write tools unless --replay-only is given; the
stop summary reports `state` instead of `state_evidence_id`.

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

* fix(environment): bound data-plane replies, keep batch order, cancel intake on stop

- One /agentenv response is held to 64 MiB: replies are measured as they
  are produced and one past the budget becomes a -32000 error stating it.
- Every message of a batch enters the arrival-order queue on arrival, so
  another request's reset never lands between them.
- Stopping the server, or a caller leaving, cancels the active download
  and answers queued calls `cancelled` without running them.
- file:// URIs name only this host: another host or a network share is
  refused before anything is opened.
- A loaded view value may not give an entity another identity.
- List answers refuse a member lacking a field every recording carried,
  as item answers do.

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

* fix(environment): keep bodies too deep for the state's nesting out of the state

A recorded body at the JSON depth limit was accepted by extraction, then
nested a few levels deeper as a document or entity value, so sealing the
state Evidence threw and environment-serve and environment-export stopped
instead of falling back. stateBodyJson, the one rule extraction and
serving share, now treats a body nesting deeper than the limit minus 16
levels as unusable, so its tool stays a recorded view.

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

* fix(environment): measure data-plane replies before building them

83fcc739 bounded one /agentenv response to 64 MiB, but it still
serialized each reply whole before measuring it, so a batch of data/get
calls on a large state built every refused reply in full only to drop
it. Each reply is now measured in bounded JSON parts (the shared
jsonParts encoder), its parts kept only while it fits; a reply past the
budget is counted for its exact reply_bytes but never assembled. A test
checks that a refused read is never handed to JSON.stringify whole and
that its reply_bytes is exact.

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

* fix(environment): release batch replies as they are encoded; judge exact numbers by value

answerBatch kept every settled reply in its pending array until the
batch ended, so a batch alternating data/add and data/get held each
loaded state alive; each reply now leaves the queue as it is encoded.

The exact-number policy judged a number by its recorded spelling, so a
state loaded with 1e20 came back from data/get as
100000000000000000000, which data/add refused, breaking the promise
that data/get output loads back unchanged. A number is exact only when
both its recorded spelling and the one serialization writes are safe,
so integers from 2^53 below 1e21 are refused in any spelling; replay
keeps such bodies as recorded text. A domain test checks that every
accepted number is accepted again once written back.

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

* fix(environment): keep room for refusals and framing in the reply budget

The response budget counted accepted replies only: a budget error that
replaced a reply, and a batch's brackets and commas, were not charged,
so near 64 MiB later refusals pushed the body past the advertised
limit. Every reply still to come now keeps room for its own budget
error, and a batch keeps room for its framing, so a refusal always fits
and the body never passes the budget. Reply ids and which messages are
answered come from one place shared by the replies and the reserve.

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

* fix(environment): refuse a loaded state that data/get would write past the intake limit

data/add measured only the document it was given, while data/get
writes the state back with the members a document may omit (types, the
state Evidence id, view fields and record citations). A document just
under 32 MiB could therefore load and be reported larger than any intake
accepts, breaking the data/get then data/add round trip. replace now
measures the state as data/get writes it, part by part, and refuses it
past the limit, leaving the state unchanged. The limit moves to the
state document module, so the application store can read it; the
intake re-exports it.

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

* fix(environment): keep merged export checks on the shared test graph

Main's export tests installed module mocks for the whole services
project, which reuses one module graph. A tarball read failure then
missed its importer, and a one-shot heap mock could be taken by another
file. Read those through bindings the tests replace for one case.

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

* fix(environment): keep a state file whose close failed owned until it closes

A file:// data/add that could not close its descriptor dropped the handle,
and an admission failure whose cleanup close failed dropped its owner, so
nothing could retry the close or report it.

The read now runs through withOwnedRegularFile, and a failed close is
retained in a server-lifetime ArtifactResourceScope. The next file read
retries it first and answers -32000 while it is still open; inline and
https documents still load. Closing the served environment retries it again
and rejects with the unfinished close.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: gensokyo-dev <gensokyo-dev@users.noreply.github.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: morluto <morluto@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-22 05:04:57 +08:00
Diya 3dbe314ada fix(javascript): preserve source-map preparation capability errors (#6149) 2026-10-22 04:24:31 +08:00
xiaofeilovecodingandxiaofeilovecoding f5acf82a78 fix(native): retain decoded zero-address Swift class references (#6069)
Co-authored-by: xiaofeilovecoding <fxy850328059@gmail.com>
2026-10-22 04:18:25 +08:00
morluto a721c42c07 fix(cancellation): bind retained errors to cleanup owners
Carry the capture handle cleanup lease through retry results and bind composed export failures to it. Bind aborted binary retirement errors to the failed close so successful later cleanup reveals the original cancellation.

Validation: 46 focused tests passed; npm run check passed. Four regression assertions fail on the original source. Full suite and real-provider workflows were not run.
2026-10-22 04:17:14 +08:00
087813fb88 fix(cancellation): preserve caller abort reasons through late finalization (#6143)
* fix(cancellation): preserve caller abort reasons through late finalization

Caller aborts that arrived during final provider, cleanup, or protocol
handling were dropped or replaced by I/O, cleanup, or success results.
Each affected path now samples the caller signal after its owner has
settled and returns AnalysisCancelledError carrying the exact
signal.reason, keeping any completed partial Evidence or cleanup state.

- Web script export: keep the exact reason for an interrupted output
  write, and honor a cancellation fact recorded before a retried
  capture close succeeds.
- Native value tracing and Hopper regex search: check the signal after
  the final provider reply or regex worker settles.
- Apple asset catalog: stop assetutil with SIGTERM so cancellation
  settles only after the helper exits.
- CDP observation: end an armed window on a fatal protocol failure.
- ADB bugreport: prefer cancellation over the staging-removal output
  error when the caller aborted.
- JavaScript recovery: check the signal after provider success.
- export-web-scripts CLI: run under command cancellation and forward
  the signal into the export workflow.
- Objdump and Rizin CLI: report cancellation after cleanup completes.
- Binary session: keep cancellation primary when previous-client
  retirement fails after abort.

Fixes morluto/rea#6042
Fixes morluto/rea#6043
Fixes morluto/rea#6048
Fixes morluto/rea#6049
Fixes morluto/rea#6057
Fixes morluto/rea#6060
Fixes morluto/rea#6078
Fixes morluto/rea#6080
Fixes morluto/rea#6108
Fixes morluto/rea#6111
Fixes morluto/rea#6115

* fix(cancellation): keep caller reasons through export rollback and commit

- Rollback failure after a caller abort now returns AnalysisCancelledError
  with the exact signal.reason, attaching the rollback failure as cleanup
  and retaining any published result as partial observation.
- A capture close retry that succeeds after abort returns cancellation
  instead of the stale cleanup diagnostic.
- An abort that lands during the final output commit returns cancellation
  carrying the completed manifest as partial observation.
- Apple assetutil stop test: abort and await the run in finally so a
  timed-out wait does not leave the child running, and skip on Windows
  where the JavaScript SIGTERM handler cannot run.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 35b247e9-c793-4b8f-979a-807c27112cf5

* fix(cancellation): bind export cancellations to cleanup and caller reasons

- A rollback-failure cancellation is bound to the retained cleanup lease,
  so a later successful close clears cleanup_incomplete instead of
  reporting a residual directory that no longer exists.
- mapExportFailure wraps the caller's own AnalysisCancelledError reason
  under export_web_scripts rather than returning it as the export's error.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 35b247e9-c793-4b8f-979a-807c27112cf5

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
Copilot-Session: 35b247e9-c793-4b8f-979a-807c27112cf5
2026-10-22 04:15:16 +08:00
Alex GorevskiandCopilot 4e28166626 fix(cancellation): preserve caller abort reason in cancellation errors (#6144)
* fix(cancellation): preserve caller abort reason in cancellation errors

Several application, provider, and MCP boundaries constructed
AnalysisCancelledError, ArtifactReaderFailure, or
DetachedPgpSignatureError for an already-observed or newly-observed abort
without passing the caller's signal.reason as the error cause. The typed
result then reported cancellation with an undefined reason even though the
exact object supplied to AbortController.abort(reason) was still available.

Pass signal.reason as the cause at each producing cancellation site:
JavaScript artifact reconstruction and file abort checks, derived MCP
operation checkpoints, detached PGP verification, ASAR listing admission,
Dart AOT inspection and its provider guard, evidence-file analysis views,
binary layout and recorded crash admission, WASM inspection admission,
and the Kuna shared assembly-index wait.

Cancellation classification and cleanup behavior are unchanged.

Fixes morluto/rea#5935
Fixes morluto/rea#5944
Fixes morluto/rea#6100
Fixes morluto/rea#6101
Fixes morluto/rea#6110
Fixes morluto/rea#6113
Fixes morluto/rea#6121
Fixes morluto/rea#6131
Fixes morluto/rea#6132

* test(cancellation): assert the exact caller reason identity

Copied reasons satisfied toMatchObject, so the tests would pass even if the
caller's abort reason were replaced by a structurally equal copy. Capture the
rejection and assert the cause is the same object.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 35b247e9-c793-4b8f-979a-807c27112cf5

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 35b247e9-c793-4b8f-979a-807c27112cf5
2026-10-22 04:11:15 +08:00
JohnandClaude Opus 5.5 7ffae5a791 test(artifacts): cover fixed input-size recovery for Rust and IL2CPP (#6148)
#5633 gave Rust and IL2CPP input-size refusals input-allowance recovery
through fixedAllowanceOptions, but no public CLI check covered either
command, and IL2CPP had no remediation assertion at all. Add both
commands to the CLI input-limit matrix.

Go and WASM still carried a duplicated hand-written copy of the same
advice. They now use the shared "input" allowance recovery, so all four
commands name their fixed allowance from one owner.

The matrix also asserted not.toContain("evidence reply"), which the new
"evidence-reply" wording passes only because of the hyphen. Assert the
absence of the old advice text instead.

Fixes #5405

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 04:10:46 +08:00
KIEU TRONG KHANG 2aadcf7d04 fix(electron): report rejected CLI arguments with issue details (#5036)
* fix(electron): report rejected CLI arguments with issue details

Pass schema validation issues and submitted input to inputError in list-electron-targets and inspect-electron-page commands, matching the structured diagnostics returned by browser commands.

Fixes #3865

* test(electron): assert exit code 1 on rejected CLI arguments
2026-10-22 04:08:52 +08:00
amyanger b0a554cc3a fix(browser): read SAZ archives with zero-byte HTTP members (#5453)
Fiddler writes a zero-byte raw/NN_s.txt (or _c.txt) for a session that
never got that message. The decoder passed it to the HTTP parser, which
found no header/body separator and rejected the whole archive, so no
session could be read even with --record.

Report a zero-byte request or response member as null, like a missing
member, keep it in location.members, and add a per-record limitation
that says only that the stored member is empty. Non-empty malformed
members are still rejected.

Fixes #5411
2026-10-22 04:08:42 +08:00
lyly 414ad52b20 fix(objc): validate direct protocol enumeration input (#4405) 2026-10-22 04:08:31 +08:00
Peter 3cfdc585b4 fix(mcp): keep analyze_function progress strictly increasing (#3460) 2026-10-22 04:08:20 +08:00
The one -NiuandKaoru0822 4fe88a7e3f fix(browser): reserve the initial scenario step id from action inputs (#3493)
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-22 04:08:11 +08:00
jayinversandmaxazure f4cabe535f fix(wasm): pass glue filenames to the JavaScript parser (#4485)
associateWasmGlue selected the unnamed-source parser mode even though
the selected glue filename was already an adapter argument, so valid
JavaScript such as `n===0?H=>({start:H.startY}):H=>({start:H.startX})`
was read with the TypeScript grammar (conditional arrows as a return
type) and the whole glue file failed parsing, losing every .wasm
literal reference after it. Pass the filename so .js glue parses with
the JavaScript grammar, matching the general filename-aware parser
boundary from #3782. The unnamed-source fallback policy itself is
unchanged.

Adds a regression test asserting the conditional-arrow fixture parses
completely and retains the ./module.wasm reference (line 2, column 6,
local-path-candidate, static-literal) while the plain fetch control
stays working; reverting the fix fails the new test.

Fixes #4474

Co-authored-by: maxazure <maxazure@gmail.com>
2026-10-22 04:08:00 +08:00
keeponlightandkeeponlight 6bc4ec3a74 fix(electron): block moveToApplicationsFolder during active capture (#3471)
Co-authored-by: keeponlight <20103260+keeponlight@users.noreply.github.com>
2026-10-22 04:07:48 +08:00
The one -Niu 1dcaa77693 fix(browser): retain the null console argument in selected console text (#3479) 2026-10-22 04:07:37 +08:00
ShuaixinHuang 0da36910dc docs(artifacts): clarify unsupported DMG extraction (#5555)
* docs(artifacts): clarify unsupported DMG extraction

* fix(cli): keep the WASM command timeout optional

* fix(cli): preserve WASM timeout help metadata

* test(pe): reuse compiled CLI startup for resource acceptance

* fix(cli): use upstream exact-optional timeout semantics
2026-10-22 04:07:28 +08:00
Danish Rafie Ekaputra 55c3129224 docs(browser): reconcile HTTP discovery wording with browser sockets (#3484) 2026-10-22 04:07:18 +08:00
dbd556043c fix(providers): name JEB, IDA, and one-shot command deadlines (#6014)
* fix(providers): name JEB, IDA, and one-shot command deadlines

Wire JEB catalog discovery to REA_JEB_MCP_TIMEOUT_MS, name the IDA
registration timeoutMs, and add command_timeout_ms for one-shot objdump
and rizin. Debugger, Frida, and Cutter caps stay fixed and say so.

Refs #5910

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

* fix(providers): pass remaining deadlines and name the cap that fired

Catalog discovery now sends the JEB budget still remaining to the SDK on
connect and every tools/list page. Objdump and rizin preflight share one
command_timeout_ms across stat and digest; the command still receives that
budget again. Architecture Evidence keeps a selected timeout, and Frida and
Rizin name the capture limit that actually stopped output.

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

* fix(frida): preserve non-timeout device lookup failures

getDeviceById is a lookup deadline only when Frida rejects with
"Device not found" after the wait. A device-manager or backend failure
stays a capability error. Align the assertions that still expected the
previous capacity and capture sentences.

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

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: morluto <morluto@users.noreply.github.com>
2026-10-22 04:01:02 +08:00
Peter G b28f6c0953 fix(reference): retain inventories with empty module specifiers (#3497) 2026-10-22 03:57:38 +08:00
Jiwon | 지원 3220e17b8b fix(snapshot): count newly retained workflow bindings on import (#4428)
AnalysisSnapshotCache.stage counted new direct entries but never
incremented for new workflow entries, so a snapshot carrying only a
trace_feature binding reported 0 new entries while retaining it.

Count a workflow binding when its query ID is new, matching direct
entries; replacements and capacity-refused bindings stay uncounted.

Fixes #4377
2026-10-22 03:51:32 +08:00
Vinícius Andrade cc4ed5b904 fix(ida): keep omitted callee truncation markers unknown (#4286)
* fix(ida): keep omitted callee truncation markers unknown

Modern headless callees pages may omit more. The runner treated every
page that was not explicitly capped as complete coverage, so
trace_call_path could return not_reached when callee completeness was
unproven. Map omitted more to unknown coverage and keep the observed
callee rows. Explicit more: false remains complete.

Fixes #3988

Signed-off-by: Vinícius DS Andrade <95101505+viniciusdsandrade@users.noreply.github.com>

* test(ida): keep omitted callee truncation from becoming not_reached

Assert that an x86 callees page without more reports unknown
relationship coverage and that trace_call_path to an unlisted goal
stays unknown instead of not_reached.

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>
2026-10-22 03:51:22 +08:00
Vinícius Andrade 126b6e20d4 fix(ida): retain analysis profile in partial operation evidence (#4645)
* fix(ida): retain analysis profile in partial operation evidence

Pass the session analysis profile commitment to createEvidence when IdaSessionClient.#failure retains partial observations after a failed operation.

Refs #3992

Signed-off-by: Vinícius DS Andrade <95101505+viniciusdsandrade@users.noreply.github.com>

* test(ida): cover analysis profile retention in partial operation evidence

Verify that failed IDA operations retain a supplied AnalysisProfileCommitment on partialObservation and projected error details while leaving unprofiled sessions null.

Refs #3992

Signed-off-by: Vinícius DS Andrade <95101505+viniciusdsandrade@users.noreply.github.com>

* test(ida): align analysis profile provider with IDA adapter identity

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>
2026-10-22 03:51:12 +08:00
Wade 8fe8622e09 fix(evm): reject repeated byte-string keys in Solidity metadata (#4024)
cborg's rejectDuplicateMapKeys compares Map keys by identity, and each byte-string key decodes to its own Uint8Array, so a terminal metadata map with the same byte key twice was reported as decoded. Compare the root map's scalar keys by kind and value and report a repeat as not-decoded, like repeated text keys.
2026-10-22 03:51:02 +08:00
Vishal Tambi 1a945ce9ff fix(cli): reject an empty JavaScript application path (#5265) 2026-10-22 03:50:52 +08:00
Wade d8199ab6af fix(javascript): keep Vite dependency slots after a spread unknown (#4023)
The __vite__mapDeps table indexer mapped a spread element to one unknown slot, so every later entry kept a definite index even though the spread can add any number of elements. A call such as __vite__mapDeps([0, 2]) over ["./first.js", ...["./middle-a.js", "./middle-b.js"], "./last.js"] then resolved index 2 to last.js instead of middle-b.js. Keep only the entries before the first spread; indexes from the spread onward stay unknown.
2026-10-22 03:50:42 +08:00
ShuaixinHuang f6e4e0296f fix(native): remove completed signing capture limitation (#5548)
* fix(native): remove completed signing capture limitation

* fix(cli): keep the WASM command timeout optional

* fix(cli): preserve WASM timeout help metadata

* test(pe): reuse compiled CLI startup for resource acceptance

* fix(cli): use upstream exact-optional timeout semantics
2026-10-22 03:50:31 +08:00
4c20744f1c test(update): isolate CLI subprocess deadlines (#5019)
test(update): isolate CLI command deadlines

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 03:50:18 +08:00
Prasad Navale 3dce8326c9 docs(errors): include Omarchy and current host-support messages in error catalogue (#5078) (#5278) 2026-10-22 03:50:08 +08:00
Prasad Navale fd64b1c240 docs(cli): include Kuna in supported doctor providers (#4624) (#5011) 2026-10-22 03:49:57 +08:00
Prasad Navale d2015e94f3 docs(setup): clarify prebuild-install warning from optional dependencies (#4881) (#5012) 2026-10-22 03:18:41 +08:00
7fdf7a5b79 docs(go): clarify cumulative build-info search budget (#2731)
* fix(go): retain build info in later ELF data segments

* test(javascript): retain unknown shadowed parameter provenance

* fix(go): bound overlapping build-info searches

* test(release): select the runnable preflight job

* 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>

* docs(go): expose cumulative build-info scan budget

* chore: retain upstream MCP test formatting

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
Co-authored-by: Lucien <164857476+dakjdakd@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-22 03:18:31 +08:00
Iven Koo 7cdf8bc5cf test(native): assert failed-callee call identities and edge caps (#2888)
* fix(native): attempt failed value trace selectors once per invocation

* test(native): assert failed-callee call identities and edge caps
2026-10-22 03:18:21 +08:00
Vishal Tambi 465e1a1f36 test(sqlite): keep incomplete cleanup primary over late cancellation (#5365) 2026-10-22 03:18:10 +08:00
Christopher Pruijsen f3537db34f fix(setup): treat empty COPILOT_HOME as unset (#5047)
`copilotDirectory` used nullish coalescing, so `COPILOT_HOME=""` produced
the relative path `mcp-config.json`. Setup then planned and wrote the
GitHub Copilot CLI registration into the current working directory, not
`~/.copilot/mcp-config.json`, and doctor looked for it in the same wrong
place.

Use `||` like the sibling client homes (CODEX_HOME, KIMI_CODE_HOME,
GROK_HOME, XDG_CONFIG_HOME), and as #2686 did for DSH_HOME.
2026-10-22 03:18:01 +08:00
Dragoloneandgensokyo-dev 48e7470041 fix(evm): trim hex carrier whitespace in linear time (#5200)
The hex carrier trimmed outer ASCII whitespace with a global
start|end-anchored regex. For an invalid carrier with a long interior
whitespace run, the end branch was retried at every position of the run,
so rejection took quadratic time (about 18 s for a 200 KB run) and a few
hundred KB could exhaust the worker deadline instead of returning the
documented invalid-format error. Trim with index scans over the same
ASCII whitespace set.

Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
2026-10-22 03:17:50 +08:00
2947a77507 fix(engines): keep format ceilings fixed and recover the EVMole deadline (#6088)
* fix(engines): keep format ceilings fixed and recover the EVMole deadline

Promote the owned EVMole worker wall clock to timeout_ms and name it in
recovery. Fixed CPU, byte, and address-space refusals in this theme say
the adjustable deadline does not raise them.

Refs #5910

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

* fix(engines): recover the allowance that refused the request

Model capacity recovery follows a structured limit instead of message text,
and the shared header ceiling no longer names Safetensors. Firmware signal
errors keep captured tool output, oversized reports do not suggest an
intermittent rerun, and NativeAOT hydration budget exhaustion explains the
fixed parser allowance on the partial report.

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

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: morluto <morluto@users.noreply.github.com>
2026-10-22 03:06:59 +08:00
ae09e3fe70 fix(native): recover macOS command deadlines (#6059)
* fix(native): recover macOS command deadlines

Promote native_command_timeout_ms (default 60000), native_ui_deadline_ms
(default 180000), and pwntools_timeout_ms (default 30000) as validated
per-call settings. Keep cpuSeconds, byte allowances, LLDB grace, and plist
probes fixed, and say those caps have no runtime override.

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

* fix(native): report the deadline that actually expired

Pass the selected native command timeout through xcrun resolution, thread
the pwntools deadline into pwndbg failures, and state the selected UI
deadline apart from the fixed wait-step cap. SIGXFSZ and EFBIG recovery
names the file-size allowance.

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

* fix(native): honor the selected deadline on helper stages

The UI helper compile and capture subprocesses used fixed 60s and 30s
exec timeouts, so a longer native_ui_deadline_ms still failed as a generic
provider error. Preparation timeouts reported the 60000 ms default instead
of the selected native_command_timeout_ms.

Memory resource-constraint recovery names memory again, and the Darwin
plist CLI expectation matches the byte-counted input allowance.

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

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: morluto <morluto@users.noreply.github.com>
2026-10-22 02:59:27 +08:00
Ulises Millán 67a6477f01 fix(cli): label reconciliation input failures as application workflow errors (#5668)
Closes #5436
2026-10-22 02:53:27 +08:00
Oke Iyanuoluwa EnochandClaude Opus 5.5 3dde9a5e1c ci(windows): run remaining Windows-only path admission cases in the lane (#5807)
Three test files have cases that run only on Windows
(skipIf(process.platform !== "win32") / describe.runIf(win32)), but the
windows-curated job runs an explicit file list that did not name them,
so CI never executed those cases:

- src/contracts/sessionPathInputs.test.ts (Windows absolute path forms)
- tests/boundary/filesystem/directAnalysisSnapshotReplay.test.ts
  (snapshot replay through a junction and before native admission)
- tests/boundary/mcp/rawBinaryMcp.test.ts (saved reads across a
  junction and a noncanonical Windows path)

Add them to the lane and the package change scope, and guard the
entries in curatedWindowsLane.test.ts.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 02:52:11 +08:00
Shakti 07fd426e9a fix(apple): preserve asset catalog caller cancellation reason (#6055)
* fix(apple): preserve asset catalog caller cancellation reason

* test(apple): cover asset catalog cancellation checkpoints
2026-10-22 02:51:11 +08:00
gensokyo-dev 0583fc69be perf(javascript): query selected source-to-bundle facts (#6128)
Select completed source-to-bundle mappings and candidates through the shared view owner, preserve reference-file provenance, and retain full Evidence. Reuse bounded JSON reading, heap admission, and cancellation before session writes.

Verified public Redux Toolkit source/map facts with an independent oracle, equivalent CLI/MCP workflows, focused negative controls, and complete CI/review. Historical paired SDK context estimates fall about 64%; timing/resource tradeoffs and final verification receipts are recorded without a general speedup or actual model-token claim.

Fixes #6112.
2026-10-21 18:45:50 +00:00
a0214e0e0e fix(javascript): name heap and semantic ceilings (#6022)
* fix(javascript): name heap and semantic ceilings

Refs #5910. Heap exhaustion names max_heap_mb with its effective value,
range, and next-analysis worker restart. Semantic ceiling coverage names
the four max_semantic_* arguments. Wakaru and transfer ceilings say this
release has no runtime override.

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

* fix(javascript): distinguish adjustable ceilings from fixed limits

The effective transfer file ceiling and the parent result budget follow
the CLI/MCP process heap, so recovery names NODE_OPTIONS instead of
denying an override. A provenance report over 8 MiB keeps the fixed
allowance guidance, and the Wakaru deadline says an unchanged retry may
hit it again.

Refs #5910

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

* fix(javascript): keep Bun OOM and checkpoint heap controls distinct

Bun heap-exhaustion exits no longer claim the ignored max_heap_mb V8
old-space limit. Checkpoint decode names the parent heap and Node
NODE_OPTIONS, the same control as the parent result budget.

Refs #5910

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

* fix(javascript): name semantic ceilings without claiming both were reached

Truncated semantic retention names the total and per-file controls as
effective ceilings. Callers only know that retention was truncated, so
the limitation no longer says both ceilings were reached.

Refs #5910

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

* fix(javascript): name only the semantic ceiling that stopped retention

Semantic ceiling limitations claimed both the application total and the
per-file control whenever either one truncated the graph. Record which
offered budget stopped node and unknown retention, and name only those
controls. Coverage limits still list both effective values.

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

* fix(javascript): require a value in the NODE_OPTIONS heap example

A bare --max-old-space-size is an illegal V8 flag and prevents Node from
starting. Parent-heap and transfer-ceiling recovery now share
--max-old-space-size=<MiB>, and the Kuna heap remediation uses the same
complete form.

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

* fix(javascript): limit the old-space placeholder to NODE_OPTIONS recovery

The review covers the CLI/MCP NODE_OPTIONS example and the transfer-ceiling
copy. The Kuna heap-headroom sentence stays unchanged.

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

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: morluto <morluto@users.noreply.github.com>
2026-10-22 02:44:14 +08:00
01788bac86 fix(android): name the JADX deadline and recover apktool decode (#6024)
* fix(android): name the JADX deadline and recover apktool decode

JADX operation timeouts now name REA_JADX_OPERATION_TIMEOUT_MS, the
effective milliseconds, the 1..2147483647 range, and that a change applies
on the next process start. Apktool decode accepts decode_timeout_ms,
omitted means 600000, and a timeout names that key. ADB deadlines and
apktool and Java version probes stay fixed and say there is no runtime
override.

Refs #5910

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

* fix(android): derive capture recovery from the fixed allowance

Main now stores a fixed allowance instead of a prebuilt remediation
string. Apktool and adb capture refusals keep the no-runtime-override
sentence in that allowance name, including that decode_timeout_ms does
not raise the apktool output cap.

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

* test(apktool): observe a real decode deadline and process cleanup

A hanging interpreter launcher and its pipe-holding child stay alive
until decode_timeout_ms elapses. The provider then reports
provider_timeout with completed cleanup, and both processes are gone.

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

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: morluto <morluto@users.noreply.github.com>
2026-10-22 00:31:43 +08:00
gensokyo-dev aa51e43439 perf(mcp)!: default to compact input schema advertisements (#5975)
Use compact MCP input advertisements by default while preserving explicit full, canonical validation, output contracts and complete Evidence. Require an explicit selected catalog for runtime identity and honor both profiles in CLI status.

Real ripgrep/Zellij SDK tasks, independent ELF oracle, actual client trials, profile parity workflows and CI are recorded in experiments/analysis-efficiency/mcp-input-catalog.md. SDK token estimates improve; actual Codex token savings and general latency/memory gains are not established.

Refs #5861

BREAKING CHANGE: MCP input advertisements default to compact. Set REA_MCP_INPUT_SCHEMA_PROFILE=full for complete advertised guidance and nested constraints.
2026-10-22 00:17:11 +08:00
gensokyo-dev 01ee6551b4 fix(ci): use one heap snapshot for JavaScript admission (#6081)
Read the V8 limit and used heap together for initial admission, checkpoint
decode and remaining result capacity. Remove the duplicated second heap/RSS
observation while preserving every reserve and ceiling.

Make the controlled parent-capacity fixture internally consistent and test
both unchanged usage and a later one-MiB collection. Real worker/transfer
coverage still retains static/module facts and declines only the large graph.

Assisted-by: Reimu (gensokyo-skills:reimu-incident-triage)
Assisted-by: Kogasa (gensokyo-skills:kogasa-surprise-testing)
2026-10-22 00:15:25 +08:00
d8a9673fc4 fix(process): name the Swift compile deadline (#6071)
Name REA_SWIFTC_COMPILE_TIMEOUT_MS, its effective milliseconds, the
1..2147483647 range, and the omitted 120000 ms default when a Darwin
ownership-helper compile times out. A change applies after REA restarts,
on the next ownership-helper compile.

Producer SIGXCPU and SIGXFSZ recovery says REA retains the tighter
inherited limit. Agent-visible fixed caps in this theme say this release
has no runtime override. Numbers are unchanged.

Refs #5910

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: morluto <morluto@users.noreply.github.com>
2026-10-21 23:46:29 +08:00
gensokyo-dev f86206535f fix(ci): validate artifact MCP replies against named schemas (#6021)
Bind artifact lifecycle and analysis calls to their advertised named SDK
output schemas, sharing discovery per connection and rebinding on reconnect.
Reject schema overrides and ambiguous catalogs, preserve request options,
and retain primary failures while attempting all owned cleanup.

Add real-stdio boundary regressions for malformed output, discovery reuse,
reconnection, schema ambiguity, override rejection and timeout preservation.

Assisted-by: Reimu (gensokyo-skills:reimu-incident-triage)
Assisted-by: Kogasa (gensokyo-skills:kogasa-surprise-testing)
2026-10-21 23:28:12 +08:00
Kaoru0822andClaude Opus 5.5 8e3bf56e11 fix(electron): record actions skipped after a failed action (#6023)
An Electron scenario stopped at the first failed or cancelled action and
returned no record for the remaining declared actions, so
normalized_result.actions could not account for the whole scenario.

Record every remaining action as cancelled with a not-executed error, the
representation browser scenarios already use. Both runners now share one
helper for that error, which also distinguishes a stop caused by request
cancellation from one caused by an action failure.

Fixes #5943

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 08:06:25 -07:00
Kaoru0822andClaude Opus 5.5 dc53c11f6d fix(process): keep structural capture issues alongside semantic ones (#6020)
parseProcessCapture re-threw only the custom (semantic) schema issues as a
TypeError whenever any existed, discarding structural issues found in the
same parse. Throw the schema's ZodError instead so every issue is retained
and describeValidationFailure reports the first issue with the remaining
count. Reconstruction obligation candidates now use that same formatter
instead of the raw error message.

Fixes #5957

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 08:05:30 -07:00
Kaoru0822andClaude Opus 5.5 d4c637f4cf fix(browser)!: keep malformed console argument inventories unknown (#6016)
* fix(browser)!: keep malformed console argument inventories unknown

An omitted, null, or non-array Runtime.consoleAPICalled args inventory now
reports argument_types as null, text capture as unavailable, and
console_events as invalid_protocol_value instead of a complete empty
inventory. Malformed argument elements keep their CDP position as
"unknown", so retained text values keep their original argument_index.

Call-site attribution now uses the frame at position zero: a malformed top
stack frame is no longer replaced by its caller. Console events are
excluded and request initiator locations are null, both reported as
invalid_protocol_value.

BREAKING CHANGE: console event argument_types may now be null, and
console text_capture.status may now be "unavailable", when the CDP
producer omits or malforms the argument inventory.

Fixes #5934

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

* fix(browser): report malformed WebMCP registration stacks as incomplete

topCallFrame now owns the omitted-versus-malformed decision for the whole
stack value: an omitted stack has no call site, while a non-object stack,
non-array callFrames, or a malformed top frame is malformed. WebMCP
discovery keeps the tool with a null registration_source but records
webmcp_tools invalid_protocol_value, so capture comparisons stay unknown
instead of confirming a changed source. Console and initiator stacks use
the same rule.

Refs #5934

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 08:01:45 -07:00
Kaoru0822andClaude Opus 5.5 4dfa1c67dc fix(capture): keep excluded MIME protobuf applicability partial (#6012)
* fix(capture): keep excluded MIME protobuf applicability partial

Automatic protobuf framing treated an explicitly excluded, conflicting or
unavailable Content-Type the same as a known-absent one, so a selected body
was skipped and coverage still upgraded to complete. Body metadata now
reports whether MIME applicability is established; an unresolved non-empty
body adds a record limitation and keeps protobuf_coverage partial. HAR uses
its required mimeType, falling back only to a visible header value.

Fixes #5926

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

* fix(capture): compare repeated Content-Type declarations by essence

Repeated Content-Type headers that differ only by case, whitespace or
parameters declare the same media type, yet the raw-string comparison
collapsed them to an unknown value and the new applicability check
reported partial coverage. Body metadata now compares media-type
essences and owns automatic framing selection, so the consumer no longer
re-parses the raw value; framing lookup ignores inherited properties.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 07:58:31 -07:00
Kaoru0822andClaude Opus 5.5 1f92c77398 fix(adb): report an unparsed push checksum as unverified (#6010)
fix(adb): derive push digest source from the parsed device digest

A zero-exit sha256sum whose output held no recognizable digest produced
device_sha256: null with device_digest_source: "device_sha256sum" and
dropped the not-device-side-verified limitation. The push receipt now
takes one tagged device digest, so the source can only name sha256sum
when a digest was parsed; unrecognized output reports "unavailable",
the unverified limitation, and the raw command output. The sha256sum
parser moves to AdbShellOutput and accepts GNU's escaped-name prefix.

Fixes #5924

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 07:56:38 -07:00
Kaoru0822andClaude Opus 5.5 2319166c2e fix(reconstruction): keep complete contradictions beside incomplete entries (#6009)
The obligation ledger flattened every contradiction entry for an obligation
into one Evidence list, so one entry naming absent Evidence suppressed a
sibling entry whose Evidence was all present. Group whole manifest entries
and evaluate each one: any complete entry still reports `contradiction`,
while absent references from any entry stay visible as
`contradiction-evidence-missing`.

Share the ready packaged-process request fixture with the CLI boundary test,
which now covers both entry orders through the public command.

Fixes #5925

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 07:54:37 -07:00
Kaoru0822andClaude Opus 5.5 63b43b69bb fix(javascript): refuse Webpack loaders that can fall off the end (#6007)
* fix(javascript): refuse Webpack loaders that can fall off the end

The bootstrap integrity proof accepted any loader whose explicit returns
all yielded the module's exports. A loader with no returns, or with only
the cache-hit return, passed vacuously even though its miss path returns
undefined, so a bundled `n(45840).BrowserWindow` became an exact Electron
window.

Require that the loader body cannot complete normally before the proof
checks its returns, so the bundled-call identity stays unresolved while
the literal require("electron") reference is preserved.

Fixes #5958

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

* test(javascript): cover finally completion in Webpack loader proofs

Group the loader completion-path cases in their own suite and add
focused finally cases: a completing finally keeps a falling-through
catch unproven, a finally returning another value stays unresolved, and
a finally returning the exports proves the loader.

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

* test(javascript): keep IPC checks for unresolved Webpack loader cases

Share the unresolved-identity assertion so the completion-path cases
also reject directly attributed IPC observations.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 07:53:42 -07:00
Kaoru0822andClaude Opus 5.5 c8d3f8f8ec fix(firmware): keep a missing producer report distinct after exit 0 (#6008)
* fix(firmware): keep a missing producer report distinct after exit 0

A firmware engine that exits 0 without writing its requested report now
fails with the observed report state (missing, empty, invalid_json,
oversized, not_regular_file), the command, arguments, exit status and
captured output, plus report-specific recovery instead of generic
`rea doctor` advice. REA still does not retry or substitute an empty
result.

Fixes #5876

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

* fix(firmware): keep missing-report recovery from replaying removed paths

The recorded command arguments name REA's private workspace, which is
removed after the attempt. Recovery now presents them as provenance and
tells callers to replace every private path when reproducing the run.
Cover the oversized and not_regular_file report states.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 07:52:40 -07:00
Kaoru0822andClaude Opus 5.5 4423b6fff2 fix(process): reject finite trace variants that only reorder unordered members (#6006)
Duplicate-variant validation keyed each finite trace by its JSON form, which
preserves the member order of unordered tokens that matching treats as a
multiset. Key traces by the observations they accept instead: sort unordered
members and expand a token that repeats one event into that event's sequence,
keeping token positions and multiplicities significant.

Fixes #5916

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 07:52:06 -07:00
Kaoru0822andClaude Opus 5.5 5df59ee1fd fix(errors): name fixed plist decode allowances instead of host memory in recovery (#5860)
fix(errors): derive fixed-allowance recovery from a typed refusal field

inspect_plist refused a binary plist above its fixed 64 MiB decode
estimate, or nested deeper than 128 levels, with resource "memory" and no
recovery, so the shared presentation told the caller to add worker or
host memory. Neither raises that allowance. The 64 MiB source-size refusal
likewise got the worker file-size advice for evidence replies.

AnalysisResourceConstraintError now carries a typed fixedAllowance
{ name, kind }. fixedAllowanceOptions sets it instead of a prebuilt
string, and the presentation derives the recovery from it, so every
existing fixed-allowance producer moves to the same field. Operation
re-attribution, IDA startup re-attribution and explicit redaction copy the
field through one resourceConstraintRecovery helper; redaction still
applies to the allowance name.

The plist decode, nesting and input refusals use the field. A nesting
refusal now reports maximum_nesting_depth instead of the decode byte
limit it did not exceed.

Fixes #5847

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 07:51:46 -07:00
Kaoru0822andClaude Opus 5.5 6d356b8434 test(ci): keep module mocks in isolated test projects (#5886)
* test(ci): keep module mocks in isolated test projects

A module mock registered after another file in the same worker loaded the
module never applies, so a mocking test in a project that shares one module
graph passes or fails with file scheduling. That is how the environment
export source tests failed on hosted runs until c324d31b4 removed their
mocks.

Guard the rule: every tracked test using vi.mock or vi.doMock must belong
to an isolated project. Move the last shared-graph case,
DirectAnalysisStatFailure, which mocks node:fs/promises, into process-global
beside the other module-mocking workflow tests.

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

* test(pe): share one compiled CLI process across PE resource inspections

The acceptance test started a fresh CLI process for each of its fixtures
inside one 30 s test deadline. Hosted runs took 24-27 s and c324d31b4 timed
out. Every inspection there reads no stdin and sets no deadline, so use the
fixture's file-scoped compiled CLI process. Local time drops from 8.5 s to
about 3.9 s with the same assertions.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 05:51:24 -07:00
gensokyo-dev c538a843cb fix(ci): keep Windows ACL observation in SID form (#5941)
Observe private-runtime ACL ownership, trustees and rule count from one SID
rule collection. Guard the account-name presentation member through the owning
PowerShell type and verify that guard is installed. Keep the original15s probe
deadline and every ACL/readonly assertion.

Record bounded phase diagnostics and document the observed failure boundary.
The repeated historical timeout cause is unconfirmed; no retry or deadline
increase is presented as a fix.

Validation: complete current-head CI passed40 jobs with one source-only skip;
real Windows typecheck, curated/native controls, package and Node24/22 checks
passed. PowerShell7.5.2 API/parser experiment and278 routing regressions passed.
Full evidence: https://github.com/morluto/rea/actions/runs/39599008194

Assisted-by: Reimu (gensokyo-skills:reimu-incident-triage)
2026-10-21 20:43:56 +08:00
Kaoru0822andClaude Opus 5.5 25e1a24a3e fix(reconstruction)!: summarize obligation ledgers by cited Evidence authority (#5875)
summary.by_evidence_authority bucketed each obligation's source_state, so a
shipped-artifact and a controlled-replay ledger returned the same summary and
the promised authority breakdown was unanswerable.

Derive the buckets from authority_references[].authority using the Evidence
authority vocabulary owned by the Evidence schema. An obligation counts once
under each distinct authority it cites, so counts can exceed total. Keep the
source-state histogram under its own name, by_source_state. The readiness
example now derives its summary from the same owner function instead of
hand-writing buckets.

BREAKING CHANGE: summary.by_evidence_authority keys are now Evidence
authorities (shipped-artifact, controlled-replay, historical-reference,
external-service, analyst-inference) instead of source states. The previous
state counts move to the new required summary.by_source_state. Ledger closure
digests change accordingly.

Fixes #5838

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 05:12:55 -07:00
Kaoru0822andClaude Opus 5.5 705058f3e2 fix(javascript): count one IPC observation per site-resolved channel (#5810)
* fix(javascript): count one IPC observation per site-resolved channel

A send with a local constant channel was recorded twice: once unresolved
by the syntax pass and once resolved by wrapper substitution, because
duplicates were keyed on the channel. A channel the call site determines
now resolves that observation in place. A channel supplied through a
wrapper argument stays a candidate beside the unresolved observation.
Invocation identity now follows the call that supplies the wrapper's
literal arguments, so one wrapper call reached through several entries
is recorded once.

Fixes #5710

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

* fix(javascript): keep wrapper invocations distinct while removing repeats

Taking the nearest literal call as invocation identity collapsed separate
outer calls that pass different handlers through one wrapper call. Entry
identity is restored, and a finding is a repeat only when an identical
finding was reached through the same trailing local calls. A registration
replaces its unresolved source observation only when the site also
determines its handler, so a parameter handler keeps the generic source
registration beside per-invocation candidates.

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

* fix(javascript): compare site-determined IPC handlers by value

In-place resolution compared handler representations, so a constant
channel with a named local handler (recorded as an identifier by the
syntax pass and as its function by substitution) stayed counted twice.
The handler is now site-determined when it evaluates to the same function
in the site's lexical environment. A replaced observation also removes
the syntax pass's unknown-channel contribution, so a fully resolved file
is no longer reported as partial.

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

* fix(javascript): keep closures over invocation values as distinct IPC handlers

A handler node created in different wrapper activations may capture
different values, so the same function node alone does not prove the call
site determines the registration.

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

* test(javascript): guard optional call paths in closure handler test

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

* fix(javascript): decide IPC handler identity from activation captures

A closure is one site-determined handler only when it reads no binding of a
function this trace activated, such as a wrapper parameter or factory local.
Unused bindings in the activation no longer split a known listener, and
closures over outer-call values stay distinct when a shorter entry traced
the same wrapper call.

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

* fix(javascript): treat implicit captures and bundle modules precisely in IPC handler identity

An arrow handler reading its enclosing function's arguments or this captures
that activation. A bundle module factory runs once per module load, so its
bindings are shared by every trace rather than per-invocation captures.

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

* fix(javascript): attribute an arrow handler's inherited context to its owner

An arrow inherits this and arguments from the nearest non-arrow function, so
an activated arrow inside that owner does not make the handler depend on
the traced call.

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

* fix(javascript): skip suffix scans for per-invocation IPC handlers

Check activation capture before scanning recorded call chains, keep the
chains of capture-dependent handlers out of the index, append chains in
place, and treat an arrow's inherited super and new.target as captures.

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

* fix(javascript): supersede nested-entry IPC records and share module-init factories

A shorter entry traced alone no longer swallows the outer calls that invoke
the same wrapper sequence: the first fuller path takes over its record and
separate outer calls stay distinct invocations. A factory activation called
at module scope runs once per load, so its closure is shared unless the
trace itself makes that call. The resolver now owns bundle module-factory
recognition instead of an environment marker.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 05:06:50 -07:00
Kaoru0822andClaude Opus 5.5 35d6d5bb0a fix(artifacts): admit ZIP archives after unclaimed leading bytes (#5803)
* fix(artifacts): admit ZIP archives after unclaimed leading bytes

ZIP recognition required the archive at byte 0, so a ZIP behind a
script or self-extractor stub was refused as an unsupported binary
format although standard readers accept it. Target resolution and root
inventory now locate a trailing single-disk ZIP from its end record,
central directory and first local header, whether member offsets include
the leading bytes or not. Leading bytes any executable header family
claims keep their executable route.

Fixes #5732

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

* fix(artifacts): share the root ZIP family across recognition paths

The trailing-ZIP fallback named a stub-prefixed root `.app` archive
`zip`, while root inventory and the leading-signature route name it
`app-pack`. The root mapping now lives once in `zipRootPackageFormat`
and both target resolution routes and root inventory use it.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 05:06:28 -07:00
Kaoru0822andClaude Opus 5.5 5b014a739e fix(adb): report unrecognized package rows from resolve-adb-packages (#5874)
resolve_adb_packages marked coverage partial when pm list packages printed
unrecognized rows, but dropped the rows themselves and could still emit a
limitation describing an unparsed_lines field the result never carried.

ADB parsers now own unrecognized-line facts as a bounded sample (first ten)
plus the exact total, and a single provider projection derives
unparsed_lines, coverage, and the truncation limitation from those facts for
every parsed listing and the package search. The resolver result schema now
carries unparsed_lines, matching list_adb_packages through CLI and MCP.

Fixes #5843

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 05:06:11 -07:00
Kaoru0822andClaude Opus 5.5 ce1f4b1bba fix(artifacts): report unknown inventory traversal in inspection coverage (#5867)
fix(artifacts): keep unknown inventory traversal out of complete inspection coverage

Artifact inspection derived its coverage status only from always-zero
omission counts, so an inventory substep with unknown container-entry
traversal (for example, an omitted FIFO) yielded
complete-within-substeps. Analysis-view summaries also exposed the
inventory's typed source_coverage only for PCK and PyInstaller roots.

Derive the inspection status from the inventory substep's coverage
(unknown traversal is partial), and carry the producer's source_coverage
plus the traversal-unknown caveat in every artifact summary. Hash-count
fields keep their existing format gating.

Fixes #5837

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 04:59:09 -07:00
Kaoru0822andClaude Opus 5.5 d124809950 fix(android): report a rejected relative JADX JAR instead of an unset one (#5862)
A supplied non-absolute REA_JADX_MCP_JAR was folded into the unset case,
emitting not_configured with jar_configured: false and dropping the
caller-selected value. The JAR parser now keeps the two outcomes distinct:
unset stays not_configured, while a supplied non-absolute value (including
an empty string) fails with open_options_invalid, jar_configured: true, the
exact rejected jar_path and the absolute-path requirement. CLI, MCP and
doctor derive their output from that one boundary result.

Fixes #5844

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 04:57:55 -07:00
Kaoru0822andClaude Opus 5.5 7f9a492a8d fix(signals): report unknown decoder help IDs as invalid input (#5858)
inspect_signal_decoders turned sigrok's explicit missing-decoder refusal
into execution_failure with rea doctor advice, while decode_signal_recording
already reported the same refusal as invalid_request on decoders.

The sigrok selector classifier now owns the unknown-ID match for both
selections: help names the decoder field and points to catalog discovery,
decoding keeps decoders/annotations. The provider passes each producer
command's caller selection to that one owner instead of per-call closures.
Installed decoders that fail to load and refusals naming other IDs remain
provider failures. The real-sigrok verifier now asserts the typed refusal
for both operations.

Fixes #5849

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 04:57:28 -07:00
Kaoru0822andClaude Opus 5.5 c2bee63e99 fix(evidence): clear staged write cleanup state after a confirmed retry (#5852)
The atomic JSON writer reported a failed staging-directory release as
cleanup state on the returned EvidenceFileError or AnalysisCancelledError,
but never bound that error to the retained staging lease. A later scope
retry that removed the stage could not clear the error, so
`rea evidence-export` (and other writers sharing the scope, such as
--evidence-output delivery) kept reporting cleanup_incomplete and a
retained resource after cleanup had completed.

Bind each staged-output cleanup failure, and each admission retry
failure, to its exact leases so confirmed release restores the primary
outcome. Export recovery now reads that bound state instead of a local
close flag. A retry that still fails keeps the error incomplete with the
same retained stage.

Fixes #5842

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 04:57:08 -07:00
gensokyo-dev 905cbaf239 perf(ci): use tsrs and reduce PE acceptance startup (#5795)
Use pinned tsrs on supported development hosts while retaining stable official
TypeScript on Windows and unsupported platforms. Partition compile/typecheck
caches by actual compiler and qualify fallback hosts for compiler inputs and
lifecycle hooks.

Reduce PE acceptance startup through the existing bounded compiled CLI. Retain
upstream named MCP definitions, every fixture/assertion and the original 30s
deadline. Verify that browser transport loss preserves the observed target.

Validation: full qualification and real host lanes in
https://github.com/morluto/rea/actions/runs/39590450889 ; 450 focused regressions
passed with two documented conditional skips. All 1724 production emit files
match SHA-256 and size across both compilers. Current-head Codex/Greptile review
found no actionable issues. Measurements and limitations are in the PR.

Assisted-by: Reimu (gensokyo-skills:reimu-incident-triage)
2026-10-21 19:47:52 +08:00
gensokyo-dev 33f912919d perf(apple): reuse retained Interface Builder graph validation (#5885)
Reuse successful owning-schema validation only for exact immutable Evidence snapshots, preserve current parent binding and complete source facts, and collect only requested document pages.

Verify real XLD CLI/MCP workflows and archived connector UIDs, including complete pre-change baselines for both admission sizes and a wrong-baseline negative control.

Fixes #5855.
2026-10-21 19:44:05 +08:00
gensokyo-dev aa53c06538 feat(firmware): make analysis deadlines recoverable per call
Expose per-call firmware analysis budgets with exact recovery guidance, shared
CLI/MCP validation and retained actual settings. Keep publication and fixed
resource policy independent; preserve typed interruption and one captured-output
owner through command and one-shot cleanup.

Fixes #5851. Executed verification, real-provider limits, known baseline issues
and authorized readiness override are recorded in #5863.

Assisted-by: Kogasa (gensokyo-skills:kogasa-surprise-testing)
2026-10-21 19:25:22 +08:00
Kaoru0822andClaude Opus 5.5 bcfcd8b6e2 fix(sqlite): attribute generated-column evaluation failures to the selection (#5879)
* fix(sqlite): attribute generated-column evaluation failures to the selection

A selected virtual generated column whose expression needs a function the
read-only connection lacks (or raises an SQL error for a sampled row) was
classified by the worker's catch-all as a malformed database and projected as
an invalid_format issue on `path`.

Row reads now attribute SQLITE_ERROR from selected cells by probing each
selected virtual generated column over the sampled rows, and report a
typed capability_unavailable refusal naming the table, each failing column
with SQLite's message, and the remaining columns to select. Other causes,
including corruption, TOOBIG and NOMEM, keep their classification.

Fixes #5719

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

* fix(sqlite): recommend only probed-readable columns after evaluation failures

Record every per-column probe failure with its own reason, discover stored
columns outside an inventory-scope selection without decoding unrelated
malformed names, and present recovery as the structured columns selection
instead of shell-quoted command text built from database-supplied names.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 04:08:49 -07:00
gensokyo-dev efab044f30 feat(gpu): inspect static CUDA and HIP code objects (#5813) 2026-10-21 19:06:44 +08:00
647717c050 perf(javascript): reuse the source-analysis worker within a process (#5819)
* perf(javascript): reuse the source-analysis worker within a process

Each analyze-javascript-application call started a new Node worker.
That startup, not fixture IO or the shared CLI session, dominated the
slow loader-review and factory-completion files.

Park one healthy worker on the artifact resource scope and on the CLI
process. Stop it when the scope, session, or one-shot process ends.
Do not reuse after failure, cancellation, or a different heap or timeout.

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

* fix(cli): close the JavaScript worker when dispatch throws

Minimal dispatcher runtimes now export a no-op closeCliJavaScriptResources,
and CLI dispatch plus output rendering always await that cleanup. A dispatch
failure and a cleanup failure are both reported.

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

* fix(artifacts): report close-hook failures with retained cleanup

When retained cleanup and the scope close hook both fail, the close error
keeps both causes and both resource labels. The pending owners and the hook
stay registered so the next close retries each path.

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

* fix(javascript): stop a replaced analysis worker before the next one starts

A heap or timeout change now closes the parked worker before constructing
its replacement. The scope retains that process only when the close fails.

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

* style: format worker-reuse close and launch helpers

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

* fix(cli): report a failed JavaScript worker shutdown before exit

Share one in-flight close and drop the CLI scope only after it succeeds,
so a later caller retries a worker that is still owned. The one-shot
exit callback writes that failure and exits non-zero, while a closed
stdout pipe still exits 141.

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

* fix(javascript): keep a worker parked during an overlapping limit change

Claim the scope slot only when it is still empty after the replaced
worker closes. A concurrent analysis that parked another healthy worker
keeps that process, and the extra worker is closed through its own port.

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

* fix(artifacts): run later close hooks when an earlier hook fails

Run every registered close hook on each close. One failure is returned
unchanged, and several failures are aggregated so a later hook is not
skipped.

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

* fix(javascript): fail a limit change when the previous worker stays alive

A failed close used to be retained and then ignored, so MCP could return
a successful analysis while the old process and its replacement were
both owned. The request now fails before the replacement is constructed,
and the still-live worker stays owned for a later close.

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

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: morluto <morluto@users.noreply.github.com>
2026-10-21 19:01:20 +08:00
morluto c76edbebbe test(pe): bind acceptance calls to advertised MCP definitions
Pass the exact named tool descriptors to SDK callTool for PE resource inspection and session inventory. Avoid full-catalog validator compilation while retaining SDK output validation, every CLI/MCP scenario, cleanup, and the unchanged 30-second deadline.

The complete focused test passed locally in 7.1 seconds versus 11.0 seconds before this change; host load and CPU/memory costs were not controlled or measured. check:ci passes.
2026-10-21 18:58:50 +08:00
morluto c324d31b45 test(environment): verify export failures through the real CLI
Remove shared-module mocks for input reads, heap statistics, and tarball streaming. Exercise actual file changes and real V8 heap admission in fresh CLI processes; keep only selected-descriptor syscall fault injection for the pathless EIO case.

Reproduced the four hosted source failures with a preloaded module graph and found the same flaw in the adjacent tarball test. The preloaded control now passes 17 cases; 32 focused tests, all 1,739 service tests (2 skips), and check:ci pass. Snapshot, cleanup, identity, and error projection coverage is retained.
2026-10-21 18:26:58 +08:00
Cursor Agentandmorluto 5eebe2fdf8 test(cli): treat omitted exactOptional inputs as optional
The advertised-schema check parsed undefined on each field.
exactOptional rejects that value and still accepts a missing key, so
inspect-wasm-artifact looked required for commandTimeoutMs.

Judge omission with an object parse that leaves the key out. The
advertised required list already matched that parse for every other
primary command.

Co-authored-by: morluto <morluto@users.noreply.github.com>
2026-10-21 18:14:39 +08:00
Edgar ArakelyanandClaude Opus 5.5 ea3534759f feat(analysis-view): share templated positions across manifest tools (#5664)
* docs(specs): plan a template rule that shares templated positions

Record the outcome, requirements, non-goals, plan and tasks for
prefix-variance, an opt-in environment-manifest rule that templates a
literal segment with the parameter another tool already uses at that
position, so a session's writes to one entity share the read's template.

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

* feat(analysis-view): share templated positions across manifest tools

Add the opt-in prefix-variance template rule. It runs after the other
opt-in rules and templates a literal at a position that another tool,
of any method or segment count, templates by one of those rules after
the same preceding parts, when the literal is one of that parameter's
observed values or carries a digit and is spelled like one. The
parameter takes the shared parameter's name, tools that now match merge,
and a merged position keeps a tool's own rule when one templated it.

A write to the one entity a session edited, such as
PATCH /api/v1/collections/0RZ576QZQA5KG, becomes
patch_collections_by_collection_id on the read's template instead of a
literal single-entity tool.

Tool naming moves to its own module, and the three comparing passes
share the 32-segment split. Manifests that do not list the rule are
unchanged.

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

* refactor(analysis-view): drop the unused ManifestRule alias

Parameters now type their rule as a format rule, a shared opt-in rule
or prefix-variance, so nothing reads the union alias.

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

* docs(specs): record the prefix-variance checks and corpus results

Record the gates, and the 50 showcase captures with and without the
rule: identical output without it, 25 write and 34 read tools templated
with it, and the write tools the write-plan branch serves on top.

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

* fix(analysis-view): share positions with digit-free generated identifiers

prefix-variance accepted an unobserved literal only when it carried a
digit, so a generated identifier that happened to hold none stayed a
literal tool: an n8n workflow id such as FkMwxmXNGaGVsXFG, whose reads
then never joined the item view. A literal now also qualifies when every
value observed at the position has one length of eight or more characters
and the literal has that length and uses only their character classes.

Across the 50 showcase captures this templates 10 more read tools in
Memos and n8n and no words.

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

* docs(specs): record five environments rebuilt end to end with the rule

Five randomly drawn environments rebuilt on VMs with the rule and the
write-plan branch: smoke, retrieval and write tasks for two models, and
the same calls against the live applications. Record the results, the
misses and their causes, and the corpus numbers after the fixed-length
clause.

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

* fix(analysis-view): share a position with the parameter that observed the literal

A shared position folded every donor's values into the earliest donor's
parameter. With an opaque-token read before sibling-variance reads at
the same position, a PATCH of /api/items/b2 qualified on the sibling's
evidence but became /api/items/{uid} instead of joining the item read's
/api/items/{item_id}. Each position now keeps one donor per naming rule
with its own values, and a literal takes the donor that observed it,
else the earliest one it is spelled like.

Each donor's spelling (length and character classes of its values, and
its fixed-length family) is summarised once per position, so a literal
is checked in time independent of how many values were observed, rather
than copying and rescanning them for every candidate.

A family now forms from one observed value, as the requirement says, so
a digit-free id of the same length shares a position that only one
opaque-token or after-uid-literal tool supplied. The 50 showcase
captures give the same tools as before.

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

* docs(specs): record the prefix-variance review round

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

* fix(analysis-view): bound the spelling summary of shared positions

spellingSummary classified every observed value with three full-size
replaceAll copies and a set per distinct character, so a donor holding
a multi-megabyte segment cost unaccounted memory and time in finish().
Characters are now classified in one pass, and a value longer than 128
characters takes no part in spelling comparisons: it shares a position
only as an exactly observed value. The 50 showcase captures give the
same tools.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 18:11:51 +08:00
morluto 19f2969c56 fix(ci): align omission and browser transport assertions
Check property absence in an object instead of treating explicit undefined as omission, preserving Zod exactOptional semantics and WASM timeout bounds. Update real-browser disconnect verification to require transport_disconnected and reject an invented crash event.

Reproduced the CLI catalog failure on d9767eb48; 37 focused tests and check:ci pass. Real-browser verification runs on hosted CI; Chrome was not launched locally.
2026-10-21 18:07:21 +08:00
d9767eb48f test: assert Result reasons instead of bare ok checks (#5821)
Malformed DOS MZ, raw-binary entry, EVM limit, and Ghidra handshake
cases only checked Result.ok. Name the producer error, or the admitted
language and compiler, so a different failure cannot keep the test green.

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: morluto <morluto@users.noreply.github.com>
2026-10-21 18:00:31 +08:00
Shreyash Patange f5410bdc4c fix(apple): keep scalar facts when object-table entries share a record (#5806)
Binary plist object tables may list one object offset more than once;
plistlib reads each entry independently. The scalar scanner marked any
repeated offset as incomplete metadata, which made the number projection
drop every exact decimal in the archive. A keyed archive whose entries
10 and 11 share an Int64 9007199254740993 reported both as observed
9007199254740992 and rounded an unrelated 2^60+3 sibling, with only the
generic incomplete-metadata caveat.

A repeated valid offset carries the same source facts as its first
visit, so observe it once and continue. Offsets that fail to parse still
mark metadata incomplete. Shared 16-byte integers and dates are rewritten
once in the decoder copy, which every entry then reads; the overlap plan
already treats one shared offset as one span. Keyed archives and
Interface Builder decoding share this scanner.
2026-10-21 17:51:24 +08:00
gensokyo-dev f801b2fbfd feat(sqlite): make worker deadlines adjustable with recovery guidance (#5818)
Expose a per-call SQLite worker deadline with exact CLI/MCP recovery controls,
parent-owned execution metadata and historical unknown preservation. Keep fixed
resource limits and cleanup admission intact. Share typed cleanup composition
with one captured-output owner and preserve the timeout after cleanup resolves.

Fixes #5812. Follow-up tracking: #5808. Existing CLI test-oracle failure: #5845.

Verified 155 SQLite tests (3 skipped), static/docs/discovery checks, six real
CLI/MCP pairs and 13 actual Codex sessions including four corrected deadline
recoveries. Three pre-dispatch discovery failures remain explicit, not passes.
Review findings include real retained-cleanup and fixture ownership regressions.

Assisted-by: Kogasa (gensokyo-skills:kogasa-surprise-testing)
2026-10-21 16:45:46 +08:00
gensokyo-dev 5241597594 perf(rust): query selected dependency facts from retained Evidence (#5815)
Add optional package/edge selection and summaries through shared retained Evidence, preserving complete delivery and parent identity.

Fixes #5802
2026-10-21 16:12:04 +08:00
gensokyo-dev a5f982de8d feat(wasm): make command deadlines adjustable with recovery guidance (#5798)
Expose the existing WABT command deadline as an optional validated per-call CLI/MCP budget, preserving its 30000 ms default and fixed safety allowances.

Retain selected execution profiles in success and rejected-module Evidence. Keep typed timeout recovery through cleanup failures without duplicating command output or making incomplete cleanup retryable.

Verified focused tests, real WABT CLI/MCP workflows and actual Codex recovery/blocked controls; detailed measurements and qualification limits are recorded in issue #5797. Merge uses the explicitly authorized maintainer override with pending CI and unchecked human-review attestations disclosed on PR #5798.

Closes #5797

Assisted-by: Kogasa (gensokyo-skills:kogasa-surprise-testing)
2026-10-21 15:00:05 +08:00
morlutoandCursor Agent ea24207b57 perf(ci): pack each test tier from its own timing history (#5799)
* perf(ci): pack each test tier from its own timing history

Fast shards were still ordered with the latest full-suite timing artifact,
which stayed stale across compiled-CLI session reuse. Publish and download
test-timings-fast and test-timings-full separately, and keep the legacy
artifact as a fallback until a tier has its own history.

* perf(ci): drop the shared timing-history fallback

Shard lookup now reads only the artifact for the current tier. A missing
artifact leaves history unset instead of packing from the old test-timings
file.

* test(ci): look up fast history when only full timings exist

The empty-history case now requests the fast tier against a full-tier
artifact and checks that the fast lookup ran and returned nothing. A
missing tier stays a separate test that does not query.

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2026-10-21 14:58:17 +08:00
morluto 6b7d429491 fix(apple): decode Swift conformance type references
Decode direct Objective-C class names separately from nominal descriptors, retain indirect class bindings as partial facts, and preserve readable protocol and witness evidence when related bytes are unavailable.

Validated with 98 focused tests, check:ci, docs:check, and named-schema CLI/MCP replays of compiler-produced imported-class and struct fixtures plus an inert kind-3 control. The fixtures were inspected statically, never executed.

Fixes #5738
2026-10-21 14:50:57 +08:00
morluto 5891bfb302 fix(browser): retain precise observation termination facts
Distinguish CDP session detachment, observed crashes, and transport loss. Preserve the observed window alongside the original typed coverage-stop failure and unconfirmed cleanup.

Validated with 40 focused boundary tests, check:ci, docs:check, and the real Chrome public CLI/MCP verifier on macOS. Browser timing comparison remains incomplete because unchanged-main fixture readiness was intermittent.

Fixes #5756
2026-10-21 14:50:32 +08:00
morluto d672cc2399 fix(apple): preserve binary plist scalar source facts
Decode native binary plists from bounded owned source bytes before conversion can erase scalar values. Share scalar preflight and isolate overlapping decoder records while retaining exact integers, date uncertainty, and original bytes. Refuse numeric payloads that read structural table bytes, and avoid retaining projection metadata when callers only need structural validation.

Closes #5701
Closes #5703
Closes #5705
Closes #5782
Closes #5783
Closes #5792
2026-10-21 14:09:29 +08:00
gensokyo-dev 25bea07b42 fix(model): preserve unsupported GGUF version diagnostics (#5789)
Preserve unsupported GGUF version bytes and both UINT32 interpretations without inventing byte order at the shared parser. Keep existing layout support, typed errors, evidence identity, scoped reads and resource limits.

Validation: 78 focused tests, full static/architecture and documentation checks, real CLI/MCP version controls and an independently checked public model. Latest-head Codex found no issues; Greptile is 5/5 and all review threads are resolved.

Some CI remained pending at merge qualification. Merge under the human owner's explicit standing bypass authorization does not claim the full CI suite passed.

Fixes #5749.
2026-10-21 05:59:18 +00:00
Kaoru0822andClaude Opus 5.5 bd8d73ab1d fix(environment): report later bundle write failures as host failures (#5581)
* fix(environment): report later bundle write failures as host failures

Only destination admission now refuses the selected directory as invalid
input. A path failure on a nested bundle file after admission, such as a
host pathname-length limit, is reported as an output failure that names the
host cause and the absolute failed path, with recovery guidance for the
partial folder it leaves in place.

Fixes #5522

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

* fix(environment): name the host-refused path in export permission failures

A permission failure after earlier bundle files were written was reported
as access denied for the bundle root, so the documented failed path was
lost. The refusal now names the absolute path the host reported, falling
back to the root, and keeps the root as `output_directory`.

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

* fix(environment): attribute pathless bundle write failures to their file

Descriptor operations such as `write` and `fsync` fail without a path, so
a disk-full failure after earlier files named only the bundle root. The
bundle writer now attributes such failures to the file it was writing.
Host-write recovery is applied only to actual host failures; an integrity
refusal keeps its own message without disk-space advice.

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

* fix(environment): attribute package tarball read failures to the tarball

The destination-write attribution also caught failures from reading the
admitted package tarball during the copy, so a source `EIO` named the
destination file and advised freeing disk space. Host failures are now
attributed by direction: tarball reads name the tarball with read
recovery, bundle writes name the file being written.

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

* docs(environment): state which export failures name a path

The export documentation and host-write message promised a failed path
for every later write failure, but a directory sync at commit time fails
without one. They now promise the failed bundle file or package tarball
when the failure is on one, and only the cause for a folder sync.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 22:55:05 -07:00
Kaoru0822andClaude Opus 5.5 0cfef92ae1 fix(desktop): classify rejected desktop roots as target failures (#5577)
inspect-desktop-application reported a regular-file, missing or read-denied
root as artifact_operation_failed/execution_failure, leaving the ENOTDIR,
ENOENT or EACCES cause only in message text.

DirectoryArtifactReader now marks root admission and root-open failures with
DirectoryArtifactRootFailure. DesktopApplicationReader, which owns the caller's
selection, maps a non-directory root to unsupported_target and host failures to
the shared BinaryTargetError (target_unavailable, or access_denied with
read-access recovery), keeping any partial observation. Child-entry failures
and other DirectoryArtifactReader consumers are unchanged.

Fixes #5513

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 22:54:40 -07:00
Kaoru0822andClaude Opus 5.5 b81431d7ff fix(artifacts): report a stable symlink loop as a path resolution failure (#5571)
ELOOP raised while resolving the caller-selected path (access/realpath)
is a symbolic link loop or over-deep chain in that path, not evidence of
a change. Classify filesystem failures by resolution stage so only ELOOP
from opening the canonical path with symbolic links rejected keeps the
"target changed while being opened" reason. The structured code,
category and retryability are unchanged.

Fixes #5521

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 22:54:28 -07:00
4a7a4b5744 fix(hopper): preserve caller abort reasons on cancellation (#5780)
* fix(hopper): preserve caller abort reasons on cancellation

Hopper cancellation dropped the caller's abort reason, and the typed
error treated an explicit undefined cause as omission. Retain the exact
reason at the request queue, startup, socket, and launcher boundaries.

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

* style(hopper): format launcher cancellation return

oxfmt rejects the wrapped abort return in HopperApplicationLauncher.

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

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: morluto <morluto@users.noreply.github.com>
2026-10-21 13:53:02 +08:00
Kaoru0822andClaude Opus 5.5 4dfe3464b2 fix(signals): report rejected sigrok selectors as invalid input (#5570)
sigrok-cli refusals of a caller-supplied decoder ID or annotation class
were reported as execution_failure with "Run `rea doctor`" advice, which
has no sigrok check. Recognize the observed sigrok-cli 0.7.2 refusals at
the provider boundary only when the rejected name matches a caller
selector, and report invalid_request naming the decoders or annotations
argument, retaining the captured output and pointing to
inspect_signal_decoders. Other non-zero exits stay provider failures.

Fixes #5515

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 22:52:33 -07:00
Kaoru0822andClaude Opus 5.5 c1e04bf8c0 fix(sqlite): name the selected path in malformed-database errors (#5564)
The owned worker inspects only a private snapshot, so its format reason
cannot name the caller's selection. Project path-scoped worker rejections
with the selected database path so distinct malformed inputs report their
own source, matching the documented failure contract.

Fixes #5520

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 22:52:18 -07:00
a00e4ac363 fix(apple): refuse invalid binary dates and unknown sub-millisecond instants (#5786)
* fix(apple): refuse invalid binary dates and unknown sub-millisecond instants

Reachable binary plist dates must use marker 0x33. Other type-3 markers are
malformed and are no longer projected as observed timestamps. A valid float64
date stays known only when it is exactly the canonical millisecond instant;
other finite instants remain typed unknown dates with a counted limitation.

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

* fix(apple): share one decoder copy and bound binary date payloads

Wide-integer rewrites and unrepresentable date payloads now share the
single parser-input copy already reserved by the decode budget. Reachable
date markers and payloads must end at the offset table even when other
decoder-bounds checks read trailing bytes.

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

* test(apple): refuse out-of-region dates through the keyed reader

Decoder-bounds date truncation is now asserted on decodeKeyedArchiveBytes,
the path that selects those bounds.

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

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: morluto <morluto@users.noreply.github.com>
2026-10-21 13:48:50 +08:00
morluto d36cb61210 fix(ci): declare the native fixture compiler for Knip 2026-10-21 13:36:46 +08:00
morlutoandCursor Agent 0ad42752d3 perf(test): reuse one compiled CLI for the remaining hot command files (#5784)
Go, IL2CPP, dylib resolution, and application-workflow tests were still
starting a compiled CLI for every command. They now reuse one process
per file. Warm focused runs dropped from 40s, 46s, 35s, and 21s to
14s, 21s, 20s, and 2s.

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2026-10-21 13:34:37 +08:00
morluto dcdab56acb fix(test): use eligible bundle targets for UI availability 2026-10-21 13:33:15 +08:00
morluto 5af8fe702b fix(test): use owned cleanup for npm invocation fixtures 2026-10-21 13:30:36 +08:00
gensokyo-dev 7d8e4e4338 perf(model): avoid decoding omitted GGUF strings (#5781)
Validate omitted GGUF strings without materializing discarded text while preserving full UTF-8 checks and retained Evidence. Record real-model costs and workflow coverage; preserve failed experiment receipts through cleanup.

Fixes #5774.
2026-10-21 13:26:13 +08:00
Jeff 7c6c25dda7 fix(artifacts): report target applicability in session discovery (#5763)
* fix(artifacts): report target applicability in session discovery

* fix(artifacts): preserve resolved target applicability facts

* fix(target): retain bundle metadata for explicit formats
2026-10-21 12:59:27 +08:00
Jeff 89b1941c8e fix(native): preserve mixed-slice signature observations (#5765)
* fix(native): preserve mixed-slice signature observations

* fix(native): retain signature facts when slice inspection fails

* fix(native): accumulate known slice observations before failure
2026-10-21 12:59:10 +08:00
71b696582b perf(test): reuse one compiled CLI for export-binding parity (#5679)
* perf(test): reuse one compiled CLI for export-binding parity

The slow export-binding cases each spawned the compiled CLI three times.
One warm process now serves those JSON commands from a single command
instance, so startup and retained registrations are paid once per file.

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

* style(test): format the warm CLI session

oxfmt --check rejected the session and its worker.

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

* fix(test): report warm CLI shutdown, deadlines, and exit status

A compiled CLI session that needed a signal after stdin closed reported
success. A command that never answered left later checks waiting, and a
failure recorded only on process.exitCode was returned as status 0.

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

* fix(test): close the warm CLI worker when a status check fails

The failure-then-success case only closed the compiled worker after both
assertions passed. Register the close with the test so a failed check still
stops the child.

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

* fix(test): retain a timed-out compiled CLI worker until it exits

A request deadline cleared the child before SIGKILL was observed. The next
command could start a replacement, and close could miss the original process.
Retired workers stay owned until exit, and a failed stop remains for a later
close to retry.

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

* fix(test): reject a compiled CLI launch that close interrupts

Closing during the ready handshake detached the child before the launch
promise settled. The queued command then waited on the startup timer.
Close now rejects that startup before the child is detached.

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

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: morluto <morluto@users.noreply.github.com>
2026-10-21 12:57:04 +08:00
43ac3d1857 fix(windows): retain native handles when close is unconfirmed (#5772)
* fix(windows): retain native handles whose close is unconfirmed

CloseHandle failure no longer drops the numeric value. A still-open handle
stays owned and can be closed again; a value missing from the handle table
is dropped and never closed again; unreadable liveness blocks another close
until a probe says the value is open.

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

* test(process): keep scratch until inherited pipes close

Lock the execFileOutput settlement contract: an inherited-pipe descendant
holds the caller's scratch root until it exits, and destroying the readers
is not process-family completion.

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

* fix(windows): retry admitted file closes before runtime removal

A failed private-file close stays on the runtime that opened it. Runtime
removal closes that same wrapper before the tree walk, including when the
read that opened the file has already returned.

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

* fix(windows): resume a close after part of the owner is released

A runtime whose directory handle is already released continues with its
parent handles instead of walking that invalid root. A process whose
settlement was already observed retries process close alone, without kill
or poll against the released job.

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

* test(process): signal only an owned inherited-pipe descendant

Skip the descendant SIGKILL after pipe settlement. On an earlier failure,
signal only when the process command line still contains that test's path.

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

* fix(windows): do not close a handle again after a failed close

An open liveness probe can observe a reused numeric value. After CloseHandle
fails, drop the value only when the handle table no longer contains it, and
do not call CloseHandle on that number again.

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

* fix(windows): stop using a close-blocked pipe for I/O

A broken pipe whose CloseHandle fails stays owned, but its numeric value
may have been reused. Later polls and stdin writes treat that pipe as
ended instead of reading or writing the number.

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

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: morluto <morluto@users.noreply.github.com>
2026-10-21 12:56:36 +08:00
morluto fa8b87c470 fix(il2cpp): validate generic-container extents before index widths
Validate the fixed 16-byte version-39 records before using their count to select the serialized index width. Reject contradictory count and byte extents while preserving valid zero, one and 300-record controls.

Validation: 89 focused reader/provider/compiled CLI and stdio MCP tests; check:ci; seven reproduced CLI and SDK-validated MCP cases with unchanged fixture hashes. Version-specific source and independent parser evidence distinguish fixed records from compressed indices. Synthetic metadata only; no real-game or target-execution qualification.

Fixes #5751
2026-10-21 12:55:35 +08:00
Matt Van Horn afa5da745e feat(artifacts): inspect and extract PyInstaller archives 2026-10-21 12:48:29 +08:00
morluto 22d5a40a2e fix(pe): inspect resource records at their declared offsets
Remove unsupported DWORD alignment refusals from resource data entries and adjacent directory tables. Preserve original record locations, UTF-16 name alignment, bounds, mapping, overlap checks and payload hashes.

Validation: 49 focused parser/provider/filesystem/lifecycle and compiled CLI/stdio MCP tests; check:ci; independent pefile agreement for the reported layout, padded control and PE32/PE32+ word-aligned directories. Public CLI and SDK-validated MCP retain truncated-input refusals and unchanged source hashes. Synthetic static inspection only; Windows loading was not tested.

Fixes #5750
2026-10-21 12:46:29 +08:00
gensokyo-dev 264862bf2e fix(ida): preserve registration recovery facts in readiness
Fixes #5771.

Preserve the configuration owner's parsed registration settings and remediation in IDA readiness, retain caller-selected paths, and distinguish configuration selection from validity and live connection. Verified with 84 relevant tests, CLI/MCP negative-case parity, static/docs gates, independent source review and repeated actual Codex recovery tasks. No real IDA engine/GUI analysis qualification.

Assisted-by: Kogasa (gensokyo-skills:kogasa-surprise-testing)
2026-10-21 12:41:50 +08:00
044f180222 test(cli): accept opaque Kuna doctor run tokens (#5677)
* test(cli): accept opaque Kuna doctor run tokens

The doctor cancellation fixture parsed REA_PROCESS_RUN_ID as a UUID.
Kuna version probes publish rea-kuna-version-<uuid>, so both signal
cases failed before checking cancellation. Accept that opaque token
and keep the JADX ownership check on a UUID.

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

* test(cli): require the Kuna doctor run token

An omitted REA_PROCESS_RUN_ID disappears from the ready JSON. The Kuna
cancellation case now rejects that record unless runId is a non-empty
ownership token. JADX still requires a UUID.

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

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: morluto <morluto@users.noreply.github.com>
2026-10-21 04:37:26 +00:00
morluto 3b67b362aa fix(apple): explain present rejected dylib candidates
Derive unresolved findings from stored candidate paths and outcomes while preserving weak, lazy and conditional load consequences. Document not-loadable and platform-mismatch outcomes and later-path resolution.

Validation: 126 resolver/CLI/MCP tests, check:ci, docs:check, and seven compiled CLI plus SDK-validated stdio MCP fixture cases. Source-built and mutated inert Mach-O inputs retained identical hashes; no target execution or dyld runtime comparison.

Fixes #5747

Fixes #5694
2026-10-21 12:34:05 +08:00
gensokyo-dev f1126ff7bf fix(wasm): retain actionable provider configuration diagnostics
Fixes #5767.

Retain exact WABT version-probe output and tool identity, including nonzero command failures, and provide condition-specific configuration recovery. Verified with 54 focused tests, the final 51-case real WABT CLI/MCP lane, actual Codex recovery trials, and independent source reviews. No provider installation, target execution or admission relaxation.

Assisted-by: Kogasa (gensokyo-skills:kogasa-surprise-testing)
2026-10-21 12:25:28 +08:00
morluto 3a0e91dd91 chore: integrate concurrent main changes with the dylib fix
Preserve the upstream WAT framing optimization alongside the qualified dylib fix. The combined tree passes 14 compiled dylib CLI tests and the full check:ci gate.
2026-10-21 12:06:06 +08:00
morluto 90062c0922 fix(apple): trace legacy macOS dylibs with implicit platforms
Apply dyld’s implicit macOS compatibility rule only for a root executable with observed macOS platform and a supported candidate CPU type. Retain the absent platform fields as raw facts and expose the derivation on the candidate. Traverse the selected dylib and report missing required children; preserve unknown platform coverage and the later known fallback graph in other contexts.

Validation: 144 source/reader tests, 14 compiled CLI tests, 18 schema tests, full check:ci and docs:check passed. Four source-built app variants passed CLI and SDK-validated stdio MCP checks with unchanged fixture digests. No fixture executable or Apple dyld comparison was run. Independent review resolved a library-root authority gap before integration.

Sixteen cold/warm task-cost observations are retained locally with sampling limits; the catalog adds 66 bytes. Actual model tokens and maximum-sized inputs were not measured; no speedup is claimed.

Closes #5696
2026-10-21 12:04:29 +08:00
gensokyo-dev 70835d3d5f perf(wasm): skip payload text during WAT framing
Fixes #5760
2026-10-21 12:04:05 +08:00
ccfaf520b7 perf(test): reuse one compiled CLI process per JavaScript application file (#5676)
* perf(test): reuse one compiled CLI process per JavaScript application file

analyzeJavaScriptDirectory CLI cases were still starting a fresh process
after stdio MCP reuse. Keep one compiled process for the same child
environment so later cases skip Node and CLI startup.

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

* fix(test): bound shared CLI worker lifetime

Retire a worker that reports a fatal failure before resolving that command,
and wait for it to exit before the next command starts. Join a session frame
once its bytes are complete instead of copying the unfinished buffer on every
chunk. Restart the shared process after 32 commands or a reported 1 GiB
resident set so one file does not keep a single heap high-water mark.

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

* fix(test): retire a shared CLI worker after a transport failure

A malformed session frame rejected the command and sent SIGTERM without
clearing the worker. signalCode stays unset until the process exits, so the
next queued command could write to that same process. Transport failures now
use the fatal-reply retirement path and the queue waits for that exit.

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

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: morluto <morluto@users.noreply.github.com>
2026-10-21 12:02:15 +08:00
gensokyo-dev bbbd208743 fix(pcap): preserve frames with empty protocol stacks (#5762)
Preserve valid empty TShark protocol stacks at the shared parser, retain frame and other fact inventories, and cover malformed producer output and mixed-frame ordering.

Validated with focused source/boundary tests, full static checks, docs checks, real Linux CLI/MCP and a mixed-interface public HTTP pcapng. Latest-head Codex found no issues; Greptile is 5/5 and its test-coverage thread is addressed. Remaining queued CI was bypassed under the human owner's explicit authorization; this does not claim that the full CI suite passed.

Fixes #5734.
2026-10-21 04:00:16 +00:00
morluto c19c2d6723 fix(native): separate container facts from Go metadata admission
Move ELF, PE and Mach-O container parsing into a shared native owner. Go alone selects and validates Go build-info sections, so unrelated malformed Go metadata no longer blocks Rust dependency or IL2CPP inspection. Remove the superseded Go-specific container representation and migrate all consumers. Adapted from draft PR #5510 without asserting human PR readiness.

Validation: 51 compiled CLI/MCP acceptance tests across Rust, Go and IL2CPP; 186 shared-reader tests; full check:ci passed. Three public synthetic-file probes reproduce the baseline Rust refusal and validate the fixed SDK-advertised MCP output, artifact digests and unchanged bytes. The exact zero-address nonallocated ELF case now succeeds for Rust while Go still reports invalid_format. No target execution or real compiler-artifact qualification was performed; no performance improvement is claimed.

Closes #3398
2026-10-21 11:58:15 +08:00
morluto 08f4ba99ce test(process): share lineage fixtures to restore the duplication gate
Extract repeated process rows, environment transitions and exact lineage expectations while retaining all identity, replacement-read and cancellation cases. Clean main exceeded the configured 3.5% duplication limit at 3.521867%; this refactor passes at 3.499188% without changing the limit.

Validation: 72 focused process-ownership tests and the full check:ci gate passed. Independent source review found no assertion or coverage loss. No runtime behavior changes.
2026-10-21 11:56:36 +08:00
df8b4d8a5a fix(process): token-check identityless exit-refresh replacements (#5675)
* fix(process): token-check identityless exit-refresh replacements

A launcher-exit refresh could drop a reparented grandchild that was absent
from the prior snapshot and report cleanup complete, or publish a verified
empty lineage, without reading its run token.

Fixes #5206

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

* fix(process): re-read identityless refresh rows when relations change

Lineage refresh classified every pre-existing process as a newcomer, and it
skipped a row after caching its first parent and group. Seed the exclusion
set from the initial snapshot and read a row again when that relation changes.

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

* style: format files oxfmt rejects on main

Repo-wide format:check fails on these two files at current main. Align them
so the process-ownership fix can pass the required static check.

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

* fix(process): keep owned groups found while confirming exit

An unreadable exit-refresh row used to hide the confirmation table, and
revalidation dropped groups discovered after the plan was built. Both now
stay in the same cleanup, and confirmation reads are bounded.

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

* fix(process): reclassify lineage rows when tree membership changes

Initial outsiders and cached refresh rows are read again when a parent,
group, or ancestor move changes launcher-tree membership. Unchanged
initial rows stay unread.

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

* fix(process): re-read cached identityless lineage tokens

Parent and group do not identify a process generation. A row whose token
was read is read again on the next refresh, so a reused PID cannot keep
a verified descendant.

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

* fix(process): revalidate an owned group that reappears

A group discovered after it was already processed is queued once more.
Another appearance stays unresolved instead of reporting cleanup complete.

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

* fix(process): recheck a helper that changes groups during confirmation

The confirmation table can move a token-matched member into a new group.
That row is read again, and cleanup signals the group it belongs to now.

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

* fix(process): publish one lineage row when a descendant returns

A refreshed descendant replaces the initial row for the same PID, including
when its group moves away and then returns to the original relation.

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

* fix(process): re-read foreign PIDs on the exit confirmation table

A readable foreign token does not identify a process that later reuses
that PID. The confirmation table reads those newcomers again.

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

* fix(process): recheck a pre-exit PID whose relation changes

An unchanged pre-existing row stays unread. A prior PID whose parent or
group changed is token-checked, so a reused owned process is not skipped.

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

* fix(process): recheck an initial descendant after its token matches

A successful read in the first lineage pass marks that PID token-checked.
A later refresh reads it again, so a reused PID cannot stay verified.

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

* fix(process): recheck the launcher on each lineage refresh

A later refresh reads the launcher PID again. A missing or unowned
launcher is unavailable instead of rooting a verified lineage.

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

* fix(process): publish lineage from the latest refresh table

A descendant missing from the latest refresh is omitted. A refreshed
launcher whose group or command no longer matches stays unavailable.

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

* test(process): cover a refreshed launcher parent mismatch

A same-token launcher whose parent no longer matches the committed
expected parent is unavailable, same as a group or command change.

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

* fix(process): recheck a token-read helper that returns to its group

A confirmation table dropped a previously matched PID once its parent and
group matched the pre-exit row, so cleanup could signal only the group it
had joined and report success while that helper survived.

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

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: morluto <morluto@users.noreply.github.com>
2026-10-21 11:43:17 +08:00
morluto 00266efe13 fix(plist): preserve Unicode separators during XML normalization
Use one XML 1.0 line-ending rule in the shared XML boundary and plist source-span owner. Normalize literal CRLF and CR while retaining NEL, line separator and paragraph separator as observed characters. Keep character references, raw scalar locations and prototype-key omission intact.

Validation: 229 XML/plist source tests, 60 adjacent Ghidra/Apktool/SAZ source tests, and 33 compiled CLI/provider tests passed. Full check:ci passed. Real plutil XML and binary fixtures reproduced the baseline loss and passed CLI plus SDK-validated stdio MCP checks with unchanged input digests. Independent source review found no blocker. Binary conversion losses tracked by #5701, #5703 and #5705 remain separate work.

Closes #5702
2026-10-21 11:42:25 +08:00
gensokyo-dev 335c042d16 docs(testing): reference the configured duplication limit (#5758) 2026-10-21 11:26:43 +08:00
morluto d0067ba34a fix(native)!: preserve Swift pointer nullability and import bindings
Resolve relative pointers using field-owned ABI encodings. Keep null, raw, rebased, imported and unknown targets distinct, and retain imported conformance rows with exact Mach-O binding provenance. Decode legacy rebase membership with source-derived memory and work bounds, preserving validated prefix facts when streams are incomplete.

Validation: check:ci, docs:check and build passed; 158 source tests and 20 schema/acceptance tests passed. Ten source-built Apple dispatch variants and four issue-specific CLI/MCP workflows passed without executing targets. Paired cold/warm CLI/MCP cost observations retained locally; model tokens and maximum-sized input runtime were not measured.

Closes #5683
Closes #5685

BREAKING CHANGE: Swift conformance type_name and protocol_name can be null when names cannot be resolved; consumers must use typed type_binding and protocol_binding evidence. MachoImportBinding replaces the former Objective-C-specific binding type and schema.
2026-10-21 08:17:12 +08:00
morluto 9258df02cc test(apple): compare asset catalogs from matching root workflows
Compare explicit bundle-resource CLI and MCP results at the positive empty-inventory offset. Preserve a separate binary-bound MCP assertion with the exact fixture executable digest and complete metadata payload.

Validation: reproduced the stale root-versus-binary parity failure on current main, then passed the macOS public CLI/MCP acceptance case across all five NIB fixtures. Focused lint, formatting and diff checks passed. No target code was executed.
2026-10-21 07:37:36 +08:00
morluto 1502cfbc8c fix(apple): resolve absolute dylib paths inside the selected root
Preserve load-command paths as evidence while passing a separate root-relative lookup path to the bounded tree resolver. Apply the same containment rule to absolute install names and rpath bases, including caller-selected root symlink spellings.

Validation: 140 source tests, full check:ci, docs and runtime build passed. Compiler-built literal, rpath and relative fixtures passed CLI/stdio MCP parity, preserved both dependency edges and left all bytes unchanged. External system paths stayed outside-target; relative control was unchanged. Fixtures were not executed, and actual dyld execution was not qualified.

Closes #5637
2026-10-21 07:37:15 +08:00
morluto 84c89d1107 test(javascript): separate heap pressure from graph admission proof
Match memory failures to the exact source identity instead of graph ordering. Preserve CLI parse, file-evidence, partial-result, later-source and unchanged-input checks across valid memory-pressure outcomes. Use a controlled parent heap envelope with a real owned analyzer to prove module retention and graph rejection independently of host GC timing.

Validation: full check:ci passed; 17 focused tests passed, with the final 4 changed tests rerun successfully. Independent test-strength and isolation review passed. The earlier hosted failure exposed missing graph-only fields; its exact alternate memory stage was not captured.
2026-10-21 07:26:20 +08:00
morluto 0bd14ca16c fix(browser): preserve uncertain main-frame navigation identity
Own CDP frame applicability and identity at the parsed event boundary. Preserve malformed or incomplete main-frame facts, recognize replacement roots, and carry the same document identity through captures, screenshots, timelines, and execution windows.

Validation: full check:ci and docs:check; 763 browser and dispatch tests passed, 6 skipped. Independent source and fixture-lifecycle review passed. Local real-Chrome behavior was not exercised.

Closes #5665
2026-10-21 07:16:47 +08:00
morluto 9dee989a79 fix(native): count Objective-C coverage by class object
Count each admitted unique class or metaclass object at its parsing owner. Preserve emitted metadata, independent class-list scan bounds, partial failures, and truncation.

Closes #5706
2026-10-21 07:01:17 +08:00
morluto f789fd1fd8 fix(native): count Swift vtable coverage in slot units
Count examined and decoded vtable slots in the same population, preserving all returned slots and partial descriptor failures. Document the coverage unit and retain malformed-sibling and unresolved-slot checks.

Closes #5681
2026-10-21 06:56:18 +08:00
morluto ccc7da309d test(mcp): retain distinct Objective-C class observations
Require both fixture addresses and unknown identity coverage when no selected-image binding proof permits coalescing.
2026-10-21 06:45:34 +08:00
morluto 56679dd2ae fix(files): preserve physical read provenance through cleanup
Keep raw physical read diagnostics alongside explicit operation provenance on the same cleanup owner. Preserve provenance after successful close retry for opted-in readers, restore legacy error projections, and prevent borrowed cleanup-shaped failures from authorizing foreign resource retries.

Refs #5686
2026-10-21 06:45:34 +08:00
morluto 52c84f6f58 test(cli): bound ADB MCP validation to the advertised tool
Reuse the exact selected advertised descriptor for SDK output validation instead of compiling the entire cached catalog for every fresh parity-test client. Retain actual catalog discovery, schema validation, and all result assertions under the existing worker heap limit.
2026-10-21 06:23:20 +08:00
morluto 4852c830af fix(native): preserve Mach-O names equal to version
Parse only documented compound otool field labels so valid segment and section names are retained at the owning boundary. Verified real Apple clang fixtures through compiled CLI and stdio MCP without executing the targets.

Closes #5680
2026-10-21 06:19:43 +08:00
morluto 949966712e fix(files): preserve physical read failures across late cancellation
Tag physical read outcomes at the owning boundary and preserve opaque caller cancellation reasons through JSON pipelines and reference imports. Share named bounded-read options across consumers and retain failed cleanup ownership for retry.

Closes #5686
2026-10-21 06:14:03 +08:00
morluto 4d7de00d4c fix(native): coalesce Objective-C class imports by binding identity
Join provider class observations only when selected-image identity and exact import bindings establish equivalence. Preserve distinct observations and unsupported coverage.

Refs #5607
2026-10-21 06:12:21 +08:00
morluto b231c28cf6 fix(hopper): own Mach-O preparation through cleanup retry
Preserve exact selected-image identity and descriptor ownership across preparation, final cleanup, and retries. Keep physical failures and failed cleanup leases observable through provider and launcher boundaries.

Closes #5688
2026-10-21 06:12:09 +08:00
morluto 7dd6941393 refactor(test): share browser and apktool fixture lifecycles 2026-10-21 05:50:50 +08:00
morluto 1bb2fa23de test(apple): match plist provenance to the selected workflow 2026-10-21 05:50:50 +08:00
morluto ab58edcdf5 fix(environment): retain tarball descriptor ownership during export
Use the shared positional reader for both tarball passes so early output refusal cannot close the descriptor ahead of its owner.

Closes #5684
2026-10-21 05:33:35 +08:00
morluto 60956c8c56 fix(ci): run cache retention from its trusted workflow revision 2026-10-21 05:28:08 +08:00
morluto 5518363aa6 test(artifacts): compare matching resource-root workflows 2026-10-21 05:28:03 +08:00
morluto 4108d1363a test(cli): align Kuna readiness and cancellation expectations 2026-10-21 05:27:56 +08:00
morluto 0bc2b42001 fix(browser)!: preserve unknown WebSocket frame facts
Keep malformed or missing CDP opcodes and payload facts unknown, retain observed zero and empty payloads, and avoid false capture differences while preserving independently known facts.

Validation: 59 source tests; compiled public CLI and stdio MCP fake-CDP acceptance; check:fast; docs:check; changed-file formatting. Real Chromium malformed-event behavior was not tested.

BREAKING CHANGE: WebSocket event opcode is nullable and numeric values are restricted to the four-bit protocol range.

Fixes #5666
2026-10-21 05:00:37 +08:00
c38f96fa69 test(cli): keep IPC trace input under the Linux spawn limit (#5673)
* test(cli): keep IPC trace input under the Linux spawn limit

The listener-callsite CLI builds one trace argument from the full
application Evidence document. On 4 KiB-page Linux that string exceeds
MAX_ARG_STRLEN, so spawn throws E2BIG before the command starts. Keep the
document in a file, require the spawned argv to fit the single-argument
budget, and lock that budget through the test process registry.

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

* test(process): size the spawn ceiling from ARG_MAX and MAX_ARG_STRLEN

A payload at the single-string limit still fails execve with E2BIG when
the stack shrinks ARG_MAX below the rest of the image. Accept only the
bytes that fit both budgets, and skip that success spawn when the fixed
argv and environment already fill ARG_MAX.

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

* style(test): format files rejected by oxfmt in static checks

check:ci:fast failed format:check on the CLI fixture import and, after
merging main, on the admission test's new command-abort case.

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

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: morluto <morluto@users.noreply.github.com>
2026-10-21 04:48:33 +08:00
Ulises Millán c4b6aa3061 fix(managed): treat extension-only module scopes as unknown identity (#5674)
Closes #5467
2026-10-21 04:35:45 +08:00
morluto dceefa22cb Merge remote-tracking branch 'origin/main' into codex/audit-continuation-20261020-refresh20 2026-10-21 04:22:27 +08:00
morluto 6a6adf703f fix(resources)!: inspect app resources without an executable
Add a read-only app resource session shared by CLI and MCP. Preserve resource-root provenance without inventing executable identity, reject binary-only snapshots and operations, and retain failed cleanup owners across switches and retries.

Keep filesystem admission failures typed and preserve cancellation ordering from observed cleanup facts. Document resource routing in the packaged investigation skill.

BREAKING CHANGE: resource-only observations may have a null Evidence subject and null executable target digests. Clients must use resource_root and reported per-resource provenance instead.

Refs #5589
2026-10-21 04:22:20 +08:00
de4579e993 fix(observation): keep incomplete cancellation observations (#5644)
* fix(observation): keep incomplete cancellation observations

Service projection treated a retained observation that failed the completed
schema as a malformed completed result. Cancellation became unreadable_output
and the partial was dropped. Incomplete observations now stay on the owner
error, including the exact abort reason.

* fix(observation): drop non-JSON incomplete observations

Keep a retained partial only when projectAnalysisError can serialize it.
Clear a non-JSON partial and record an AnalysisOutputError as a secondary
failure so cancellation and timeout stay the owner error.

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

* fix(observation): validate incomplete partials without copying

jsonValueSchema.safeParse cloned a JSON-safe partial that projection
copies again. Use jsonValueValidationIssue so the gate only checks.

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

* test(ida): expect non-retryable unresolved procedure lookups

Main stops treating invalid_request as retryable. An unchanged missing
procedure stays invalid_request and is not marked retryable.

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

* test(observation): share incomplete-observation assertions

The new non-JSON cases repeated the same owner-error checks and pushed
duplication over the 3.5% limit. One expectation helper keeps those
checks without a second copy.

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

* test(observation): keep the assertion helper out of the package

The helper imports Vitest. Rename it to a fixture so the production
build excludes it and dist does not import a dev dependency.

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

* fix(observation): clear non-JSON partials on cleanup primaries

Projection can return the cleanup primary after retry. Clearing only
the wrapper left that primary's partial in place, so projectAnalysisError
threw. The gate now clears every non-JSON partial in the chain.

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

* fix(observation): freeze retained incomplete observations

A JSON partial stayed a live provider object, so a later mutation could
make projection throw. The gate now keeps one frozen snapshot and still
drops a non-JSON partial without copying it.

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

* refactor(observation): share web runtime partial retention

Observe and inspect used the same promote-or-keep path. One helper
owns that sequence so the two operations cannot drift.

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

* fix(observation): snapshot a shared partial once

A cleanup wrapper and its primary can hold the same partial object.
The gate now reuses the first frozen snapshot for that object.

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

* fix(observation): parse a kept partial once

jsonValueSchema.safeParse validates and copies only a JSON partial.
The gate freezes that copy and no longer walks the same value first.

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

* test(observation): share repeated observation fixtures

The incomplete-observation tests repeated the same provider setup and
an empty Electron coverage tail, which pushed duplication over 3.5%.
One coverage fixture and shared assertions bring the tree back under
the threshold.

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

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: morluto <morluto@users.noreply.github.com>
2026-10-21 04:17:46 +08:00
6b101ff23b fix(errors): preserve caller abort reasons at admission (#5671)
* fix(errors): preserve caller abort reasons at admission

Keep the opaque caller reason on reverse, pcap, and SQLite cancellation,
and leave a recognized physical digest or command failure in place when a
late abort is also observed.

Fixes #5670

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

* fix(reverse): keep a foreign digest ABORT_ERR as an adapter failure

Treat ABORT_ERR as cancellation on the command boundary, where that code
already meant an aborted provider process. A digest or stat rejection with
the same code stays a physical adapter failure unless the caller signal
is aborted. Cover aborted command EACCES, ETIMEDOUT, and ABORT_ERR with
and without captured output.

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

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: morluto <morluto@users.noreply.github.com>
2026-10-21 04:16:44 +08:00
morluto 6389e50337 Merge remote-tracking branch 'origin/main' into codex/audit-continuation-20261020-refresh20 2026-10-21 03:57:03 +08:00
morluto d23be5c089 fix(web): preserve static WebMCP schema property coverage
Reuse the shared object-member owner for literal spreads, retain known property names under unresolved membership, and distinguish complete, partial, unknown, and absent projections. Keep historical coverage omission unchanged and preserve own keys across prototype setters.

Refs #5075
2026-10-21 03:56:35 +08:00
c0cbbf8d50 perf(ci): keep fast built shards under ten minutes (#5652)
* test(acceptance): split competing loader ownership across files

The loader matrix was one 578-second file, which set the floor for any
shard that received it. Three files keep every CLI and MCP case and let
workers overlap them.

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

* perf(ci): run eight fast built shards under a ten-minute budget

Charge each Vitest project by its resolved worker width so serial
process-boundary files spread as tail time. Fast feedback uses eight
built shards. The npm download cache stays; node_modules is too large
to ship as a shared artifact.

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

* fix(ci): keep each scheduled file on one worker lane

Dividing a file by maxWorkers dropped its own wall-time floor and could
pack a parallel file onto a serial tail. Place each file whole on a lane,
overlap peers only inside a sequence group, and add later groups after.

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

* docs(testing): separate the shard budget from workflow time

The ten-minute figure is the built shard job. Classification, build, and
the portable-test merge are measured separately on run 39451548945.

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

* docs(testing): match fast shard count to lane scheduling

Main still described five shards and half-duration weights. Fast
qualification uses eight shards, and each file keeps its full duration
on one worker.

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

* fix(ci): seed split loader durations from pre-split history

Timings published before the file split still name one 578-second path.
Until a new file appears in that artifact, share the recorded duration
by the pre-split case counts.

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

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: morluto <morluto@users.noreply.github.com>
2026-10-21 03:55:40 +08:00
morluto a7d13893a6 Merge remote-tracking branch 'origin/main' into codex/audit-continuation-20261020-refresh20 2026-10-21 03:50:44 +08:00
morluto 1bfa60ac20 fix(cli): preserve caller-selected JSON paths
Keep POSIX symlink-parent traversal at the shared JSON input boundary and forward the selected browser executable without a second lexical normalization. Verify Electron, browser, and Evidence-file consumers through registered commands and the built CLI.

Refs #5103
2026-10-21 03:50:14 +08:00
9d8e780332 ci: keep npm download caches from Turbo snapshot growth (#5655)
* ci: keep npm download caches from Turbo snapshot growth

Per-commit static and portable Turbo snapshots had filled the 10 GB Actions
cache, which is what makes warm npm ci cheap. Trusted static runs now keep two
generations and one download-cache writer per host.

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

* fix(ci): declare cache retention types for strict checks

Typecheck imports cache-retention.mjs and needs a sibling declaration.
The generation count stays module-private so knip does not report it.

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

* fix(ci): keep compatible caches and qualification read-only

Npm dedupe deletes a record only when another has the same key, version,
and ref. Cache deletion moves to a follow-up workflow so reusable
qualification can keep its read-only token.

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

* fix(ci): retain caches after failed default-branch runs

Static and build save Turbo snapshots before later lanes finish. Retention
now follows a failed, timed-out, or cancelled trusted run, not only a green one.

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

* test(cli): pass application trace input as a file

The CLI already reads workflow JSON from a path. Putting the full
application Evidence document on argv exceeds the runner argument limit.

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

* fix(ci): retain deep and Knip snapshots without deleting a repeated id

Fast and deep static checks publish rea-static-deep-v1 and knip-meta-v1
entries per commit. Retention prunes those families with the same two-generation
rule. A cache id listed twice is one record; inconsistent repeats abort
before any delete.

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

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: morluto <morluto@users.noreply.github.com>
2026-10-21 03:48:14 +08:00
40428da1dd perf(test): reuse one stdio MCP server per JavaScript application file (#5658)
* perf(test): reuse one stdio MCP server per JavaScript application file

Repeated analyzeJavaScriptDirectory MCP cases started a full server for
every case. Share one file-scoped server and compile cache so later cases
skip that startup while CLI cases still launch a fresh process.

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

* fix(test): close in-flight shared MCP startup with the file

File teardown waits for a stdio session that is still starting, skips a
server once close has begun, and still closes one registered during that
wait. The later-case reuse test can run without the earlier case.

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

* fix(test): keep a failed MCP handshake when cleanup fails

The shared session reports the startup error and carries the cleanup failure
as its cause. A cleanup that failed stays owned and is retried on close
without running a cleanup that already succeeded.

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

* fix(test): fail shared MCP cleanup when the rejection has no reason

A cleanup rejection of undefined still fails the drain and stays owned.
Close caches completion only after every retained cleanup succeeds, so a
later close can release it.

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

* fix(test): keep one MCP server while failed cleanup is retained

A later call retries retained cleanup before opening another server. The
new server starts only after that cleanup succeeds.

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

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: morluto <morluto@users.noreply.github.com>
2026-10-21 03:46:14 +08:00
morlutoandCursor Agent ef59174c1e fix(errors): name fixed allowances in resource-constraint recovery (#5633)
* fix(errors): name fixed allowances in resource-constraint recovery

Fixed parser, count, report, input, and capture refusals inherited
host-memory, wait-and-retry, or retained-Evidence advice. Producers now
attach allowance-specific recovery, and deterministic capacity caps are
not retryable. IL2CPP reader occupancy stays retryable capacity.

Refs #5556, #5405, #5456, #5459, #5460, #5504, #5505, #5506, #5537, #5628, #5630, #5631, #5632

* fix(il2cpp): keep a closing metadata reader non-retryable

close() stops admission before the active inspection finishes. A second
inspect in that window was projected as retryable capacity. Closing and
closed readers now report capability_unavailable. Occupied readers that
remain open stay retryable.

* fix(errors): align fixed-cap recovery with retained facts

IL2CPP report refusals keep observed source facts instead of denying Evidence. Command-output caps use the capture allowance. Ghidra seed recovery names the limit that was exceeded.

* fix(errors): call byte caps a byte allowance

WASM representation limits and the IL2CPP Ghidra seed-file cap are byte budgets. Their recovery no longer says the exceeded allowance is a fixed count.

* test(ida): keep unresolved procedure lookups non-retryable

Main now treats an unchanged invalid request as non-retryable. The IDA lookup tests follow that projection.

* fix(errors): qualify capture recovery for live output

A fixed capture allowance still rejects the same output. Live captures such as adb logcat can change between requests, so recovery no longer says every unchanged request fails again.

* fix(adb): keep a truncated live capture retryable

Device output can shrink between calls, so an adb capture that exceeds the allowance stays retryable. Deterministic capture and version-output limits stay non-retryable.

* fix(adb): keep truncated mutations non-retryable

Only live observations opt into retrying a capture that exceeded the allowance. Push, install, uninstall, and other mutations stay non-retryable.

* fix(errors): keep live capture recovery off deterministic limits

Artifact-derived capture allowances again say the unchanged request fails. Only volatile adb observations use the live-output sentence.

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2026-10-21 03:45:34 +08:00
53e0943223 fix(javascript): preserve path reasons, module coverage, and property provenance (#5651)
* fix(javascript): preserve selected path host resolution reasons

Classify ELOOP and ENAMETOOLONG while resolving the caller-selected input so the path and host code stay on the invalid-request issue.

Refs #5623

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

* fix(javascript): count summary coverage in module units

Use the module population for both examined and total counts. Non-module graph nodes remain in the application coverage section.

Refs #5464

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

* fix(javascript): connect direct property reads to return and argument flow

Record the unwrapped value expression and alias the object projection's read occurrence so a direct member read stays in backward provenance.

Refs #5585, #5648

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

* fix(javascript): count return wrappers against expression depth

Evaluate the original return expression so erased TypeScript and Flow wrappers stay inside the expression-depth budget. Keep the unwrapped node for identity and value ranges.

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

* fix(javascript): describe ELOOP as unresolved symlink traversal

A host can return ELOOP for a cycle or for a chain past its traversal limit. Keep the selected path and code without asserting that a cycle exists.

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

* test(javascript): skip symlink traversal fixtures on Windows

Keep the overlong-name case portable. Windows does not produce ELOOP for these fixtures, matching the other path-failure tests.

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

* fix(javascript): describe ENAMETOOLONG as a path or component limit

realpath reports that code for a long component and for a long full path. Keep the selected path and host code without claiming only a filename was too long.

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

* style(javascript): format return-depth and object-read changes

oxfmt rejected the return-depth test and the object-read index. No behavior change.

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

* test(ida): expect unchanged invalid requests to be nonretryable

The shared projector no longer marks invalid_request retryable. This lookup assertion was not updated with that contract.

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

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: morluto <morluto@users.noreply.github.com>
2026-10-21 03:45:10 +08:00
b718ad166d fix(doctor): reject unusable Kuna caches and invalid native settings (#5647)
* fix(kuna): reject dangling cache roots in doctor

Treat a cache path as creatable only when no directory entry occupies it.
A dangling symbolic link, including one hidden by a trailing separator,
fails the cache check and clears provider availability while the installation
check and caller-selected registration settings remain.

Fixes #5554
Fixes #5105

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

* fix(doctor): admit the same settings native analysis rejects

Project parseConfig failures into scoped engine readiness. Invalid provider
preferences, startup timeouts, and other shared settings now fail the
selected provider and the full audit, while a JEB-only timeout stays outside
that admission.

Fixes #5454

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

* fix(doctor): report an invalid log level from doctor

The dispatcher rejected REA_LOG_LEVEL before every command, so doctor never
emitted its readiness check. Doctor now runs and names that setting; other
commands still stop before execution.

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

* fix(doctor): require log-level failures in every scope

An invalid REA_LOG_LEVEL stops the executable and MCP server before a client
or skill can run, so those scopes now fail readiness too. Other native
admission failures stay limited to a selected provider.

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

* fix(kuna): strip only the platform cache separator

On Darwin and Linux a trailing backslash is a filename character. Doctor now
removes only the selected platform separator before inspecting the cache path.

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

* fix(doctor): fail every provider on an invalid Hopper timeout

REA_HOPPER_STARTUP_TIMEOUT_MS is rejected before any native engine starts.
A selected non-Hopper provider now includes the existing timeout check.

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

* style(doctor): format the Hopper timeout regression

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

* fix(doctor): fail client scopes on MCP startup errors

A registered client starts the MCP server, and parseConfig rejects every
native admission failure before that server starts. Client readiness now
includes those failures, not only REA_LOG_LEVEL.

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

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: morluto <morluto@users.noreply.github.com>
2026-10-21 03:44:49 +08:00
morluto 690fc51854 fix(browser)!: preserve unknown console timestamps
Retain absent or invalid Runtime timestamps as null and mark console event coverage incomplete. Preserve observed finite timestamps, including zero, and document the epoch-millisecond clock.

BREAKING CHANGE: console event timestamps may now be null when the protocol does not provide a finite number.

Refs #5656
2026-10-21 03:43:20 +08:00
23196daa11 fix(apple): read LC_LAZY_LOAD_DYLIB_INFO dependency payloads (#5650)
* fix(apple): read LC_LAZY_LOAD_DYLIB_INFO dependency payloads

Parse the linkedit install name for lazy-load info commands, resolve those
edges without traversing them, and stop inspect-macho from treating an otool
dependency list as exhaustive when that payload is omitted.

Fixes #5551
Fixes #5649

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

* fix(apple): bound lazy-load payload reads and image offsets

Reuse identical LC_LAZY_LOAD_DYLIB_INFO ranges, cap their combined size,
and reject flag or chain-start offsets outside the slice VM range.

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

* fix(apple): bound lazy-load string scans and contained reads

Reject payload strings that start in the 24-byte header, validate each
file-offset string once, and reuse payload bytes already read for a
containing range.

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

* fix(apple): reject cached lazy-load strings outside a payload

Remember each cached string's terminating offset and require that NUL to
fall inside the payload being decoded.

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

* fix(apple): bound lazy-load payload range lookups

Count containment checks across earlier payload reads and stop at the
existing 16 MiB work limit.

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

* fix(apple): count every lazy-info string reference

Charge repeated and overlapping LC_LAZY_LOAD_DYLIB_INFO scans against the
16 MiB validation budget, and stop a scan once that budget is exhausted.

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

* fix(apple): cap retained lazy-info dependency results

Stop after 1024 LC_LAZY_LOAD_DYLIB_INFO commands so a load-command region
full of identical commands cannot become a million traced edges.

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

* fix(apple): reject a lazy-load path NUL in the final byte

dyld stops the load-path scan before the payload's last byte, so a
terminator in that byte leaves the image malformed.

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

* fix(apple): keep may-be-missing lazy loads optional

A missing LC_LAZY_LOAD_DYLIB_INFO with the may-be-missing bit is weak, so the lazy finding says dyld continues on first use instead of failing.

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

* fix(apple): cap lazy-info symbol string references

A payload of distinct one-byte symbol strings stayed under the byte budgets while caching one entry per offset. A slice now stops at 65536 symbol references.

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

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: morluto <morluto@users.noreply.github.com>
2026-10-21 03:33:07 +08:00
0add6a5aae ci: split fast and deep static checks (#5634)
* ci: split fast and deep static checks

Fast PRs run typecheck, lint, and format before provider admission.
Knip, duplication, architecture guards, and Python stay on the full tier.
Shared-boundary fallbacks keep the Linux x64 baseline, and Turbo jobs
export remote-cache credentials when they are configured.

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

* ci: keep pull requests from writing the remote cache

Trusted default-branch runs still receive TURBO_TOKEN. Pull requests get an
empty token and remote read-only mode. Knip saves a per-commit cache key so
the next trusted full run can refresh the restored snapshot.

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

* ci: forward the release cache token and upload deep static reports

Release qualification now passes TURBO_TOKEN into the called workflow.
Labeled pull requests still do not. The deep static job records its task
context and uploads duplication reports after a failed gate.

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

* style: format the turbo cache policy test

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

* ci: keep fast checks exclusive and trusted cache writes explicit

Declare the static artifact name and context script so typecheck matches the
implementations. Lock check:ci:fast around dist, and publish the remote cache
only for release qualification or default-branch push, schedule, and dispatch.

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

* ci: record static task graphs without nesting the artifacts lock

Keep check:ci:fast locked, and point graph recording at the unlocked
check:ci:fast:tasks script so check:pr and check:test can record hashes
while they already hold that lock.

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

* ci: keep unauthenticated Turbo runs on the local cache

Open pull requests do not receive TURBO_TOKEN, so a remote read mode cannot
authenticate. Those runs now use the local cache. Trusted runs still publish.

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

* test(ida): treat unchanged invalid requests as nonretryable

Main already stopped marking invalid_request retryable. The pull request
merge runs that projector, while the IDA lookup tests still expected
retryable true. Match the merged behavior.

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

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: morluto <morluto@users.noreply.github.com>
2026-10-21 03:29:31 +08:00
morluto 0dbc046ca6 Merge remote-tracking branch 'origin/main' into codex/audit-continuation-20261020-refresh20 2026-10-21 03:28:13 +08:00
morluto 2a0410f171 fix(browser)!: bind session timelines to the observation window
Sample the browser monotonic clock with observation already subscribed, filter historical lifecycle and network facts, and preserve scope and termination handling when event time is unknown.

Bound pre-arm retention using transport-owned wire bytes. Fail closed when the relevant-event buffer fills, and keep unrelated protocol payloads outside that budget.

BREAKING CHANGE: observe_web_session timeline timestamps can be null when the producer supplies no comparable timestamp. Consumers must handle unknown time explicitly.

Refs #5609
2026-10-21 03:27:31 +08:00
gensokyo-dev 4bfdadb7de perf(frida): add retained summaries for session status (#5661)
Add opt-in Frida session status summaries linked to complete retained Evidence. Share canonical session facts across contracts and preserve observed state, source identity and capture truncation.

Validated with native SDK/client workflows, default delivery compatibility, lifecycle and provenance tests. The completed large-history task uses 50.9% fewer estimated request tokens; no general CPU or latency improvement is claimed.

Fixes #5640
2026-10-21 03:21:30 +08:00
morluto 1629266e3c fix(errors): preserve canonical failures and correction details
Retain process input and unexpected failure diagnostics through the shared error projector. Preserve filesystem causes and firmware validation issues, and mark unchanged invalid requests nonretryable.

Use typed stat settlement and parse unknown command failures without losing null or undefined rejection causes.

Refs #5397, #5401, #5402, #5642
2026-10-21 02:03:17 +08:00
morluto b3aadaf474 fix(il2cpp): preserve recognized truncated metadata prefixes 2026-10-21 01:47:09 +08:00
morluto eb7acc70bf fix(native): verify signature snapshots and frame unsigned diagnostics
Bind and recheck the digest-verified snapshot used by codesign, retaining cleanup ownership when its identity changes. Classify unsigned only from the exact operand-framed diagnostic so echoed caller paths cannot turn unrelated failures into signature facts.

Closes #5636
2026-10-21 01:35:44 +08:00
morluto 998882045b Merge remote-tracking branch 'origin/main' into codex/audit-continuation-20261020-refresh20 2026-10-21 01:31:57 +08:00
morluto eb41f358cf fix(model): retain completed GGUF facts on interrupted reads
Keep parser-owned completed metadata and tensor declarations as typed partial Evidence on cancellation or physical read failure. Preserve unknown final identity and digests, exact caller inputs, primary failures, and retained cleanup owners; support inline and retained MCP error delivery.

Closes #5625
2026-10-21 01:31:17 +08:00
gensokyo-dev 17f581ff75 perf(reverse): add retained GDB and objdump summaries (#5614)
Add opt-in retained GDB and objdump summaries using the shared analysis-view layer. Preserve default complete Evidence, ordered records, provenance and completion facts.

Validate real Bash disassembly, whole-client context costs, CLI publication and cleanup; retain the original measured cohorts and explicit coverage limitations.

Closes #5584.
2026-10-21 01:20:52 +08:00
morluto edbd291a28 Merge remote-tracking branch 'origin/main' into codex/audit-continuation-20261020-refresh20 2026-10-21 01:13:13 +08:00
morluto 2b263a7fe9 fix(native): retain Mach-O facts when dyld rejects missing UUID
Admit only the corroborated missing-UUID validation diagnostic from header-stripped dyld stdout. Preserve readable load commands and nm exports with unknown dyld coverage, and keep unrelated process and tool failures typed.

Closes #5606
2026-10-21 01:12:51 +08:00
gensokyo-dev d98fc3859d test(evaluation): include native preparation in test deadlines (#5627)
Derive Darwin subprocess test budgets from the bounded ownership compiler budget while preserving short command deadlines. Always join the retained command scope after test failures.

Refs #5094.
2026-10-20 17:10:11 +00:00
morluto d0eca0517e Merge remote-tracking branch 'origin/main' into codex/audit-continuation-20261020-refresh20 2026-10-21 01:02:22 +08:00
Kaoru0822andClaude Opus 5.5 85b379c4ca test(evidence): assert the exact cancellation reason after staging cleanup fails (#5601)
52d1a72af made a cancelled write whose staging cleanup also fails carry the
caller's exact abort reason as its cause, with the cancellation retained as
cleanupPrimary and the staging directory as cleanup state. The boundary test
still expected the old wrapped cause, so it failed on main and in every PR
built on it; its finally block then masked the assertion with a cleanup
error. Assert the current contract.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 10:01:56 -07:00
morluto 22c84ce64a fix(hopper): recognize stripped Objective-C class labels
Classify legacy class-object names at the Hopper boundary and carry the typed fact into class discovery. Preserve exact labels, literal filtering, unrelated row metadata, and raw replies; derive classification from source text rather than incoming tags.

Closes #5617
2026-10-21 01:01:42 +08:00
morluto dd0015aaf1 fix(ida): own registration reads and bind selected configuration
Read bounded regular registrations asynchronously with retained descriptor cleanup. Carry the selected parsed configuration through provider factories and restoration, preserve exact cancellation reasons, and share workspace-parent resolution with doctor checks.

Closes #5561

Closes #5559

Closes #5604
2026-10-21 01:00:00 +08:00
morluto 7d6f90485e Merge remote-tracking branch 'origin/main' into codex/audit-continuation-20261020-refresh20 2026-10-21 00:53:09 +08:00
morluto 04ae07ab8c fix(kuna): bind version probes to stable executable identity
Compare executable fingerprints before and after the owned version probe, and capture the script prefix from the final hash stream. Preserve partial facts and failed-close ownership without claiming process interruption from a snapshot.

Closes #5611
2026-10-21 00:52:53 +08:00
gensokyo-dev 98f4072f1f feat(model): inspect GGUF and safetensors declarations (#5603) 2026-10-21 00:50:50 +08:00
morluto 5b676ba9f3 Merge remote-tracking branch 'origin/main' into codex/audit-continuation-20261020-refresh20 2026-10-21 00:32:49 +08:00
morluto 14cd78c322 fix(apple): retain unsupported NIB connectors in connection coverage
Recognize AppKit binding connectors as explicit unknown connection facts and
report partial semantic coverage rather than silently omitting them. Share
format-aware connector admission across keyed archives and NIBArchive.

Make the keyed archive table own parsed class and superclass facts for names,
array hierarchy traversal, inherited connectors, and class-table projection.
Preserve malformed class evidence and exclude unrelated custom class names.

Validation: 84 focused tests passed, including compiled CLI, filesystem,
MCP summary, and retained views; check:fast and test discovery passed.
Independent source review found no blocker. Production CLI and stdio MCP
replayed a read-only copy of Terminal 2.15's picker NIB: 13 connections with
partial unsupported-type coverage, versus baseline 9 with complete coverage.
Advertised schemas and retained coverage results validated with the SDK/Ajv.
No app, analysis target, or optional deep provider was executed.

Fixes #5590
2026-10-21 00:31:00 +08:00
gensokyo-dev 8716b52411 fix(evaluation): prepare and retain native ownership helpers (#5615)
Prepare native process ownership outside short command deadlines. Reuse the shared cleanup adapter and resource scope for direct/nested preparation failures and command cleanup.

Fixes #5094.
2026-10-20 16:29:46 +00:00
gensokyo-dev a35623707e fix(mcp): guide client and provider timeout recovery (#5608)
Teach client deadline recovery before REA results can be discarded, distinguish
producer-owned server budgets, and preserve Evidence/cleanup uncertainty.
Include actual Codex CLI recovery probes and CLI/MCP regression coverage.

Fixes #5598

Assisted-by: Kogasa (gensokyo-skills:kogasa-surprise-testing)
2026-10-21 00:16:44 +08:00
gensokyo-dev 2a6fc712eb fix(evaluation): join owned command cleanup before removing files (#5613)
Use shared owned command supervision for all evaluation helpers, join streamed output, and retain/retry incomplete cleanup before deleting run files.

Fixes #5094.
2026-10-20 16:16:29 +00:00
morluto 506400748e fix(electron): count bridge listener registrations by source callsite
Keep directly observed renderer listeners singular when bridge traversal
reaches them, while retaining separate paths for every exposed bridge member.
Preserve separate physical callsites and on/once operation identities.

Validation: 190 focused tests passed, including public CLI and stdio MCP
analysis and incoming feature tracing. Typecheck/lint and discovery passed.
The inert fixture was analyzed statically; no Electron target was executed.

Three alternating synthetic CLI tasks showed overlapping wall times (median
1.825s baseline, 1.808s current). Corrected bridge provenance increases serialized
task payload; no speed or context reduction is claimed. Actual model tokens
and full process-family CPU/peak memory were not measured.

Fixes #5586
2026-10-21 00:14:11 +08:00
morluto 57d64b6ba5 Merge remote-tracking branch 'origin/main' into codex/audit-continuation-20261020-refresh20 2026-10-21 00:09:56 +08:00
morluto ff78165978 fix(kuna): own version-probe processes through output and cleanup
Use the shared command supervisor for installation probes and retain exact
process cleanup owners through doctor, profile resolution, and session health.
Preserve exit status, bounded captured output, cancellation reasons, and live
cleanup facts. Block subsequent inspection until retained cleanup is retried.

Generalize the existing inspection resource scope and migrate its consumers,
including verifier scripts; remove the verifier's duplicate raw version probe.

Validation: 116 distinct focused tests passed, including built CLI doctor with
inherited output, cancellation/finalization, cleanup retry, and fake-provider
verifier workflows. Build, check:fast, and test discovery passed. No real Kuna
distribution or analysis target was executed.

Synthetic Darwin inspection timing: first-call median 707ms versus 5.79ms;
same-process repeat 37.9ms versus 3.08ms. First use includes ownership-helper
preparation. Family CPU and actual peak family memory remain unmeasured.

Fixes #5557
2026-10-21 00:09:34 +08:00
gensokyo-dev c3d7d0aafc fix(browser): honor long passive observation windows (#5605)
Reuse the cancellable segmented wait and settle observation termination through one cleanup owner. Preserve caller cancellation and release timers/listeners across early target end.

Fixes #3929.
2026-10-20 15:56:15 +00:00
morluto 90e433df06 fix(swift): retain uncertainty for unsupported nested identifiers
Stop inheriting an outer nominal category when a zero-prefixed identifier cannot be decoded. Preserve exact manglings in unresolved results under class and enum filters while retaining ASCII nominal controls.

Validated 39 source and in-memory MCP tests, typecheck/lint, and 22 compiler-produced code symbols from an inert Swift 6.3.2 object. No real Hopper inventory or target execution. Fixes #5553.
2026-10-20 23:41:16 +08:00
morluto 0ba4d6f43a fix(native): bind dispatch snapshots to the local reader profile
Retain native-only dispatch workflows without requiring a deep provider profile. Validate exact target, operation, and current reader bindings during open, import, recording, and export; preserve selected deep bindings and caller limits.

Validated public CLI and stdio MCP save/reopen, exact CLI replay, changed-limit cache partitioning, baseline failure and candidate success on an Apple-clang fixture, snapshot lifecycle regressions, typecheck and lint. No target or deep engine execution. Fixes #5529.
2026-10-20 23:31:45 +08:00
morluto 080c0e2b2b Merge remote-tracking branch 'origin/main' into codex/audit-continuation-20261020-refresh20 2026-10-20 23:15:43 +08:00
morluto 9eec57b469 fix(native): report record-local Objective-C budget omissions
Have method, property, ivar, and adopted-protocol list owners flag real budget omissions on returned class, protocol, and category records. Preserve known empty lists and existing decode reasons.

Validated 59 source tests, two built CLI controls, five static inspections of an Apple-clang fixture, and typecheck/lint. The target was never executed. Fixes #5543.
2026-10-20 23:15:05 +08:00
morluto 8d198d6ec7 fix(javascript): preserve unavailable scope uncertainty in comparisons
Classify wholly unknown or unavailable node observations at the version-item owner and retain their limitations across matched and unmatched comparisons. Preserve positive mixed observations and genuine additions/removals.

Validated source regressions and public CLI/stdout MCP workflows in the 103-test focused gate; typecheck and lint pass. Fixes #5534.
2026-10-20 23:14:47 +08:00
gensokyo-dev c2858d2aa0 perf(go): summarize build identity and query retained metadata (#5524)
Add opt-in Go build summaries and selected retained metadata pages while preserving complete Evidence and source identity.

Validated with 258 focused tests, full static/docs checks, real CLI/SDK workflows and native metadata oracles. CI baseline remediation assertions reproduce on unmodified main; fail-fast cancels two other shards. Owner authorized bypass with those coverage limits recorded in PR #5524.

Fixes #5499.
2026-10-20 23:09:10 +08:00
gensokyo-dev e52a71602d test: stabilize worker cancellation and trailer regressions (#5497) 2026-10-20 23:06:09 +08:00
HawkForge 8443692e2b fix(javascript): keep overflowing timer delays unknown (#3456)
Normalize non-finite timer literals to the existing unknown value at the semantic fact producer. Keep timers, source provenance, binding resolution and unrelated functions; preserve all finite delay values.

Qualify shared CLI/MCP behavior with precise regression oracles, 111 tests, actual source fixtures, real-package summary/fact/Evidence workflows and current-main integration.

Fixes #3400.
2026-10-20 15:02:38 +00:00
morluto 7b2087e449 test(kuna): separate composed doctor startup from cancellation timing
Allow the preceding Ghidra ownership preparation before waiting for the gated Kuna child. Keep the two-second cancellation join, exact reasons, reaping and cleanup assertions unchanged.

Refs #5568. A controlled startup delay reproduces the old failure; disabling cancellation forwarding still fails the retained join assertion. All 86 focused setup, doctor, helper and CLI checks passed on pinned Node 24.18.0/macOS arm64.
2026-10-20 22:57:21 +08:00
morluto f5d1012fab fix(setup): advertise only providers with passing readiness checks
Keep installation availability and full doctor inspection facts separate from the setup ready-provider list. A blocked Kuna cache no longer advertises deep analysis; passing unverified-version notes remain usable.

Refs #5105. All 16 focused setup and public CLI dry-run controls passed; typecheck and lint passed. Tests use a scripted Kuna version fixture and do not establish real-engine behavior. The pre-existing doctor cancellation startup-poll failure is tracked separately.
2026-10-20 22:49:13 +08:00
morluto d2a3269d0c Merge remote-tracking branch 'origin/main' into codex/audit-continuation-20261020-refresh20 2026-10-20 22:39:34 +08:00
morluto fd7da77fc0 fix(hopper): verify bookmark names and clear existing labels
Use the public rename API for an explicit empty existing name, retain omitted names and raw listing values, and verify fresh unnamed creation and explicit mutations by readback. Preserve independent verifier restorations and their primary failures.

Refs #5475. Focused bridge regressions and the complete real-Hopper suite passed on macOS arm64 with Hopper 6.6.0-demo 42.6316032. The verifier used a temporary unavailable Ghidra override to meet its sole-candidate prerequisite; reporter Hopper 6.1 remains unverified.
2026-10-20 22:39:23 +08:00
morluto 44521d3d75 refactor(macho): share thin header CPU interpretation
Give partial thin-header facts one domain owner and reuse them for standalone target admission and Mach-O load-command inspection. Preserve each consumer endianness policy and header/command checks; remove the FAT-only parser names.

Refs #5542. All 246 focused checks and six public CLI archive/image controls passed; typecheck and lint passed.
2026-10-20 22:37:25 +08:00
Sagar Kewat eac296a7da fix(browser): reject overflowing scenario timers (#3515)
Reject browser scenario waits and action deadlines beyond the underlying signed 32-bit timer interval in the shared input contract. Preserve maximum legal values and omitted deadlines; prevent unsupported input from opening a browser.

Strengthen the rejection oracle and verify public CLI/MCP canonical validation in both schema profiles, actual Chromium timing/cancellation/cleanup, Evidence export and compatibility with current main.

Fixes #3392.
2026-10-20 14:32:54 +00:00
gensokyo-dev 55cce6fc11 test(cli): retain recovery guidance in filtered failures (#5536)
Preserve typed input-contract recovery and source diagnostics through filtered CLI output in JSON, TOON, YAML and Markdown. Update legacy remediation-text assertions introduced by the concurrent main update.

Refs #5431 and #5470.
2026-10-20 22:26:57 +08:00
morluto 57e76599c0 Merge remote-tracking branch 'origin/main' into codex/audit-continuation-20261020-refresh20 2026-10-20 22:21:13 +08:00
morluto 498a667234 fix(javascript): retain source map section paths on declarations
Carry the parsed section path onto retained source nodes and contains edges. Refs #5466; validated with the same 50 focused source-map checks.
2026-10-20 22:19:35 +08:00
morluto 1195929fa5 test(browser): synchronize cancellation before the worker exits 2026-10-20 22:19:18 +08:00
morluto c4007f4542 Merge remote-tracking branch 'origin/main' into codex/audit-continuation-20261020-refresh20 2026-10-20 22:19:10 +08:00
morluto a74d859a19 fix(evm): probe the selected limiter before advertising availability
Share asynchronous executable selection between discovery and execution. Recheck it for default Linux x64 sessions, preserve caller-injected service policy and propagate cancellation through pending probes.

Refs #5086. Focused owner, inventory and MCP controls passed; Linux-specific discovery and execution cases were skipped on Darwin arm64 and remain unverified.
2026-10-20 22:18:52 +08:00
morluto 2b2e362a00 fix(javascript): retain partial indexed source map facts
Share section classification across declaration analysis and mapping parsing. Preserve external URLs, malformed section reasons, section paths and safe sibling declarations without fetching nested maps.

Refs #5466. All 50 focused tests passed, including real CLI and stdio MCP regressions; typecheck and lint passed.
2026-10-20 22:18:23 +08:00
morluto 497037e6e3 fix(macho): validate FAT slice format and CPU identity
Use observed thin-header facts before target architecture admission, reject archive payloads, and retain far-offset root inventory through bounded reads. Retry complete bounded FAT table reads.

Refs #5462, #5542. Focused native/filesystem checks passed (245 plus 70 after the table retry). Public MCP inventory parity and unchanged token estimates verified; no external analysis engine execution claimed.
2026-10-20 22:18:03 +08:00
morluto fa36657251 test(ci): deduplicate browser lifecycle fixtures and cover export ordering 2026-10-20 22:05:55 +08:00
morluto d43d3568f2 fix(evm): retain cancellation through final cleanup 2026-10-20 22:05:55 +08:00
morluto 25d3805e7f Merge remote-tracking branch 'origin/main' into codex/audit-continuation-20261020-refresh20 2026-10-20 22:04:00 +08:00
morluto 82902465c4 docs(errors): describe live JSON heap admission
Replace the superseded fixed 64 MiB process-input refusal with the shared reader diagnostic and recovery text. Restore CSV field boundaries in the adjacent import-limit catalogue entry.

Refs #5077. Verification is source consistency and CSV parsing, without a new large-file runtime claim.
2026-10-20 21:59:05 +08:00
Nitin Kushwaha cf5194f68e fix(firmware): separate analysis and publication deadlines (#4602)
Separate extraction/inventory and caller-selectable publication budgets in the shared firmware workflow. Preserve phase, selected timeout, verified progress, caller cancellation identity and committed observations through cleanup. Enforce monotonic expiry even before timer callbacks run.

Validated with 101 targeted tests, full static checks, documentation checks and real OpenWrt CLI/MCP workflows with digest and cleanup verification. Existing main CI failures are documented separately.

Fixes #4176.
2026-10-20 13:57:20 +00:00
JANG BYUNGKUN 52d1a72af0 feat(il2cpp): link native methods and export Ghidra seeds
Link IL2CPP metadata names to native addresses and types through shared CLI/MCP workflows, with bounded queries and atomic Ghidra seed export.

Preserve the original implementation by 8rulerstar and follow-up correctness, resource, provenance, and CI dependency repairs. Verified against real Windows/Android artifacts, independent references, actual Ghidra, and focused CLI/MCP tests.

Fixes #5112.
2026-10-20 21:56:54 +08:00
gensokyo-dev e50a271c72 fix(mcp): guide agents through tool error recovery (#5470)
Add shared, typed input-contract and timeout-state recovery guidance. Normalize SDK input failures within the actual MCP wire budget, preserve full diagnostics and protocol metadata, and retain original Evidence when normalized delivery would exceed the budget.

Fixes #5431.
2026-10-20 21:45:05 +08:00
Syed Rishad c374c2fe72 fix(ghidra): preserve partial dossiers on decompiler failure (#4650)
Preserve listing-backed facts and explicit unavailable decompiler facets when one function fails. Retain Evidence while excluding transient failures and derived workflows from snapshot reuse; keep healthy cache reuse and owned cleanup semantics.

Fixes #4644.
2026-10-20 12:08:08 +00:00
morluto 0c440bfc07 fix(cli): match listener recovery guidance to system failure
Classify listener errno and lifecycle phase at the transport owner. Preserve native cause and unknown codes while distinguishing host lookup, unavailable address, occupied port and permission recovery.

Refs #5455.
2026-10-20 19:54:56 +08:00
morluto a4dbb36d8e fix(javascript): retain uncertainty for missing source map mappings
Share the required mappings field contract and retain readable declarations across malformed indexed leaves while marking graph coverage partial.

Refs #5469.
2026-10-20 19:54:33 +08:00
morluto 0e83d92c58 fix(mcp): advertise existing absolute path constraints
Reuse the schema-visible host path owner across session, firmware, managed, browser, Electron and Evidence-file inputs. Keep existing runtime and CLI path semantics.

Refs #5493.
2026-10-20 19:54:14 +08:00
morluto ee12d517a2 fix(prompts): describe crash document without provider assumptions
Refs #5474. Use the existing provider-neutral document selector wording; public prompt list/render tests passed without starting providers.
2026-10-20 19:39:31 +08:00
morluto 9cf7a7cb0d fix(protobuf): preserve partial facts for malformed web trailers
Parse gRPC-Web trailer blocks with typed failures, enforce field values and lower-case names, and propagate unexpected decoder failures. Retain earlier fields and raw capture bytes across binary and text framing.

Refs #5386. Related control-value fix: #5422.
2026-10-20 19:39:18 +08:00
morluto 00a055be0f chore: merge latest main before publishing audit fixes 2026-10-20 19:29:26 +08:00
morluto 22278b20c0 chore: merge main into audited lifecycle fixes 2026-10-20 19:28:09 +08:00
morluto 3cf6206614 fix(mcp): require absolute evidence bundle paths 2026-10-20 19:26:17 +08:00
morluto 4714c7eaa3 fix(cli): preserve typed failures through output filters 2026-10-20 19:26:08 +08:00
morluto cec8f1e8f3 fix(providers): offer only admitted ambiguity choices 2026-10-20 19:25:59 +08:00
JANG BYUNGKUNandgensokyo-dev 21e0c580e7 feat(il2cpp): read member names, string literals and metadata versions 24 and 27 (#5326)
* feat(il2cpp): read member names, string literals and metadata versions 24 and 27

Adds field, property, event and parameter names with their owners, managed string literals, and the version 24 and 27 header layouts to inspect_il2cpp_metadata, with filtered pages for the new collections. Member decoding yields between row batches like the other row readers.

* fix(il2cpp): bound metadata expansion and query parameter owners

* test(il2cpp): cover nonempty property and event tables

---------

Co-authored-by: gensokyo-dev <gensokyo-dev@users.noreply.github.com>
2026-10-20 19:21:42 +08:00
morluto f01dccab77 fix(javascript): retain cancellation after observed worker failure 2026-10-20 19:13:06 +08:00
morluto 5f0bc86d1a docs(ko): describe update alias and bounded output 2026-10-20 19:05:50 +08:00
morluto f69332c94c test(process): bind capture watchdog cleanup to test lifetime 2026-10-20 19:05:44 +08:00
morluto 2e03251b61 fix(zip): retain raw filename provenance for legacy decoding 2026-10-20 19:05:38 +08:00
morluto 108c0e7e49 fix(macho): share width-specific load command alignment 2026-10-20 19:05:31 +08:00
morluto 266dd1d958 fix(go): reuse PE content spans for build metadata search 2026-10-20 19:05:24 +08:00
morluto cac12ae334 fix(workers): retain observed protocol failures across cancellation 2026-10-20 19:05:00 +08:00
morluto 861c11ec77 fix(evidence): preserve execution profiles in composed observations 2026-10-20 19:04:52 +08:00
morluto 9051809343 fix(adb): preserve empty string activity extras 2026-10-20 18:38:13 +08:00
morluto 9db5f1b276 fix(apple): keep empty asset catalog pages complete 2026-10-20 18:38:05 +08:00
morluto 3a9176098d fix(javascript): preserve observed worker failure ordering 2026-10-20 18:38:00 +08:00
morluto 483678bf9f fix(managed): retain operand-free instruction anchors 2026-10-20 18:37:56 +08:00
052956af34 fix(doctor): validate Hopper startup timeout in readiness checks (#3755)
Fixes #3306. Reuse the runtime timeout parser in doctor, preserve scope semantics and separate setting validation from provider configuration.

Co-authored-by: xiajiahang <xiajiahang82@gmail.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-20 10:32:12 +00:00
gensokyo-dev aa314ab682 ci(windows): run environment registration regressions (#5409)
Retain 13 Windows-only environment/setup regressions in the curated native lane. Select Windows qualification for both their three test files and their client configuration, registration and DSH production owner families. Guard membership and actual Git-diff classification; qualify all 60 target cases on hosted Windows.
2026-10-20 10:18:17 +00:00
gensokyo-devandKaoru0822 c23e161629 perf(mcp): record catalog identities for all schema profiles (#5415)
Share compiled schema conversion while recording all four built-in catalog profiles. Preserve canonical hashes, output validation and custom-budget fallback. Refs #723.

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-20 10:09:28 +00:00
gensokyo-dev 1417bf943b feat(native): match retained function inventories across builds (#5390)
Match retained native inventories through shared CLI/MCP workflows with conservative symbol/byte correspondence, unresolved structural candidates, complete Evidence provenance and bounded paged reads.

Closes #5343.
2026-10-20 09:48:52 +00:00
ShuaixinHuang e84468c0bb fix(setup): preserve Windows environment override identity (#4946)
* fix(setup): preserve Windows environment override identity

* fix(setup): preserve selected platform and YAML alias metadata

* fix(setup): reconcile layered environment and projected YAML identities
2026-10-20 17:25:51 +08:00
ShuaixinHuang afde435c94 test(setup): use native Windows launcher in layered OpenCode fixture (#4947) 2026-10-20 17:25:38 +08:00
ShuaixinHuang e9fc8fb30c fix(javascript): preserve runtime path bytes and Electron ownership (#4941)
* fix(javascript): preserve literal percent runtime paths

* fix(javascript): ignore resolve prefixes before the final anchor

* fix(electron): retain application roots for loadFile declarations

* fix(electron): resolve loadFile from enclosing application package

* fix(electron): retain outer application root across package scopes

* fix(electron): derive application ownership from inventoried packages

* fix(electron): retain resolved package entrypoint ownership

* fix(electron): preserve competing application ownership

* fix(electron): retain invalid and omitted runtime ownership

* fix(electron): preserve omitted module ownership and runtime paths

* fix(electron): preserve proven roots before confining page paths

* fix(electron): retain possible owners behind missing imports

* fix(electron): distinguish absent imports and owned page candidates

* fix(electron): keep unavailable JSON out of owner gaps

* fix(electron): retain exact loader formats and default entry status

* fix(electron): retain unavailable entrypoint ownership candidates

* test(ghidra): preserve Windows Node probe prerequisites

* test(ghidra): assert cancellation during profile shutdown

* fix(electron): separate examined imports from frontend ownership

* fix(electron): retain unavailable directory entrypoint owners

* test(jeb): retain Linux ownership prerequisite in isolation

* fix(javascript): retain unchecked loader ownership and literal main paths

* fix(javascript): preserve runtime paths and call-site module aliases

* fix(javascript): bound module alias traversal and preserve projections

* fix(javascript): retain host module and nested artifact path boundaries

* fix(javascript): preserve ESM and archive module loader provenance

* test(javascript): distinguish unmapped containers from missing exports

* fix: retain default module aliases and Electron builtin imports

* fix: preserve host file URL and inward ASAR ownership

* fix: preserve CommonJS file URL classification in CI fixtures

* fix: retain bound module loaders and Windows entrypoint uncertainty

* fix: retain ASAR uncertainty and invalidate changed Electron app roots

* fix(javascript): retain ownership uncertainty across loader boundaries

* fix(javascript): preserve loader sources and bound alias queries

* fix(javascript): retain destructured loader sources

* fix(javascript): preserve loader uncertainty at resolution boundaries

* fix(javascript): preserve callable and package launch ownership

* fix(javascript): preserve captured values across member reads

* test(process): assert tagged filesystem cancellation failures

* fix(javascript): preserve inventory bases and invocation read timing

* fix(javascript): preserve ownership across indirect and unread loads

* test(javascript): order fixture directory creation on Windows

* test(javascript): share ordered filesystem fixture writer

* fix(javascript): unify loader provenance and package validation

* fix(javascript): preserve captured Node require loader values

* fix(javascript): preserve loader invocation authority

* fix(javascript): verify generic loader invocation authority

* fix(javascript): preserve conditional loader method alternatives

* fix(javascript): require proven factories for source association

* fix(javascript): honor replaced Electron app path setters

* fix(javascript): prove path and assignment source ownership

* fix(javascript): retain native delegate eligibility through shared writes

* fix(process): settle stale baseline errors after confirmed absence

* fix(javascript): preserve assignment and path boundary evidence

* fix(javascript): prove source-directory resolve anchors

* fix: preserve application path provenance and portable schemas

* fix(javascript): retain CommonJS exported setter effects

* fix: isolate Linux identity observations and retain native module exports

* fix(javascript): preserve published app and helper provenance

* fix(javascript): normalize typed stores and native path fixtures
2026-10-20 17:25:19 +08:00
morluto c284168c4b fix(objdump): scope debug-link limitations to DWARF requests 2026-10-20 17:18:21 +08:00
morluto be20ac3740 fix(source-maps): preserve failure priority and cancellation provenance 2026-10-20 17:18:20 +08:00
morluto cc73587ec4 fix(lldb): preserve observed failures through interrupted cleanup 2026-10-20 17:18:20 +08:00
morluto 98edca9f96 fix(pwndbg): clarify debugger context output limit message 2026-10-20 17:18:19 +08:00
morluto 98a5e56380 docs(ghidra): explain server-wide analyzer override scope 2026-10-20 17:18:19 +08:00
morluto 7c6142efdc fix(ghidra): retain stable Homebrew installation aliases 2026-10-20 17:18:18 +08:00
morluto 562dab00c5 fix(mcp): retain protocol envelopes when replacing oversized errors 2026-10-20 17:18:18 +08:00
morluto 3b02179560 fix(javascript): preserve uncertain call targets across activation boundaries 2026-10-20 17:18:18 +08:00
morluto 599fdac029 fix(browser): stop late navigation before releasing attached sessions 2026-10-20 17:18:17 +08:00
morluto 696832caa3 fix(gdb): gate readiness and retain caller cancellation through cleanup 2026-10-20 17:17:16 +08:00
morluto 459a356edb fix(evm): preserve cancellation and exact cleanup ownership 2026-10-20 17:17:16 +08:00
morluto 419502c8d0 fix(pcap): preserve cancellation and endpoint evidence through cleanup 2026-10-20 17:17:15 +08:00
morluto 1006714271 fix(sqlite): retain primary outcomes through cleanup retries 2026-10-20 17:17:15 +08:00
morluto e367750b1b fix(process): preserve observed command failures and native ownership 2026-10-20 17:17:14 +08:00
morluto cf3288d8cf fix(native): preserve Mach-O container framing across inspection 2026-10-20 17:16:44 +08:00
Liu Jieandgensokyo-dev d532b18123 fix(doctor): propagate Kuna cancellation and preserve partial observations (#5241)
Fixes #5106. Propagate the owned inspection signal, preserve typed cancellation and earlier failures, and retain completed installation observations through exact-owner cleanup retries.

Co-authored-by: Liu Jie <maxazure@gmail.com>
Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
2026-10-20 09:15:09 +00:00
吴佳浩AlbenandXiaoBinGan 074768901f fix(pwndbg): separate the words in the debugger-context limit message (#5372)
The 64 MiB reply-budget refusal rendered as "exceeds64 MiB" because the
message word and the limit were joined. The pwntools sibling names the
same budget as "exceeds the 64 MiB reply budget"; use that wording.

The output-limit path had no producer regression, so the pwndbg suite
gains one that drives the real 64 MiB boundary, and the suite is now part
of test:python instead of running only when invoked by hand.

Co-authored-by: XiaoBinGan <XiaoBinGan@users.noreply.github.com>
2026-10-20 16:12:55 +08:00
gensokyo-devandKaoru0822 2538214e2c feat(analysis-view): add native inventory summaries and pages (#5378)
Add opt-in summaries and stable selected-field pages for native inventories through the shared CLI/MCP Evidence layer. Preserve complete source access, row identity, provenance, string qualifications and unknown discovery coverage.

Record real true/jq workflows and portable old-Evidence checks, including tokenizer proxy reductions, additional call/schema costs and observed latency/memory regressions.

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-20 07:59:43 +00:00
gensokyo-dev b5d540f3a2 test(capture): split protobuf MCP acceptance scenarios (#5379)
Separate independent exclusion, partial-source, credential-preflight and descriptor-failure workflows so their costs do not accumulate under one test deadline. Reuse one worker-owned MCP connection with explicit awaited teardown, preserving sensitivity isolation and retained-Evidence checks.

Assisted-by: Reimu (gensokyo-skills:reimu-incident-triage)
2026-10-20 07:54:47 +00:00
gensokyo-devandakushonkamen 4eaf3a117b fix(mcp): retain protocol framing for oversized tool errors (#5371)
Preserve the SDK resultType when bounded error recovery replaces an oversized response. Verify both revisions through the public serving entry, retained Evidence and same-connection recovery.

Refs #5273

Co-authored-by: akushonkamen <akushonkamen@163.com>
2026-10-20 07:31:02 +00:00
0134732fcd feat(capture): decode selected protobuf and gRPC bodies (#5031)
Retain exact bounded wire facts and optional supplied-schema interpretations for selected historical captures. Share CLI/MCP views and preserve Evidence, partial coverage, credential exclusions and source provenance.

Fixes #4986

Co-authored-by: AayushJB03 <aayushjb14@gmail.com>
Co-authored-by: gensokyo-dev <gensokyo-dev@users.noreply.github.com>
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-20 07:10:54 +00:00
gensokyo-dev 799e8265b2 fix(verification): retain Hopper ownership evidence across owner exit (#5357)
Keep one ProcessOwnershipEvidenceOwner for each graceful/SIGKILL session and share it across active and terminal scoped scans. Require live token-verified evidence before stopping the owner, and retain structured cleanup failure diagnostics.

The real Ubuntu 24.04 Hopper clean-setup/CLI/MCP job passes, including owner exit, target reacquisition, idempotence and zero remaining descendants. Local verification passes 87 CI guards, 51 ownership controls, discovery of all 1,714 test files and full static checks.

Assisted-by: Reimu (gensokyo-skills:reimu-incident-triage)
2026-10-20 05:52:06 +00:00
gensokyo-dev 4668cea246 fix(ghidra): explain server restart for timeout recovery (#5356)
Explain process-scoped startup settings in shared CLI/MCP timeout guidance and the bundled skill. Preserve Evidence and cleanup before restarting REA, reconnect MCP, and allow enough client request time.

Validated with focused tests, static/docs/package checks, real Ghidra CLI/MCP recovery, and a completed 591-second EXE analysis under CPU and memory limits.
2026-10-20 05:49:00 +00:00
gensokyo-devandKaoru0822 58d6043303 perf(ghidra): omit redundant raw results (#5355)
Keep ordinary Ghidra facts in normalized Evidence without repeating complete raw replies. Preserve distinct overview, attestation, file-offset and pre-overlay observations through shared CLI/MCP execution.

Record real application measurements, equivalent selected facts, response admission and explicit latency/coverage limits.

Fixes #5337.

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-20 05:14:33 +00:00
gensokyo-dev d148ce0905 perf(environment): index observed-state write cutoffs (#5350)
Index parsed recorded-write roots once per state projection and reuse positive and negative type cutoffs. Preserve wildcard, ancestor/descendant, canonical timeline and Evidence semantics under the existing representation budget.

Follow-up to the final performance thread on #5319, which was merged while this optimization was being validated.

Validated with 508 relevant source tests, compiled state CLI/MCP cases, real Gitea HAR CRUD/search captures and Chromium captures, static/build/docs/catalog gates, and equivalent completed before/after projections.
2026-10-20 12:16:02 +08:00
3fca2e0ca6 feat(environment): extract the initial state a capture's reads observed (#5319)
* feat(environment): extract the initial state a capture's reads observed

Add the environment-state analysis view. From the parents the environment
manifest accepts, it extracts the data the capture's reads observed before
the first related write: entity types with an identity field (proved by a
path parameter or inferred from an identity name), entities with their
recorded values and provenance, documents for written non-entity paths, and
a render plan per read tool that is kept only when it reproduces the tool's
latest initial recordings from the state; every other read tool is a
recorded view with the reason. The Evidence is always inferred.

Response-body decoding moves to a domain owner shared with replay, exact
JSON parsing moves to the owner of the exact-number policy, the manifest and
state views share parent admission and record grouping, and
projectAnalysisView takes its accumulators as one object.

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

* fix(environment): bound state at redacted writes; infer identity only from items

A scenario write whose URL was redacted has no request line, so it was
dropped and reads after it counted as initial state. Such a successful
write now joins the timeline with an empty path, whose root relates to
every type, so it ends every type's state; a limitation counts them.

A list whose recordings were all empty passed the "every listed item
carries it" check vacuously and published an identity field nothing
showed; it now needs at least one item, else the view stays recorded
as no-identity.

A value that differed between two request lines of one item tool, such
as a query variant, was called volatile, and the gate then substituted
each recording's own value; only repeats of one request line now show
volatility, so differing lines go through fields-differ.

List membership is compared only while a list can still be the whole
collection, and merged entities are indexed by type and collection once,
so paged captures no longer cost pages times entities (8,000 pages of
40,000 entities: 5.7 s to 0.23 s).

Docs and the tool contract say scenario state keeps the capture's
redaction markers and how redacted writes bound it.

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

* fix(environment): keep first sightings, one list pointer and linear field unions

Entities took the order of the first plan observed, and item plans run
before list plans, so a complete list read before its detail reads came
out reordered and lost "all" membership; each entity now keeps its
earliest recorded sighting.

A list plan reused the first recording's pointer for later recordings,
so a later response that was itself an entity wrapping a list was read
as that list; every recording must now locate the same list.

The field union scanned its ordered list for every key, quadratic in an
object's width; a set now answers membership.

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

* fix(environment): keep post-write values and partial lists out of the initial state

A value that changed between repeats of one request line was named
volatile whenever no write under the type's own root lay between, so a
write under another root (for example POST /api/dashboards/db before a
second GET /api/search) left its effect in the initial state. Any
recorded write between the two observations now leaves every tool of the
type recorded with reason fields-differ instead.

A list was published with membership "all" even when a total or count
envelope member exceeded the number it listed. Such a counter now makes
the membership "recorded".

Also drop a stale doc comment on ObservedEntity.order.

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

* docs(environment): scope item-shaped and deleted written paths out of documents

State documents are held only for list-shaped paths a POST, PUT or PATCH
tool writes. Reads of item-shaped written paths and of paths that are only
deleted stay recorded views; the guide now says so and the spec defers them
to a later increment of #4933.

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

* fix(environment): count writes in flight when a value changes between reads

The volatility check compared write start positions with the two reads,
so a write already in flight when the earlier read started was missed and
its effect was named volatile. A write now counts when its interval
overlaps the span from the earlier read's start to the later read's end.

The check also ran once per changed field and scanned every write. It now
runs once per pair of observations, answered by a binary search over the
writes in timeline order and the latest end among each prefix.

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

* fix(environment): catch cross-tool changes across writes; read only item totals

A value that changed across a write was caught only between repeats of
one tool's request line, so a list read before a write and an item read
after it put the post-write value into the initial state. Changes are
now classified per entity in timeline order after observation: across
any recorded write, a change between repeats of a line or between two
lines of the type rewrites the type (every tool recorded as
fields-differ); with no write between, a repeat's change is volatile.
The classification lives in environmentStateMerge.ts.

Any numeric member whose name contained "total" or "count" counted as
an item total, so total_pages, account_id or page_count made complete
lists partial. Item totals are now members whose name tokens include
total or count and none of page, pages or per; a string of digits counts
as a number; and a set has_more, next or next-page token member also
marks the list partial.

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

* fix(environment): keep post-write observations out of initial state

* fix(environment): retain field provenance and bound state inference work

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: gensokyo-dev <gensokyo-dev@users.noreply.github.com>
2026-10-19 20:33:46 -07:00
gensokyo-devandKaoru0822 4e716b0607 perf(analysis-view): reuse validated immutable captures (#5349)
Reuse the format owner's full validation for exact authenticated immutable captures across retained queries. Preserve ordinary mutable-input validation and per-request subject, coverage and budget checks.

Refs #3133.

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-20 03:32:18 +00:00
gensokyo-dev d72021b0da fix(hopper): verify publisher before probing private display (#5346)
Return publisher refusal before unrelated private-display prerequisites can mask it. Retain display validation for admitted launchers, native launcher digest revalidation and owned cleanup.

Add a public CLI fault control using an unusable Python home, preserve the ordinary control and assert display selection for admitted versus refused launchers. Affected tests: 177 passes; 13 changed-file cases passed three additional repeats; full static checks pass.

Real-Hopper owner-exit identity revalidation fails identically on unchanged main and the PR; complete real-provider qualification is not passing. This pre-existing failure remains outside this focused correction.

Assisted-by: Reimu (gensokyo-skills:reimu-incident-triage)

Assisted-by: Kogasa (gensokyo-skills:kogasa-surprise-testing)
2026-10-20 02:41:14 +00:00
f93a0335a8 feat(analysis-view): project rich captures from Evidence files and template path pairs (#5293)
Project large local Evidence captures through the authenticated file-source boundary and add guarded opt-in sibling-pair path inference. Preserve request cancellation, resource ownership and file-relative validation diagnostics.

Validated relevant source and compiled CLI/MCP/environment boundaries, static/build/docs/catalog gates, and a real isolated Gitea 28.2.0 capture through CLI, default stdio MCP and HTTP replay. Current-head automated review passed; unrelated long-running platform jobs may still be pending.

Co-authored-by: Edgar Arakelyan <edgar.arakelyan@scale.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: gensokyo-dev <gensokyo-dev@users.noreply.github.com>
2026-10-20 10:33:52 +08:00
5fa4defcbc feat(artifacts): inspect and extract Godot PCK with retained facts (#3634)
* feat(artifacts): read Godot PCK packs

Add a Godot Engine PCK artifact reader for standalone packs and packs
embedded behind an executable trailer (formats 2 and 3). Members are
uncompressed byte ranges verified against their declared MD5 on read.

Encrypted directories, sparse bundles, patch removal entries, unknown
flags and format 1 fail closed as unavailable; encrypted members are
reported without being read. inspect-artifact and extract-artifact
recognize standalone packs by their GDPC magic.

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

* fix(artifacts): bound Godot PCK metadata and own member streams

Cap member paths at 4096 bytes and the retained directory at 64 MiB
before allocation. Read members with positional chunk reads through
pipeline so cancellation is typed, abandoned consumers release their
source, and the shared descriptor stays reader-owned. Retain the root
descriptor for godot-pck inventory, as for ZIP and universal Mach-O.

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

* feat(artifacts): complete Godot PCK facts, integrity and selected views

* fix(artifacts): preserve atomic-root inventory Evidence

* fix(artifacts): bound aggregate PCK member expansion

---------

Co-authored-by: Samucahub <151155051+Samucahub@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
2026-10-20 09:53:53 +08:00
JANG BYUNGKUNandgensokyo-dev 833136507d feat(il2cpp): inventory Unity IL2CPP global-metadata images, types and methods (#3807)
* feat(il2cpp): inventory IL2CPP global-metadata images, types and methods

Add inspect_il2cpp_metadata (CLI inspect-il2cpp-metadata), a read-only
in-process decoder for unencrypted Unity global-metadata.dat version 39.
It reports header sections, serialized index widths, images, type
definitions (nesting from the nestedTypes section) and method names.

Unrecognized magic and other versions are unsupported; damaged headers,
overlapping sections, inconsistent record sizes, out-of-range indices and
invalid strings fail with a reason code and no partial success. MCP
detail summary retains the complete Evidence and pages rows through
inspect_analysis_view kind il2cpp-metadata.

Adds an opt-in verify:il2cpp:metadata -- --metadata PATH check.

* feat(il2cpp): deliver metadata summaries by default over MCP

A 112,232-method file produced a 10.5 MB complete response, above the
10 MiB stdio budget. Default MCP delivery is now the retained summary;
detail complete stays available, and the one-shot CLI requests it.

* fix(il2cpp): reject shifted strings, nesting cycles and method owner drift

Review fuzzing showed that moving the strings section offset by one byte
silently renamed every image, type and method. Name indices must now start
a string, which also bounds total decoding by the section size.

Nesting cycles and methods whose declaring type disagrees with the owning
type range are rejected instead of returned. Adds service, shutdown and
index-width coverage, and moves environment-specific measurements out of
the public guide and tool description.

* feat(il2cpp): decode global-metadata versions 29 and 31

Unity 2022.3 writes version 29 or 31 metadata: a 256-byte header of
(offset, size) pairs, fixed 4-byte indices, type records with
elementTypeIndex, and method records that gained returnParameterToken in
version 31. Record counts come from section sizes and must be whole.

Sections report a null count for these versions and index widths report
fixed-width-version. Image, type and method rows keep the same shape, and
the string, nesting and method-ownership checks apply to every version.

* fix(il2cpp): bound report size before serializing and tighten coverage

Rows share decoded names, so a 130 KB file could expand into gigabytes of
JSON before the 64 MiB budget check, ending in an internal RangeError. The
provider now refuses from a lower bound on the serialized rows first.

The decoder also rejects non-empty sections inside the header, methods
outside their declaring type's range and overlapping image type ranges, and
keeps a leading byte order mark in names. Layouts now carry one indexing
mode instead of two coupled flags, and the tool description explains
nested_type_start.

* fix(il2cpp): refuse oversized metadata before decoding rows

Address review findings on the IL2CPP metadata inventory:

- check a lower bound from header row counts before any row object is
  allocated, so a large rejected file no longer builds every row first;
- validate sealed parent Evidence and count its rows once per report
  instead of on every inspect_analysis_view page;
- decode version 39 fixtures with 2- and 4-byte type-definition,
  generic-container and parameter indices.

* docs(il2cpp): attach the decoder comment to readIl2cppMetadata

* fix(il2cpp): deliver complete rows by default like the other inspection tools

inspect_il2cpp_metadata defaulted to detail summary, the only inspection
tool to do so. Omitted detail now returns every row inline, as
inspect_artifact, decode_interface_builder and the capture tools do;
summary stays one argument away and the guide, CLI reference and skill
reference say to select it first for large games, whose complete MCP
delivery is refused at the stdio budget with retained Evidence.

* test(il2cpp): cover narrow-width -1 sentinels and fix the skill's default

An empty image stores its type start as the all-ones value of a one- or
two-byte index; the decoder reads it as -1, but no fixture exercised it,
so removing either conversion passed the suite. The fixture can now append
an empty image, and both widths decode it. The skill still described a
summary default; it now says to select summary first, as the guide does.

* docs(il2cpp): request summary explicitly in the summary example

* chore(il2cpp): hash the verifier metadata through owned file resources

* fix(il2cpp): count escaped name length without building the escaped string

* fix(il2cpp): keep the verifier's check failure and small-table last page

* feat(domain): count JSON string length without building the escaped string

* fix(il2cpp): keep descriptor cleanup owners and share the JSON length count

* test(domain): give the backslash case real backslashes

* fix(il2cpp): select the CLI path and finish the command like the other tools

Follow main's absoluteCallerPath (#4067) and pass the command signal to finishAnalysisCommand.

* fix(il2cpp): select the verifier input path like the CLI

Use absoluteCallerPath (#4067) so a path with a symlink followed by .. names the same file the CLI and MCP inspect.

* feat(il2cpp): add compact composable metadata queries

Retain source-indexed Evidence for filtered batch and field queries. Bound representation growth before serialization, yield decoding for cancellation, and share CLI Evidence publication with desktop inspection. Verify public Unity metadata against independent byte and managed-DLL oracles and exercise actual stdio SDK workflows.

* fix(il2cpp): retain the validated metadata header magic

* test(il2cpp): remove obsolete execution wording exception

* fix(artifacts): preserve export selection and completed source facts

---------

Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
2026-10-20 07:20:47 +08:00
gensokyo-dev 82c76e2cba fix(ci): validate unindexed readiness heads with live PR metadata (#5316) 2026-10-20 07:19:58 +08:00
bf1eeee94f feat(environment): build environments from browser scenario captures (#5182)
Build an agent environment directly from retained capture_browser_scenario Evidence through the existing manifest, serve and export commands. Preserve request ordinals, source pointers, redaction markers, unavailable bodies, provenance and the default stdio MCP transport.

Followups admit copied/transformed text before body validation or decoding, avoid request-text allocation in replay, normalize portable file inventories, select dependent CI lanes, retain the author's redaction/body-budget corrections, and count body coverage over represented requests.

Validation: full pinned-toolchain static gate, source and public CLI/MCP regressions, canonical catalog checks, actual Chromium interactive and live GitHub API captures with exact HTTP replay and stdio parity, the generated non-root Docker image, and its smoke task through real agent-env 0.9.1334. Verification scopes, resource measurements and platform limits are recorded in the PR and feature verification document.

Co-authored-by: gensokyo-dev <gensokyo-dev@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 06:07:15 +08:00
7a0c47bfa4 feat(environment): export an environment manifest as an agent environment bundle (#4999)
Export an authenticated capture and environment manifest as a pinned agentenv bundle with provenance, a replay verifier and smoke task.

Followups preserve immutable input ownership, bounded discovery, caller paths and numeric values; bind bundle writes and package copying to retained filesystem owners; verify POSIX modes; retain cleanup authority; and verify framework/npm executable selection. Default MCP remains stdio.

Validation: pinned Linux toolchain, typecheck/build/lint/format/docs/knip, 452 focused tests, ten product-catalog cases, real GitHub response replay in the generated non-root Docker image, and the real pinned agentenv framework smoke workflow. Platform and resource-measurement limits are recorded in the feature verification document.

Co-authored-by: gensokyo-dev <gensokyo-dev@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-20 05:29:43 +08:00
gensokyo-dev b4938aed58 feat(artifacts): triage desktop installation directories (#5207)
* feat(artifacts): triage desktop installation directories

* fix(artifacts): retain completed probes through failed file cleanup

* perf(artifacts): decode only assembly reference names for desktop triage

* fix(artifacts): enforce exact signatures and responsive directory probes

* fix(artifacts): recognize Unity bundles and bound FORM chunks

* fix(artifacts): retain bounded desktop omission evidence

* fix(artifacts): select ASAR targets before inventory probes

* chore: preserve tested upstream HTTP owner during integration

* fix(artifacts): reuse Mach-O signatures for FAT64 desktop probes

* fix(artifacts): expose bounded desktop probe diagnostics

* fix(artifacts): derive desktop snapshots from observed source identities

* fix(artifacts): admit desktop facts before JSON expansion
2026-10-20 04:01:36 +08:00
gensokyo-dev 71ae02c6ef fix(process): retain child scope and bound native exit waits (#5280)
Retain original parent/child scope and prior native-generation proof across exit settlement and token retries. Keep historical scope separate from signal authority, count each supplied snapshot once, and share one existing native-exit deadline throughout cleanup.

Validated 387 affected tests (one Darwin-only skip), five controls repeated three times, and 30 synced-main integration tests (one platform skip). Synced-head static, architecture, docs and discovery checks pass; real GDB qualification passes 44 CLI/MCP cases. Native-observer edge controls are simulated.

Assisted-by: Reimu (gensokyo-skills:reimu-incident-triage)

Assisted-by: Kogasa (gensokyo-skills:kogasa-surprise-testing)
2026-10-19 19:27:43 +00:00
10f1994fe1 feat(environment): serve an environment manifest as a replay environment (#4975)
Serve retained capture Evidence as a bounded MCP replay environment with discovery.
Preserve default stdio, caller-selected facts and complete source Evidence.

Fix numeric fidelity, argument identity, startup cancellation and bounded discovery,
query/path matching and output metadata. Verify real recorded API data and public
CLI/MCP workflows; preserve explicit gaps for Windows and third-party hosts.

Co-authored-by: Edgar Arakelyan <edgar.arakelyan@scale.com>
Co-authored-by: gensokyo-dev <gensokyo-dev@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 03:06:29 +08:00
gensokyo-devandKaoru0822 909b9f7b6b fix(rizin): keep protocol failures outside exit recovery (#5290)
Separate debugger exit-policy readback from process descriptor checks. Require the final native readback before considering inferior-end proof, and regress an ambiguous reply delivered as the real owned inferior exits. Preserve debugger ownership on failure.

Fixes #5282

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-20 02:56:41 +08:00
gensokyo-devandKaoru0822 e84e17e30f fix(rizin): accept proven inferior exit after detach (#5281)
Reuse the common Linux identity reader and retain the original PID/start-time pair throughout detach. After Rizin confirms no active descriptor, accept a proven end of that original process while preserving refusals for unresolved or live mismatched targets. Remove the duplicate proc parser and cover deterministic exit/live-redirect transitions with owned fixtures.

Fixes #5277

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-20 02:29:04 +08:00
gensokyo-dev 1c2c2f1e78 fix(process): settle native exit and repair ownership verification (#5249)
* fix(process): classify baseline cleanup with current ownership facts

Use the pre-launch baseline only to select candidates for current native
identity/token validation. Preserve confirmed absence and verified foreign
generations instead of carrying a premature baseline identity failure.

Exercise the real Linux enumeration/exit race and retain countercontrols for
unknown identities and live token-owned processes.

Assisted-by: Reimu (gensokyo-skills:reimu-incident-triage)

* fix(ci): distinguish process inventory from historical PID lookups

* test(ci): verify decoded historical PID controls before tracing

* fix(ci): trace historical PID handles and signal probes

* fix(ci): resolve traced directory paths across pathname syscalls

Classify producer-decoded FD/path pairs once instead of limiting failed relative
lookup detection to openat on /proc. This preserves attempted readlinkat, statx
and newfstatat accesses with no returned descriptor.

Three regression cases fail against the previous policy and all 33 controls
pass with the shared pathname interpretation.

Assisted-by: Reimu (gensokyo-skills:reimu-incident-triage)

* test(ci): declare unresolved legacy pathname coverage

Report the historical-PID access surfaces actually checked and explicitly mark
legacy cwd-relative pathname calls unverified. The existing tracer does not
provide their cwd identity, and a blanket rejection also rejects legitimate
GDB build-ID metadata queries.

Retain every committed access guard and document the preexisting limit without
claiming absence from missing path evidence. The complete real 44-case CLI/MCP
recorded-crash qualification passes with the coverage declaration.

Assisted-by: Reimu (gensokyo-skills:reimu-incident-triage)

* fix(ci): reject priority access to historical PIDs

* fix(ci): preserve C-quoted components in traced path checks

strace renders non-ASCII pathname bytes as C octal escapes, not JSON strings.
Keep those components opaque when resolving literal ASCII path separators and
proc PID selectors, matching the existing absolute-path interpretation.

Two controls fail before the correction and all 35 pass afterward. An actual
strace observation independently reproduces the rejection of an unrelated
Chinese filename and verifies the corrected classification.

Assisted-by: Reimu (gensokyo-skills:reimu-incident-triage)

* fix(ci): reject historical PID resource and scheduler calls

* fix(schema): preserve portable large base64 validation

* fix(ci): share explicit PID syscall contracts with tracing

Derive tracer selection and reported coverage from the same named target
contracts used by the access checker. Cover resource limits, scheduler calls,
process metadata, PID groups, capabilities and pidfd operations without mixing
UIDs, policy enums, self selectors or output values with target PIDs.

Interpret each target once and retain unresolved target facts as a distinct
failure. Emit the coverage catalog once per verifier result.

The previous oracle misses 28 new controls. All 83 controls pass after the
change; real native resource/scheduler probes capture 14 previously missed
calls with resource limits and affinity unchanged. All 35 selected syscall
names are accepted by the pinned local strace.

Assisted-by: Reimu (gensokyo-skills:reimu-incident-triage)

* fix(ci): classify I/O priority and qualify PID trace coverage

* fix(process): keep ownership retries on the native clock

* fix(ci): retain process edges in ownership trace inventory

Keep fork/vfork and resumed edges along with clone events so the shared attribution owner receives the complete process relation facts it consumes.

Assisted-by: Reimu (gensokyo-skills:reimu-incident-triage)

* fix(process): type native exit predicates and ownership fixtures precisely

* fix(ci): retain trace coverage limits after integration

Restore explicit unverified symlink aliases and same-Node stat/status purpose in the shared coverage contract and guide. The native-exit and trace-attribution integration does not establish those properties. Preserve both limitations rather than silently broadening the reported qualification.

Assisted-by: Reimu (gensokyo-skills:reimu-incident-triage)
2026-10-20 02:01:00 +08:00
NoThingandKaoru0822 1b3ac94242 fix(rizin): bound recent history frames by UTF-8 byte budget (#5246)
Bound recent decoded history to complete UTF-8 prefixes and reuse the bounded capture implementation without changing command Evidence or wire queue accounting. Add deterministic Unicode and invalid-byte coverage, and document history retention.

Fixes #5091
Fixes #5242

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-20 01:41:46 +08:00
morluto b44cb5bd30 test(javascript): distinguish selected paths from artifact identity
Check CLI parity against the admitted absolute selection and verify that MCP retains relative selections separately. Equal canonical artifact results do not imply equal Evidence IDs when caller-selected paths differ.
2026-10-20 01:17:45 +08:00
gensokyo-devandKaoru0822 2dd5effe2a fix(rizin): default one-shot output to plain presentation (#5269)
Default native one-shot Rizin presentation to plain text before collection while preserving caller colour overrides, literal output, recorded argv and shared CLI/MCP Evidence.

Fixes #4488

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-20 01:12:35 +08:00
morluto 0e62094b69 test(setup): isolate registration checks from installed providers
Select absent Hopper, Ghidra and Java locations consistently across registration environment acceptance fixtures. Host PATH Java was executing during IDA budget checks and exposing those checks to unrelated provider cleanup failures. Keep the process ownership failure policy and all registration assertions intact.
2026-10-20 00:53:52 +08:00
morlutoandgensokyo-dev 5199c672ff fix(process): keep lifecycle waits independent of application timers
Use Node promise timers consistently for ownership retries, final verification and capture-journal quiet settlement. Preserve the existing elapsed-time budgets, ownership checks, cancellation and trailing-callback drain.

Adapt the ownership timer correction from gensokyo-dev's PR #5249 and complete the same fix in the adjacent capture-journal owner. Three new controlled regressions fail before the fix; 93 source tests and 94 CLI/MCP/process boundary tests pass afterward, with static checks passing. Global performance replacement and ordinary native Jadx incidence remain unverified.

Fixes #5267

Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
2026-10-20 00:45:49 +08:00
c4aaf813e0 fix(javascript): retain selected application paths in Evidence
Record the admitted selected path separately from canonical artifact identity in successful and partial Evidence. Anchor relative inputs consistently across both CLI routes using the existing caller-path owner.

Adapt shared client verifier assertions and boundary coverage from PR #5261, and the dedicated-route anchoring correction proposed in PR #5265. Historical parsing remains independent of live paths, including legacy records without the parameter.

Validation: 118 focused tests in eight files, static and docs checks, discovery, and installed Gemini CLI 0.29.5 loopback verification of 207 schemas/241 examples passed. Synthetic static-analysis fixtures; no live Google API or native target execution.

Fixes #5258

Co-authored-by: kwakhyun <khyun2348@naver.com>
Co-authored-by: vishal-tambi <tambivishal3@gmail.com>
2026-10-20 00:37:45 +08:00
morluto aacf740a04 fix(ci): correlate ownership trace threads and preserve setup failures
Follow CLONE_THREAD edges across strace -ff files when excluding the ownership process's stat/status reads. Keep provider child processes outside the allowance and document the same-process attribution limit.

Validate setup result array shapes and preserve the original failed-plan JSON instead of masking it with a diagnostic TypeError.
2026-10-20 00:05:22 +08:00
morluto 0949313d8f fix(schema): compose portable stack-safe base64 constraints
Replace the lookahead-based pattern with separate allOf/not JSON Schema rules for quartet length, alphabet, padding and canonical pad bits. Share the runtime predicates with HAR and padded base64 parsing.

Validate converted advertised leaves with the MCP SDK validator, preserve permissive versus canonical semantics, and exercise every pattern near the 32 MiB encoded-input budget in all JavaScript modes.
2026-10-20 00:05:22 +08:00
morluto 4af71a45f9 fix(process): reconcile native lifetime and selective Linux UID facts
Settle absent and zombie candidates before retaining baseline or token-read failures. Preserve fail-closed behavior for live and unknown generations, and stop retrying exited candidates.

Bind Linux effective UID to stat-bracketed status reads only for unreadable-token candidates, preserving partial identity facts and avoiding full-table UID enrichment.
2026-10-20 00:05:22 +08:00
Edgar Arakelyanandgensokyo-dev 60bd646478 feat(mcp): add opt-in streamable HTTP transport (#4955)
Add an explicit Streamable HTTP entry for environment/gateway hosts while keeping stdio registrations and default behavior. Preserve the shared provider/session owner across DELETE and replacement; fence and drain tool/completion work before handover or teardown.

Bound HTTP input and plain-response admission, cancel refused bodies, join whole exchanges and asynchronous response cleanup before provider hooks, gate startup readiness, and reject queued HTTP/1.1 pipelining while retaining sequential keep-alive.

Validation: 8 static/build tasks, 83 adapter tests and registry metadata pass. A real installed gh binary produces seven equivalent CLI/stdio/HTTP reports with DELETE/replacement and clean signal shutdown. Broader local native-provider/ownership failures reproduce on clean main and are documented with limitations in the PR.

Co-authored-by: Edgar Arakelyan <edgar.arakelyan@scale.com>
Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
2026-10-19 23:58:51 +08:00
morluto 255d906e15 fix(native): scan byte-pattern inputs without regexp stack growth
Keep exact wildcard, whitespace and aligned-mask validation in the shared
domain refinement. Advertise descriptive string inputs instead of repeating
regexes, and remove discarded byte/mask array materialization.

Preserve caller spelling and error precedence; cover large public stdio
queries, provider-boundary parsing and connection recovery.

Fixes #5257
2026-10-19 22:09:42 +08:00
morluto 49b4edfe77 fix(browser): validate large base64 without regexp stack overflow
Share padded decoding across CDP response shapes and cache fingerprints,
and count binary WebSocket bytes without allocating a decoded buffer.
Preserve HAR canonical pad bits and validate before allocation.

Replace repeated alphabet quartets in canonical schemas with separate
alphabet, padding and fixed-width length assertions. Cover near-32MiB
encodings under ordinary, Unicode and Unicode-set modes, plus complete
4MiB CLI and MCP retained-evidence export workflows.

Fixes #5253
2026-10-19 21:46:23 +08:00
gensokyo-devandKaoru0822 3ea0d9489e feat(objdump): select native disassembly by symbol (#5171)
Push optional native symbol selection into GNU objdump before collecting Evidence. Preserve native matching, raw output, artifact identity, scoped limitations and the existing full-disassembly default through the shared CLI/MCP workflow.

Fixes #5159

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-19 20:15:20 +08:00
morluto dc936f433c test(process): distinguish native preparation from capture allocation
A missing temporary path stops Darwin native-inspector preparation before
capture allocation can account for resources. Keep its progress coverage
unknown; Linux reaches the capture root allocation and reports not-required
resources. Assert the public typed failure preserves the selected temp path.

The combined Electron/progress focused rerun passed 33 tests on macOS arm64
with Node 24.18.0. The seven progress cases passed; native Linux was not run.
Static checks, docs checks and test discovery also passed on the final tree.
2026-10-19 20:04:00 +08:00
morluto 49e67aa1d9 fix(process): retain native ownership evidence through cleanup
Keep a run-scoped evidence owner from acquisition through settlement and
cleanup retry. Bind token observations to native generations and retain
related generations and unresolved identity lookups across table omissions,
reparenting and process-group changes without treating PID hints as authority.

Use native effective UID facts for withholding decisions, corroborate Linux
procfs absence, and share the Linux stat parser with sampling and debugger
identity checks. Signal only after current token and group revalidation.
Complete swept cleanup across all groups with one bounded final family check;
retain unresolved owners and successful-peer completion across retries.

Validation: npm run check, docs:check and test discovery passed. Focused
ownership/consumer coverage totals 513 passed and 1 skipped across 35 files,
including the corrected Electron cancellation test rerun independently. Darwin helper compilation, real
owned Node child cleanup, CLI/MCP capture and retained cleanup retries ran.
Real Electron applications, native Linux races/PID reuse and Windows cleanup
were not qualified. The original Linux reports remain open for that work.

Fixes #5238
Refs #4322, #5206
2026-10-19 20:04:00 +08:00
gensokyo-dev 0fc8462be1 fix(tests): complete independent agent evaluation cleanup
Attempt independent auth and run-tree cleanup even after permission denial,
retaining the primary verification failure alongside cleanup errors. Exercise
both retention modes through the verifier entrypoint, preserve startup
diagnostics, and retain cooperative coverage for root.

Fixes #5095

Assisted-by: Reimu (gensokyo-skills:reimu-incident-triage)
2026-10-19 11:54:04 +00:00
gensokyo-dev b999e72476 fix(ci): repair capture ownership and portable ABI validation (#5224)
* fix(tests): restore reliable snapshot and container CI checks

Keep exact cancellation causes and partial snapshot facts in regression oracles. Match fault injection to real directory handles and isolated MCP module ownership; await terminal cleanup progress within the existing test deadline. Reuse the valid ASAR Pickle fixture and preserve truncated-container rejection.

* fix(tests): isolate runtime faults and own CLI subprocesses

Keep the runtime provider mock in the isolated project after reused CLI graphs exposed lost fault injection. Use the existing owned CLI fixture for Inspector parity and update command checks, retain caller environment choices, and require the removed alias to fail with COMMAND_NOT_FOUND. Keep current test deadlines and production behavior.

* fix(process): retain executable handles through read cancellation

Use the existing borrowed descriptor reader so stream cancellation cannot invalidate a handle owned by capture. Preserve the exact cancellation and permit immediate scope reuse after cleanup. Reproduce the native-stream failure with actual file IO and retain completed/failed-close controls.

Synchronize runtime cancellation and backwards-clock injection with observed running progress. The corrected clock oracle now rejects a Date.now-based deadline under controlled rollback; keep test deadlines and narrow host cleanup checks unchanged.

* test(process): observe prelaunch cancellation at descriptor reads

* fix(process): retain read failures alongside observed cancellation

* fix(flutter): advertise portable Unicode ABI selectors
2026-10-19 19:32:22 +08:00
morluto a0edd145d3 fix(analysis): preserve input diagnostics and reuse JEB contracts 2026-10-19 18:45:48 +08:00
morlutoandYi-111-a 64abd4aefb fix(flutter): select reported ABI labels through shared contracts
Reuse canonical CLI fields and preserve literal archive labels through exact selection. Keep UTF-8 path budgets at inventory while advertising the same bounded Unicode selector that runtime admission enforces.

Co-authored-by: Yi-111-a <153097222+Yi-111-a@users.noreply.github.com>
2026-10-19 18:38:57 +08:00
morluto 69aa5465cd fix(frida): preserve resume failures across caller cancellation 2026-10-19 18:21:16 +08:00
morluto 2ec602b191 fix(frida): clarify device selection and cleanup contracts 2026-10-19 18:17:54 +08:00
morluto 44b6ec97ee fix(frida): retain cancellation through final session cleanup 2026-10-19 18:17:45 +08:00
morluto 69fe86c5c7 fix(frida): separate attachment from target resume observations 2026-10-19 18:11:27 +08:00
morluto 00dd959b82 fix(mcp): derive shared provider cleanup from loaded ports
Separate load-failure metadata from the typed optional observation ports
and derive the process cleanup inventory from those ports. This includes
the JavaScript runtime owner and removes the separate four-provider list.

Preserve all load failures in server injection and keep shared providers
caller-owned across individual server closure. Retain failed close hooks
for retry while removing peers whose cleanup completed.

Validated with 19 focused lifecycle/isolation checks and a final rerun of
the new boundary regression, check, docs and exact test discovery. The
runner test uses real idle providers with controlled SDK/shutdown handles
and a close rejection; active Inspector resource consequences, native
Linux/Windows and end-to-end stdio shutdown remain unverified.

Fixes #5222
2026-10-19 18:03:22 +08:00
morlutoandPrabhuram Karki 435efe7afc fix(frida): share exact JSON string byte accounting
Count the serialized JSON representation at its owner, including escaped
lone UTF-16 surrogates. Frida now retains the correct bounded prefix and
reports truncation in script results and subsequent session Evidence.

Move the existing exact managed string counter into the domain layer and
use it for Frida, managed projections and Ghidra inline PDB caveats. Remove
the superseded counters while keeping Ghidra's bounded length guards and
NativeAOT's distinct raw-byte admission before string allocation.

Validated with 296 focused tests, check, docs and exact test discovery.
The Unicode oracle checks every UTF-16 code unit and adjacent fragments
against native JSON encoding. Frida execution uses simulated native handles
through the real MCP handlers/client; real target instrumentation is unverified.

Fixes #5097

Co-authored-by: Prabhuram Karki <prabhuramkarki07@gmail.com>
2026-10-19 17:50:50 +08:00
4cbcead46c fix(frida): share canonical discovery results across adapters
Own device and process discovery projection in the application workflow,
bound to each named output contract. CLI and MCP now consume that result
directly, including retained observations when final CLI cleanup fails.

Remove the duplicate adapter projections and preserve provider failures,
caller-selected identifiers, process records and cleanup diagnostics.

Validated with 103 focused tests, npm run check and docs:check. Real local
Frida 17.23.2 discovery on macOS arm64 returned the same three devices
through the compiled CLI and production stdio MCP; both now pass the
advertised schema. Remote endpoints and native Linux/Windows unverified.

Fixes #4637

Co-authored-by: shreyansh1729 <shreyanshiitian1729@gmail.com>
Co-authored-by: Kaushalendra-Marcus <yadavkausha4a5@gmail.com>
2026-10-19 17:35:10 +08:00
91884c3743 fix(ghidra): own operation-scoped JVM failure evidence
Scan provider output before retention and keep bounded confirmed token facts
through output completion. Exclude identifier prefixes, pre-checkpoint context,
and unconfirmed tails; preserve invalid UTF-8 and directional truncation facts.

Make confirmed OOME observations authoritative for projection. Retained raw
prefixes cannot establish an OOME, and unrelated output cannot supply its
resource kind or recovery advice. Incomplete no-OOME output preserves the
primary error and explains uncertainty. Current checkpoint coverage remains
separate from historical budget loss.

Adapt the retention approach from #4774 with independently reproduced scanner
and projection counterexamples corrected before integration.

Validation: 268 focused tests, npm run check, npm run docs:check, and test
discovery (1639 files exactly once) pass. Controlled Node pipes/socket and fake
Ghidra JVM only; real Ghidra and native Linux/Windows behavior are unverified.

Fixes #4682
Fixes #5220

Co-authored-by: reason-machines[bot] <293529679+reason-machines[bot]@users.noreply.github.com>
Co-authored-by: Saim Shafique <msaimshafique4664@gmail.com>
2026-10-19 17:15:32 +08:00
morluto 048d70755e fix(frida): preserve script evidence through cancellation and decoding
Keep per-script messages and truncation in one owned capture through native
load completion and awaited cleanup. Return cancelled partial observations
when cancellation is observed at native completion, including native rejection;
retain failed cleanup for retry and preserve earlier failures during cleanup.
Report missing creation handles without claiming the engine did no work.

Hash original script-file bytes and retain replacement-decoding provenance
without changing normalized CLI/MCP result schemas or accepted file behavior.
Share this projection across persistent and one-shot Evidence.

Validation: 103 focused tests, final 38 manager source tests, npm run check,
and npm run docs:check pass. Controlled public CLI/MCP workflows and fake
Frida bindings only; no real target injection or device verification.

Fixes #3979
Fixes #3396
2026-10-19 16:41:15 +08:00
morluto 39275420ea fix(process): preserve cancellation through liveness rechecks
Forward the accepted observation signal into per-member process-table
rechecks and preserve its exact cancellation reason. Keep uncancelled
inspection failures unverifiable and guard successful late completions.

Fixes #5205
2026-10-19 15:52:36 +08:00
morluto ca71e91d23 fix(process): await owned observation commands through finalization
Supervise sampler and ownership-query commands through physical exit on
cancellation. Share Darwin helper command ownership, reject new commands
after close starts, and drain admitted commands before removing the root.
Keep failed root cleanup retryable without rerunning completed queries.

Fixes #5199
Fixes #5202
2026-10-19 15:37:15 +08:00
morluto ba866a26ec fix(process): preserve partial sampling observations and shutdown gaps
Keep procfs absence separate from denied and failed reads. Carry successful
rows, thread children and bounded diagnostics together so one failed sibling
cannot erase readable peers. Retain root initialization failures and validate
ps-discovered Linux candidates through stable procfs identity brackets.

Await issued reads during shutdown, retain completed rows and report an
unfinished pass through the existing sampling failure/diagnostic contract.
Derive partial coverage at its owner and preserve actual read errors when
shutdown also interrupts observation. Keep the public capture schema unchanged
and verify it with the previous strict capture reader.

Update directly affected coverage consumers and regression fixtures, including
identity contradictions, mixed outcomes, late failures, cleanup, cancellation,
process-only comparison unknowns and converted MCP schemas.

Validation: 12 focused files / 123 tests; full check; docs check (5 tests);
test discovery (1,635 files). Real macOS CLI/MCP controls exercised source-owned
PTY targets. Linux permission behavior uses deterministic boundary fixtures;
real Linux and Windows qualification remain unavailable. Completed CLI cost
comparisons do not establish a speedup.

Fixes #5081
Fixes #5194
2026-10-19 15:14:42 +08:00
morluto e8da7e1edd fix(artifacts): share bounded ASAR signature observations
Parse Pickle extents once at the ASAR boundary and carry finalized signature
facts through root and nested classification. Scan JSON whitespace within its
declared extent and decode budget without retaining the whitespace prefix.
Keep prefix storage bounded independently of stream chunk ownership.

Fill fixed ASAR prefixes and DMG trailers across short reads, preserve
cancellation through admission and stream EOF, and retain existing cleanup
ownership. Cover public CLI/MCP selection, root/nested inventory, malformed
JSON whitespace and padding, limits, native/XML consumers and descriptor faults.

Validation: 24 focused files / 170 tests; full check; docs check; test discovery.
Official inert ASAR fixtures and controlled descriptor faults were exercised;
real Electron execution, Windows and natural OS short-read frequency remain
unverified. Equivalent compact CLI tasks returned identical results and reply
token estimates; repeated timings do not establish a speedup.

Fixes #3574
Fixes #5188
2026-10-19 14:32:48 +08:00
morluto 2e5171e391 fix(browser): retain historical capture outcomes through cleanup 2026-10-19 14:03:57 +08:00
morluto 87ab054ea3 fix(runtime): retain Inspector and Electron cleanup ownership 2026-10-19 14:03:28 +08:00
morluto 7f4e7d5577 fix(browser): preserve caller cancellation through CDP observations 2026-10-19 14:00:01 +08:00
morluto 9aca543f46 fix(browser): isolate web bundle analysis with bounded partial results
Run inert captured-script analysis in owned children with per-script and aggregate deadlines, bounded source/result transfer and explicit per-script coverage. Preserve completed facts and exact cleanup authority across cancellation, mixed descriptor/process failures and cross-consumer retry.

Share aggregate source admission across page and Electron capture, expose CLI/MCP resource controls and retain selected parameters in Evidence. Verified with controlled CDP public workflows, real owned Node failure controls and the reporter-matching ReactDOM UMD artifact; real Chrome/Electron and Windows/Bun remain unqualified.
2026-10-19 13:22:54 +08:00
morluto 715d93e954 refactor(process): share ownership for isolated analysis workers
Separate child and private-root ownership from JavaScript analysis protocols. Retain exact cleanup lifetimes across retries, late handshakes and runtime reuse; observe request-stream errors and preserve per-job file cleanup during worker reuse.
2026-10-19 13:12:25 +08:00
e4c3d72396 fix(javascript): converge cyclic aliases with explicit projection widening
Replace initializer-history and full-path enumeration with a finite projection
join at each source read. Retain conservative local returned-reference effects
and disclose precision loss through static, semantic and captured-web results.
Close full-binding callback cycles while preserving narrowed read semantics.

Fixes #4348
Fixes #5138
Refs #4349
Refs #4364

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Fuxuan-CN <3072252442@qq.com>
2026-10-19 12:26:54 +08:00
morluto 9af0b6113c fix(process): retain snapshot handles and partial read outcomes 2026-10-19 11:37:41 +08:00
morluto 974487f00f fix(process): preserve cleanup ownership through retries and close
Carry historical cleanup reports with the exact lease that can confirm
release. Bind originating capture errors and typed retry aggregates to
those owners while preserving primary failures and completed observations.

Keep those bindings through MCP close and compose mixed session/process
cleanup failures. Start both close operations even when a child throws a
typed failure synchronously.

Fixes #5144
Fixes #5163
Fixes #5166
2026-10-19 10:52:55 +08:00
morluto dfe8378cf5 fix(electron): preserve callback ownership through listener removal
Keep canonical callback ownership independent of writable raw-listener
metadata. Resolve the native removal argument in current registration order,
then let EventEmitter own mutation, getter timing and removal meta-events.

Leave registrations unwrapped with explicit observation gaps when methods
or receivers do not support that ownership model. Preserve per-callback
Evidence for supported registrations and document the remaining limits.

Fixes #4513
2026-10-19 09:51:04 +08:00
morlutoandDeryFerd d6ca7b3f20 fix(process): gate launch and retain cancellation provenance
Check cancellation at the PTY execution boundary after executable sampling
and descriptor cleanup settle. Completed file observations cannot authorize
launch for a cancelled request. Preserve exact caller reasons in the shared
capture cancellation constructor and its admission, exit and finalization
consumers instead of reconstructing them downstream.

Exercise real executable reads and closes through the capture API with a
throwing PTY sentinel at five schedules. Retain a real completed Node capture
and its cleanup facts when the final progress callback cancels the request.
Strengthen existing metadata and baseline cancellation controls.

Adapted after review of #4836 at 014afa3892.
Raw snapshot/sampler descriptor cleanup ownership remains tracked in #5140;
this execution-admission change does not claim to solve failed close recovery.

Closes #4512
Closes #5142

Co-authored-by: DeryFerd <deryferdikao125@gmail.com>
2026-10-19 09:14:00 +08:00
morlutoandshreyansh1729 d3627b1038 fix(mcp): preserve procedure aliases and complete name ambiguity
Derive address suggestions separately from name uniqueness across every
validated inventory row. Keep the sole exact address or an explicit ambiguous
state per name, without retaining a set of all name/address pairs. Reuse the
canonical address/value leaf contract for completion's parsed facts.

Cover order independence, same-address duplicates, later ambiguity and alias
rows beyond the MCP reply limit through the public SDK. Preserve provider
spelling, document context, cold-session admission and cancellation behavior.

Adapted from #4648 at 5b019e1179.

Closes #4623

Co-authored-by: shreyansh1729 <shreyanshiitian1729@gmail.com>
2026-10-19 08:54:23 +08:00
Vaidehi Kakad 273bb66276 fix(setup): treat empty APPDATA as unset for VS Code, Trae and Devin (#5126) 2026-10-19 08:45:04 +08:00
morluto 6296fdf560 fix(reference)!: preserve symlink races and root failure facts
Classify replacement EINVAL at the readlink syscall so the reader and importer
retain sibling observations. Give an admitted root's enumeration failure its
own typed field, use it for unresolved symlink coverage, and project it as a
graph limitation instead of an invalid dot-path child entry. Keep admission,
cancellation and cleanup failures distinct.

Closes #5135
Closes #5136

BREAKING CHANGE: Partial reference-source reader results report failed root
enumeration in rootFailure instead of a failed directory child at path ".".
Historical source graphs preserve the root failure as a top-level limitation.
2026-10-19 08:36:53 +08:00
morlutoandrudrakshagarwal4200-ai c85b44e1b1 fix(process)!: retain partial filesystem snapshot observations
Keep expected readlink and follow-up lstat failures as typed entry omissions,
including the observed path and errno. Retain directory metadata and sibling
observations, distinguish known identity replacements, and keep cancellation
and unexpected I/O failures terminal. Reuse admitted root metadata and test
cancellation by descriptor state rather than a count of named lookups.

Adapt the symlink race handling proposed in #4587 at fae4e25539.

Closes #4575

BREAKING CHANGE: Filesystem enumeration omission reasons now also include
entry_unavailable and identity_changed. Clients validating the old literal
reason must accept the expanded capture schema.

Co-authored-by: rudrakshagarwal4200-ai <rudrakshagarwal4200@gmail.com>
2026-10-19 08:34:39 +08:00
morlutoandDragolone 95c8f57aa2 fix(investigation): scope automatic unknowns to result Evidence
Construct automatic unknown identity once at the application boundary.
Validate the original question before adding immutable Evidence scope, and
preserve each result's supporting link through CLI and MCP producers.

Apply the shared policy to derived investigations, managed comparisons and
verification, composed workflow residuals, and process-capture residuals.
This prevents registry conflicts and silent loss of later result links.
Keep explicit creation/update semantics and exact Evidence replays intact.

Cover all affected producer families with public MCP regressions, preserve
snapshot provenance distinctions, and retain blank-question/error handling.
Remove the superseded single-result call-path test.

Fixes #3333

Co-authored-by: Dragolone <160818051+Dragolone@users.noreply.github.com>
2026-10-19 08:02:03 +08:00
morlutoandHenglie 92dadf9334 fix(domain): preserve partial reference comparison facts
Own endpoint identity and per-edge kind knowledge in one parsed reference
representation. Preserve exact same-artifact coordinates and cross-artifact
name uncertainty, including generated and nonunique procedure labels.

Use exact multiplicity-preserving sparse compatibility flow instead of
positional/greedy pairing or a quadratic adjacency matrix. Share the native
coordinate parser with call paths and remove the superseded projections.

Validate public MCP comparison/reconstruction outcomes, independent tiny
permutation oracles, dense matching and advertised JSON Schemas.

Fixes #4210
Fixes #5131

Co-authored-by: Henglie <ebhenglie@gmail.com>
2026-10-19 07:45:34 +08:00
morluto 559af3d3ff fix(uninstall): retain per-file partial outcomes
Preserve configuration paths and constituent actions independently of aggregate status. Derive summaries from all leaf outcomes, including after final cleanup reporting, so a later failure cannot hide an earlier removal.

Closes #5079
2026-10-19 07:22:01 +08:00
morluto a80575c736 fix(native): preserve partial call reference evidence
Retain provider-reported call sites and raw classification independently of generic reference flags. Keep all endpoint evidence when containing-procedure routes merge, and include relation in edge identity to avoid order-dependent labels.

Closes #5130
2026-10-19 07:12:44 +08:00
morluto fdaffe21cc docs(manifest): align plan with top-level body parsing 2026-10-19 07:08:20 +08:00
291059adb4 feat(analysis-view): project an environment manifest from web network captures (#4954)
* feat(analysis-view): project an environment manifest from web network captures

Add the environment-manifest view kind to inspect_analysis_view for retained
HAR Evidence: the primary origin's recorded request lines become a
deterministic tool surface (names, path templates, parameters with their
rule and basis, observed query names, value-free body shapes, record
pointers), with excluded groups, three fixed unknowns and the shared
projection budget. The four format rules always apply; after-uid-literal,
opaque-token and sibling-variance are opt-in and labelled, and literal
fold-in never touches a format-rule parameter. The api-surface accumulator
moves onto shared route, media-type and budget helpers without behaviour
change.

Refs morluto/rea#4933

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* test(web-network): check the manifest refusal text like the api-surface refusal

The real-capture verifier stringified the whole MCP result for the
environment-manifest refusal, so a declared "[" literal matched the result
envelope itself; compare the parsed refusal as the api-surface branch does.

Refs morluto/rea#4933

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* docs(specs): record the hosted web-network lane run for the manifest view

Refs morluto/rea#4933

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* docs(specs): reconcile the manifest view verification scope note with the hosted run

Refs morluto/rea#4933

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix(analysis-view): bound body, route and name work in the environment manifest

Addresses the automated review findings on the PR: read request bodies at the
top level only instead of walking nested values; admit every literal route
part before materializing it; compare at most 32 segments in sibling-variance
and say so; keep stop words literal after a uid literal; keep the next
collision suffix per tool-name base; preserve a templated exclusion when two
groups print the same row.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix(analysis-view): keep manifest folding, naming and media-type reads linear

Addresses the second automated review round: fold literal siblings through
set membership, key collision suffixes by the truncated name they share, and
charge a request media type before it is scanned.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix(analysis-view): charge the manifest group key before building it

Addresses the third automated review round: the composite key copies the
method, origin, template and rules, so that copy is reserved before the key
is stringified.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix(analysis-view): accept a +json suffix only on application media types

Addresses the fourth automated review round: `image/example+json` or
`text/example+json` no longer counts as JSON for origin selection, exclusion
or body shapes, matching the documented `application/json` and
`application/*+json` contract.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix(analysis-view): require the capture parent's exact subject for a manifest

Addresses the fifth automated review round: the manifest cites the report
path and digest, so a parent whose subject is absent or names another path or
SHA-256 is refused at admission, as the capture view already does; the
api-surface view keeps its existing admission.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix(analysis-view): share truncated suffix allocation work

Track suffix progress by the actual truncated prefix and suffix width. Distinct full names can collide only after suffix truncation; sharing that progress prevents quadratic rescanning while preserving shorter free names and existing allocation order.

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

* fix(analysis-view): merge only identifier-like siblings under sibling-variance

Addresses the sixth automated review finding: a bucket merged whenever one
literal carried a digit, folding plain words such as `mine` into the
parameter. The rule now partitions the bucket, merges the identifier-like
siblings and leaves the other literals as their own tools.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix(analysis-view): combine qualifying sibling cohorts

Keep coded identifiers and a qualifying plural hyphenated cohort in one eligibility Set before checking the minimum merge size. Read each literal once and reuse the same membership at the consumer, preserving non-identifier routes and the three-hyphen minimum.

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

* docs(analysis-view): clarify opt-in sibling folding

State the additional sibling-variance selection in the guide, acceptance specification and MCP parameter description. Preserve the existing single-rule behavior and caller-selected inference rules.

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

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
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-19 07:07:37 +08:00
morluto 65884350ff fix(native): retain partial call routes and omitted sites
Separate enumeration outcome from retained facts in the native call-route
owner. Keep every available typed call with its source provider and site,
supplement incomplete kinds with inferred fallback targets, and retain
known routes when fallback fails. Per-reference unknowns take precedence
over aggregate availability; cancellation remains terminal.

Project partial routes before enumeration failures, preserve each omitted
route's relation, and include its call site in the omitted-edge identity
so distinct observations to one target cannot overwrite each other.
Remove the superseded success-only provider/result representation.

Validation: 69 focused cases plus final 30-case rerun, including nine
public SDK MCP regressions; full static/architecture, docs and discovery
checks pass. Controlled-provider source REDs reproduce both route loss
and omitted-site collisions. Real native providers remain unverified.

Closes #5073
Closes #3922
Closes #5128
2026-10-19 06:48:14 +08:00
22ddd82014 fix(uninstall): preflight repeated registration ownership
Read shadowed registrations from the effective JSONC syntax tree before
any client is changed. Share the same typed ownership decision with the
reread before removal, including foreign OpenCode V1 fallbacks. Remove
all admitted duplicates using one member/separator planner and grant
serializer authority only for exact admitted paths.

Setup may remove its explicitly superseded OpenCode V1 path. Preserve
inactive repeated parents, unrelated entries, comments and source backups.
Use the pinned JSONC scanner instead of handwritten string decoding.

Validation: final 86 focused cases and 9 setup acceptance cases passed;
104 additional filesystem consumer cases passed. Static/architecture,
docs and discovery checks passed. A broader 186-case run had one OMP
timeout; its isolated 9-case rerun passed, with timeout cause unverified.
Controlled compiled CLI comparisons verify removal/refusal and backups;
real developer-client execution remains unverified.

Adapted from #4977 at 4cd22d4ec2.
Closes #4976

Co-authored-by: Nikhil Bhagat <knewboy.nykhil@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 06:32:14 +08:00
morluto d1adc60795 test(contracts): align plist and UI finalization regressions
Update public-boundary expectations after the explicit XML unknown-scalar
and native UI deadline provenance changes. Retain raw malformed literals,
independent exact integers, expanded-year ISO dates, and partial UI reports.
Assert successful cleanup releases its lease and failed cleanup retains it
until close retries. Keep malformed-input and true date-range coverage.

The shared number fixture owns CLI/filesystem/MCP expectations. Native
plutil controls confirm integer entities are invalid plist literals, even
though they are valid XML. No runtime behavior or CI gates change.

Validation: 187 source cases, 138 focused public cases, final 97-case
rerun after fixture typing/group changes, full static/architecture checks,
and test discovery (1591 files).
2026-10-19 06:22:32 +08:00
morluto 32f0e51209 fix(session): describe native UI trace source routes
Share source operations with execution owners and derive availability and missing prerequisites from one evaluation. Distinguish authored UI dispatch from native-seed call tracing without inventing a default request mode. Preserve target gates, typed failures and partial results.

Fixes #5071
2026-10-19 03:39:02 +08:00
morlutoandMert Kaya 29910d16b9 fix(apktool)!: select numeric regions using Android b+ qualifiers
Normalize three-digit region aliases at the shared locale owner, preserving
caller labels and physical-table reuse. Reject malformed legacy qualifiers
through the same CLI/MCP contract and table classifier.

Adapt the owner correction and regressions from PR #4918 after checking Android
and supported Apktool qualifier sources. Add public MCP validation and preserve
existing two-letter regional selectors.

Fixes #4916

BREAKING CHANGE: Numeric legacy locale selectors such as es-r419 are rejected.
Use es-419 or b+es+419. Two-letter region selectors remain supported.

Co-authored-by: Mert Kaya <47145541+mertkayacs@users.noreply.github.com>
2026-10-19 03:25:25 +08:00
morluto 50b6975d86 fix(session): admit direct native dispatch metadata providers
Share named metadata source operations between execution and capability discovery. Preserve symbol fallback and typed runtime failure behavior, and cover availability through public MCP workflows.

Fixes #5070
2026-10-19 03:19:33 +08:00
morluto 1d5bf5ff08 fix(session): align call relationship discovery with traversal modes
Declare the missing trace composition and correct call graph prerequisites to require address resolution plus the selected relationship direction. Report forward/backward modes and the actual default independently, preserving missing operations, owner failure reasons and direct partial outcomes.

Share the direction-to-provider mapping across runtime and discovery, reuse the mode evaluator, select defaults by name, and check composition coverage against every canonical enhanced contract. Keep same-endpoint traces callable without relationship reads.

Adapt the missing trace-composition correction proposed in PR #4729 and complete the directly related ownership/refactoring and sibling graph correction.

Validation: 75 source tests and 76 public MCP/schema tests passed; full static/architecture checks, docs and test discovery passed. Controlled SDK/provider workflows verified unchanged catalogs and normalized traces. Small whole-session costs were recorded, with no speedup claim. No native-engine, CLI, Linux/Windows or model tool-selection qualification.

Fixes #4687
Fixes #5068
Co-authored-by: AHMED <aptech356@gmail.com>
2026-10-19 03:08:39 +08:00
morluto 3537e58c1a fix(plist)!: preserve unknown XML scalar identities
Interpret complete scalar source at the XML owner instead of accepting numeric prefixes, permissive date strings or nonempty booleans. Retain malformed content as explicit unknown scalars with original literals and reasons, while preserving the surrounding archive and independent numeric facts.

Share the representation across plist, keyed archive, Interface Builder and entitlement projection; remove the superseded numeric fallback and helpers. Parse date fields as UTC, retain real entity/CDATA semantics, and distinguish native integer/real whitespace without ambiguous whitespace backtracking.

Validation: 276 source tests and 60 compiled/public tests passed, with updated scalar regressions rerun; static, architecture and docs checks passed. Native macOS plutil controls and CLI/MCP source-hash/time-zone controls executed. No Linux/Windows or deep-provider qualification. Tiny completed CLI task costs recorded; no performance improvement claimed.

BREAKING CHANGE: Unresolved XML scalar values now use $plist_type unknown wrappers with element, literal and reason. This replaces the former real/null unknown marker and malformed scalar prefix fallback. Supported extended UTC years now preserve their timestamp.

Fixes #4852
2026-10-19 02:48:09 +08:00
morlutoandAvichal Pandey cee95b2ae6 fix(plist): preserve source semantics in XML data decoding
Adapt the completed-quartet decoder from PR #4981 at 4cf6cac14. Bind raw source spans to validated DOM nodes before entity and line-ending interpretation can change data bytes. Preserve depth preflight, reject invalid data forms and allocate exact decoded output through one shared quartet grammar.

Fixes #4978

Co-authored-by: Avichal Pandey <avichal2018lko@gmail.com>
2026-10-19 02:28:27 +08:00
morluto eb2c3b589e fix(native): retain LLDB evidence prefixes after read failures
Return byte prefixes with tagged read outcomes and exact causes. Share those facts across partial journals and captures, preserve typed primary failures and successful siblings, and keep zero-byte failures unavailable. Retain failed descriptor and root owners through retry.

Fixes #5058
2026-10-19 02:06:07 +08:00
morluto d4e233946a fix(errors): retain secondary failures without replacing primary causes
Give the typed error owner a separate retained secondary-failure collection. Preserve exact direct causes, including caller cancellation reasons and absent causes, while keeping existing diagnostic and live cleanup ownership projections.

Fixes #5059
2026-10-19 01:56:58 +08:00
morluto 292e7bef27 fix(native): bound LLDB PID reads before validation
Derive the read limit from the accepted safe decimal PID plus a tolerated CRLF and overflow sentinel. Retain short-read handling, cached valid facts and existing descriptor ownership and handshake semantics.

Fixes #5057
2026-10-19 01:48:37 +08:00
morluto 36d156ff05 fix(native): retain UI interruption source through final cleanup
Record caller cancellation versus the owned deadline once and share that provenance through helper execution, scenario waits and finalization. Preserve independent failures and partial reports, consolidate scenario failure handling and dispose listeners on every exit.

Fixes #5056
2026-10-19 01:41:52 +08:00
morluto 7201e8a443 fix(native): validate LLDB readback encoding before reporting facts
Decode completed journal rows strictly while retaining valid neighbors and torn-tail semantics. Preserve structured result read, decoding, JSON and schema failures through the typed fallback.

Fixes #5055
2026-10-19 01:41:48 +08:00
morluto 9e259f87c8 fix(native): retain signature roots until descriptor cleanup completes
Give each signature snapshot a private child-resource scope. Its composite
root lease closes those children before removing the directory and retains
both stages across failed retries. Bind local cleanup failures to their
exact owner so child release cannot clear a failed root removal.

Replace the mixed-outcome regression that expected premature root removal.
Cover real descriptor denial, primary/cancellation preservation, MCP close
and same-connection recovery. Expose underlying real codesign test errors
before asserting result fields.

Fixes #5052
2026-10-19 01:24:40 +08:00
morlutoand星闌 8307ea3717 fix(adb): preserve command failure target identities
Keep the selected device, addressed file and actual host operand distinct
at the command owner. Parse known device and file diagnostics before
projection, including AOSP host write/read failures and exact snapshots.
Classify complete captured stderr before shortening display text, and
scan leading notices without expanding the capture into a line array.

Adapt Xinglan233's PR #4655 and extend its source-shaped host controls.
Add built CLI/MCP identity, long-path, recovery and input-preservation
coverage. Real platform-tools and Android devices remain unverified.

Fixes #4641
Fixes #4643
Fixes #5051

Co-authored-by: 星闌 <xinglan233@163.com>
2026-10-19 01:13:57 +08:00
morlutoandMangolycheematcha fd728f271e fix(plist): ignore processing instructions between container members
Exclude processing-instruction nodes at the shared container-member owner
before dispatching plist tags. Keep strict syntax validation and scalar
content rules, with app-resolution and CLI/MCP evidence regressions.

Adapted from Mangolycheematcha's PR #4965, including its boundary controls.
Added built CLI/MCP coverage and verified container fixtures with plutil.

Fixes #4964

Co-authored-by: Mangolycheematcha <288892596+Mangolycheematcha@users.noreply.github.com>
2026-10-19 01:03:32 +08:00
gensokyo-devandKaoru0822 b0b485abff fix(hopper): verify and repair the Linux embedding runtime (#5041)
Verify the distribution Python embedding library during approved Linux Hopper
setup, conditionally repair the matching Ubuntu dependency, and confirm that
the library loads. Share fresh bounded observations with doctor, preserve
unknown configuration and caller cancellation, and join probe exit through
the existing process supervisor.

Validate clean Ubuntu installation, normal-user real Hopper CLI/MCP workflows,
independent ELF/SQLite oracles, cancellation and cleanup regressions, 154
focused checks and all 40 current-head CI checks. Preserve measured artifact
identity, failed attempts and local host-memory guard limitations.

Closes #5015.

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-19 00:11:10 +08:00
gensokyo-dev 23f984b1a8 fix(tests): restore Android prerequisite verification
Supply a real retained resource owner to every Android verifier prerequisite
check, sharing the existing Ghidra verifier cleanup scope. Match recovery
checks to typed Java or archive failures and applicable remediation, preserving
exact close retry authority and primary failures.

Assisted-by: Reimu (gensokyo-skills:reimu-incident-triage)
2026-10-18 12:46:25 +00:00
gensokyo-dev 093ce695f0 fix(tests): preserve temporary fixture ownership
Normalize shared test fixture prefixes using the hygiene verifier ownership rule. Preserve caller labels, existing owned names, and third-party caches; cover retained fixtures and exact cleanup through subprocess regressions.
2026-10-18 11:34:44 +00:00
gensokyo-devandKaoru0822 63c970812b perf(hopper): reuse decoded records for literal string matches (#5017)
Keep complete decoded string records in Hopper's existing search inventory
and directly project literal matches, removing the redundant full record
projection and superseded value-only representation.

Record real Linux CLI/MCP correctness and Evidence checks, shorter warm
queries, negative whole-task costs, native coverage gaps and resource stops.

Fixes #5010.

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-18 19:19:20 +08:00
gensokyo-devandKaoru0822 c12d9eda87 docs(jeb): qualify Linux CE native workflows (#5002)
Qualify the official Linux CE headless launcher and engine-native item references.
Record real native CLI/MCP qualification, costs, failures and integration controls.

Closes #4996

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-18 17:13:04 +08:00
morluto a25eb5fbb4 chore(release): sync 6.7.0 checkpoint
Preserve the immutable 6.7.0 tag ancestry and synchronize version metadata after verified npm publication. Keep newer main performance changes and the full 6.6.0 changelog.
2026-10-18 16:18:49 +08:00
github-actions[bot]andmorluto 27dc42d102 chore(release/6.7.0): release rea-agents 6.7.0 (#4983)
* chore(release/6.7.0): release rea-agents 6.7.0

* docs(release): finalize frozen 6.7.0 contribution notes

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-18 15:40:45 +08:00
7c458f04b9 perf(apktool): avoid duplicated primary strings on request
Add opt-in string_tables_only and CLI --string-tables-only to keep selected
strings once in typed table facts. Preserve existing defaults and legacy replies;
charge aggregate output capacity for the copies actually requested.

Validate shared CLI/MCP behavior with independent XML oracles, three real APKs,
94 source tests, nine compiled boundary tests, 36 completed MCP tasks and three
CLI parity tasks. Preserve full task estimates, negative timings, resource
admission refusal and verification limitations in the measured report.

Fixes #4982

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-18 15:38:16 +08:00
8f7d12e30d perf(analysis-view): reuse validated immutable parents
Reuse successful format validation for exact authenticated immutable native and imported JavaScript results. Preserve schema normalization and alias topology before borrowing source data; owned JavaScript graph wrappers retain their existing metadata-normalization path.

Validated with 130 source tests, 36 compiled CLI/MCP boundary tests, 27 default-deadline integration tests, full checks/docs/build, and paired real retained-Evidence tasks. The report preserves cold costs, previous negative results, incomplete resource-stopped runs, and the limits of offline token estimates.

Fixes #4810

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-18 14:38:00 +08:00
Kaoru0822andClaude Opus 5.5 b83b9caf4a fix(setup): edit the duplicate JSON member that clients read (#4957)
JSON.parse, JSONC parse, and native Claude Code keep the last duplicate
object member, and parseClientConfiguration therefore validates the last
one. The shared setup/uninstall serializer instead edited text through
jsonc-parser's modify and findNodeAtLocation, which select the first
matching member, so setup and uninstall on configurations with repeated
server tables or rea members wrote into a shadowed member and failed
readback.

Locate every JSON text edit by the effective (last) member in one place,
replacing the first-member modify path. Removing an effective member
that shadows an earlier duplicate would activate that duplicate, whatever
its ownership, so the serializer now returns a tagged shadowed_member
failure and setup and uninstall refuse before writing a backup or the
configuration.

Fixes #4952

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 22:15:47 -07:00
morluto bb212ffecb fix(tests): inherit server identity deadline policy
Remove per-case ten-second overrides from MCP catalog and identity checks. Keep the suite-owned bounded ordinary and coverage deadlines, all assertions, and session cleanup.

Fixes #4967
2026-10-18 13:01:44 +08:00
morluto 6b180783ed fix(tests): budget CLI coverage from parsed options
Use the pinned Vitest parser for coverage and shard flags. Keep ordinary CLI workers at 384 MiB and covered workers at the tested 512 MiB with unchanged concurrency. Retain complete coverage and add actual-config regressions for false, provider-only and spaced-shard options.

Validation: 27 focused cases; 16 covered cases with CLI preloaded; 5 docs checks; typecheck/lint; exact discovery of 1578 files. Measured covered control: 22.18s elapsed, 46.52s CPU, about 2.33 GiB sampled summed process-family RSS on macOS arm64. Full Linux CI pending. No runtime/device verification claimed.

Fixes #4960
2026-10-18 12:43:17 +08:00
morluto 8cbaf6c702 fix(tests): isolate command finalization module mocks
Move the five registered WASM finalization cases into the existing per-file isolated project. Preserve all result, exit-code, observation and cleanup assertions. A preloaded shared CLI graph reproduces three failures before the move; the isolated covered packet passes.

Fixes #4959
2026-10-18 12:42:26 +08:00
github-actions[bot]andmorluto 2c4338db1f chore(main): release rea-agents 6.6.0 (#3476)
* chore(main): release rea-agents 6.6.0

* docs(release): finalize 6.6.0 notes and contribution record

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-18 11:39:53 +08:00
Kaoru0822andClaude Opus 5.5 18aa9af2b2 fix(setup): admit only strict JSON for Claude Code configuration (#4944)
Claude Code shared the generic `json` client format, which the shared
configuration reader admits through JSONC parsing with comments and
trailing commas enabled. Native Claude Code reads `.claude.json` with a
strict JSON parser (after a UTF-8 BOM), so setup reported `ready`, wrote
a registration into a file the client rejects as corrupt, and doctor
reported it `aligned`.

Give Claude Code its own `claude_code` configuration format and parse it
at the owning boundary with strict JSON.parse after removing a leading
BOM. Setup, doctor, and uninstall all consume that parser, so comment or
trailing-comma documents are now refused as malformed before any backup
or write, while plain and BOM-prefixed JSON still configure. Clients that
natively accept JSONC keep the `json` format and its behavior.

Fixes #4940

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 19:57:28 -07:00
morluto 1f23e977b5 fix(pwntools): retain exact cleanup owners through finalization
Give retained processes and runtime roots separate cleanup leases. Bind
composed failures to their exact dependencies so process retirement removes
only process labels and root deletion resolves the remaining markers.
Retain local evidence while descriptor cleanup is unresolved.

Preserve completed decoded facts and the original cancellation reason when
final cleanup itself fails, then expose that primary after successful retry.
Remove the superseded root-error composition branch after descriptor gating.

Validation: 68 focused tests across nine files, including real retained file
handles, late process handshakes, failed final cleanup, mixed child outcomes
and cleanup retry. Static, docs (five checks) and discovery (1577 files) pass.
The producer qualification scope is controlled Node/file fixtures with the
real macOS inspector and a simulated availability gate, not native pwntools
or GDB. Task tokens/CPU/process-family memory are unmeasured; no speed claim.

Fixes #4945
2026-10-18 10:51:51 +08:00
morluto 08fbeb8bda fix(pwntools): preserve producer cancellation outcomes
Keep opaque reasons at admission, queued work, command/file projection and
successful final cleanup. Normalize Node's ABORT_ERR envelope only at the
snapshot write API that produced it, using the verified original cause.
Remove downstream signal/name/identity guesses in decoder and debugger
catches, including reply-close and path-probe handling.

The initial regressions failed 15 cases with seven controls passing.
The complete final series passes 68 focused tests, static checks, docs and
discovery. Real owned Node/file fixtures use the actual macOS inspector;
only the Linux x64 availability gate is simulated. No native pwntools or
GDB qualification or performance improvement is claimed.

Refs #4945
2026-10-18 10:51:50 +08:00
47d3c6b1a1 test(perf): verify authentic retained investigation queries (#4814)
Verify retained queries against authentic SQLite ELF and npm histories, retain failed RPC/cleanup/output costs, and make mixed-history verification repeatable. Record the separate validation-reuse bottleneck in #4810.

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-18 02:32:58 +00:00
morluto b33c0df02e fix(pwndbg): reject core mapping paths that escape sysroot
Interpret source-bound ELF64 NT_FILE descriptors once before debugger
launch. Bound counts and pathname coverage by the descriptor, retain exact
path bytes, and refuse relative names or dot segments rather than letting
GDB resolve them against the host. Pin GDB's pathname semantics to Unix.

Preserve the decoded partial observation on refusal, basic-only recovery,
and pre-admission cancellation. Share an exact mapping fixture and replace
the old fixture boolean modes with explicit report producers.

Validation: final series has 33 passing Vitest cases plus five Python
producer controls; 38 Linux x64 provider cases are explicitly skipped on
macOS. Typecheck/lint, docs checks and test discovery pass. Real GDB16.3
and strace reproduce the relative-path escape using only an owned fixture;
compiled admission admits a real absolute-path core and refuses that
relative-path core. Full native pwndbg / Linux x64 remain unverified here.
Task token, CPU and peak-family-memory costs are unmeasured; no speed claim.

Fixes #4942
2026-10-18 10:27:37 +08:00
morluto 5bf4fffc1b fix(pwndbg): prevent core-triggered executable discovery
Occupy GDB's executable slot with its owned /proc/self/exe while loading a
core, then discard it before mapping queries. Select the validated decoder
architecture explicitly and restrict build-ID lookup to the owned empty path.
Preserve cancellation reasons at debugger admission.

Keep the original fixture executable present during syscall verification and
add Debian 13 / GDB 16 coverage alongside Ubuntu 24.04 in the CI definition.

Validation: controlled producer regressions and request-profile controls;
real GDB 16.3 / strace on Linux arm64 shows the old bridge opening the
recorded executable and the fix returning the same mapping without that
access. A cross-architecture gdb-multiarch probe confirms profile selection.
The plugin was controlled; full native pwndbg and Linux x64 remain unverified.
No performance improvement claimed; task tokens/CPU/family memory unmeasured.

Fixes #4914
2026-10-18 10:26:42 +08:00
morlutoandLuigi Pinna 847472acf5 fix(gdb): report close disposition as a capability precondition
Adapt #4732 using canonical provider identity and explicit producer recovery. Keep the valid session retained for caller-selected disposition and preserve default capability error behavior. Refs #4653.

Co-authored-by: Luigi Pinna <luigipinna3@gmail.com>
2026-10-18 10:03:39 +08:00
Kaoru0822andClaude Opus 5.5 270bc38f72 fix(setup): read signed 64-bit TOML integers losslessly (#4937)
Codex and Grok client TOML was parsed with smol-toml's default reader,
which rejects any integer beyond JavaScript's safe Number range. Valid
TOML 1.0 Int64 values such as `max_bytes = 9223372036854775807` made
setup, doctor and uninstall treat the whole file as malformed.

The client TOML owner now parses with `integersAsBigInt: "asNeeded"`,
keeping safe integers as numbers and larger ones exact as bigint, and
rejects values outside signed Int64 before any write. All TOML parses in
the document owner (document read, inline string decoding and the
preserving-edit check) use that reader. The inline-table writer emits
bigint values, and setup and doctor accept a positive bigint
`startup_timeout_sec`/`tool_timeout_sec` instead of replacing or
rejecting it.

Fixes #4917

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 19:02:26 -07:00
morlutoandXander-AJ d5fc7e6ebf fix(rizin): preserve output whitespace before completion markers
Adapt #4044 at the shared normal/interrupted frame owner. Preserve the decoded prefix before byte-budget accounting and cover MCP delivery across framing and UTF-8 boundaries. Refs #4016.

Co-authored-by: Xander-AJ <133286098+Xander-AJ@users.noreply.github.com>
2026-10-18 09:53:59 +08:00
morlutoandTapraj-Pathak 279c590ef6 fix(cli): require cancellation through command finalization
Adapt the forwarding proposal in #4652 with a required signal before optional resource diagnostics. Migrate all callers and cover actual command registration during final close. Refs #4939.

Co-authored-by: Tapraj-Pathak <165466587+Tapraj-Pathak@users.noreply.github.com>
2026-10-18 09:48:11 +08:00
morluto 2ae4e1608f fix(cli): preserve omitted positions in filtered JSON arrays
Normalize only filter-owned selections before streamed JSON and JSONL output, and count the same normalized payload. Keep source values and strict domain JSON validation intact. Refs #4938.
2026-10-18 09:40:02 +08:00
morluto ba98130049 fix(mcp): distinguish schema references from literal metadata
Share schema keyword roles and preserve reference bases for explicit local pointer targets and uncertain intermediate scopes. Retain literal values and canonical validation. Refs #4936.
2026-10-18 09:32:28 +08:00
Ysopwnable 1accac27c3 fix(ci): restore plist, verifier, and TOML capacity checks (#4930)
Correct stale plist and prerequisite fixtures, and replace the TOML serializer's repeated terminal-LF regex scans with a backward scan.

Keep capacity, timeout, source-preservation, backup, and idempotence coverage intact.

Fixes #4931
2026-10-18 01:20:43 +00:00
morluto 9df559b7e7 fix(analysis): gate document selection fallback and completion 2026-10-18 09:19:37 +08:00
morluto 94bc7ed322 fix(browser): retain module resolution through finalization 2026-10-18 09:07:53 +08:00
morluto 52b73ee857 fix(analysis): retain artifact admission cancellation reasons 2026-10-18 08:52:55 +08:00
morluto a8170163b9 test(rust): verify CLI retries exact descriptor cleanup 2026-10-18 08:51:13 +08:00
morluto bbfe6edd60 fix(artifacts): bind inventory and extraction cleanup to exact owners
Preserve typed failed attempts through root admission, hashing, inventory,
nested JavaScript readers and extraction rollback. Remove the intermediate
cause/resource records that discarded canonical cleanup capabilities.

Resolve original provider errors after confirmed release while preserving
primary failures, partial observations and invalidated-handle uncertainty.

Fixes #4928
2026-10-18 08:44:49 +08:00
morluto 2582c267af fix(artifacts): carry exact cleanup authority through scope failures
Return the canonical owner and lease from failed releases. Compose initial
cleanup failures from explicit operation outcomes and failed attempts, and
carry their authority through the shared artifact failure contract.

Keep independent unknown cleanup unresolved. Distinguish operation failures
from completed work whose close rejected. Let the scope own current child
resource labels without replacing its canonical retry lease.

Cover mixed owners, partial release, cancellation provenance, typed close
rejections and independent unknown cleanup through the producing boundaries.
2026-10-18 08:44:41 +08:00
morluto 2183c9598c fix(process): retain kernel limit descriptor cleanup ownership
Use the shared artifact file owner for kernel report reads, preserving the
physical outcome and exact retry lease through failed close and recovery.
Remove the handwritten finally path that discarded this authority.

Cover successful and failed reads with and without a caller scope, denied
retry, blocked replacement admission, and recovery of original error markers.
Adapt the separate descriptor repair proposed in #4863.

Fixes #4926
Co-authored-by: abdooo2235 <abdalrhmantheiv15@gmail.com>
2026-10-18 08:29:57 +08:00
morlutoandabdooo2235 93196449ad test(javascript): verify endpoint certainty through CLI and MCP
Exercise Map omission, escaped prototypes, opaque eval and candidate Evidence
through the rebuilt CLI worker and public MCP transport. Preserve exact static
fetch evidence and SQLite certainty controls.

Co-authored-by: abdooo2235 <abdalrhmantheiv15@gmail.com>
2026-10-18 08:25:28 +08:00
morluto fbfdb6abe9 fix(javascript): distinguish Map lookups from network endpoints
Resolve unchanged Map operations through shared binding, capture, mutation,
and ambient escape facts. Reuse that proof in application and captured
bundle analysis; unresolved get/delete receivers remain inferred candidates.

Preserve repeated native operations without manufacturing receiver escapes.
Retain uncertainty for real escapes, opaque eval, and changed constructor
adder hooks. Converge stored aliases at the producing boundary.

Adapt the JavaScript changes from #4863, excluding its Linux resource fix.
Add captured-bundle parity, repeated-operation and constructor-hook coverage.

Fixes #4853
Co-authored-by: abdooo2235 <abdalrhmantheiv15@gmail.com>
2026-10-18 08:25:06 +08:00
morluto e6c487799b fix(native): preserve resolved plist selection intent
Resolve lookup identity and selected/default origin together in the plist
source owner. Physical-file and conversion failures consume that fact
rather than reconstructing intent from omission of the path argument.
Selected plist artifacts resolve independently of readiness; unresolved
scope admission does not acquire an invented file-selection context.
Remove the superseded resolver and path/requested representation.

Cover direct-target CLI/MCP refusals, physical file conditions, permission
denial, default controls, unresolved scope context and cleanup retry for
both explicit path and selected-target workflows.

Fixes #4923. Related: #4849.

Validation: public CLI/MCP baseline3 failed8 controls passed; final55
passed across7files, check:fast, docs5, discovery1566 and source review.
Actual macOS26.3 plutil CLI/MCP; no Linux/Windows native qualification.
2026-10-18 08:11:46 +08:00
morlutoandapitoagents 7ca7e38717 fix(native): retain bounded plutil refusal diagnostics
Read failed conversion diagnostics from both output streams, retaining
raw bounded excerpts, observed byte totals, exit status and truncation.
Remove only repeated path prefixes from display text and report a useful
reason when the command prints no diagnostic. Reuse native failure
capture bounds rather than expanding accepted nonzero output into errors.

Adapted from PR #4851. Fixes #4849.

Validation: six controlled boundary failures on baseline, including the
stdout-only empty-reason defect; final46 passed across6files, real macOS
CLI/MCP controls, static checks, docs5 and discovery1565. On macOS26.3
plutil uses stderr; stdout-only coverage is controlled output. Separate
selected-target classification defect is reproduced and tracked as#4923.

Co-authored-by: apitoagents <339136651+apitoagents@users.noreply.github.com>
2026-10-18 08:04:33 +08:00
morluto f2b7bbb312 fix(plist): retain known binary non-finite value identity
Make XML numeric uncertainty explicit at its producing boundary, separate
from decoded IEEE NaN and signed infinities. Shared projection preserves
known values using canonical numeric labels; unresolved XML numeric
values remain explicit unknowns. Remove the superseded known-value
wrapper and spelling alias, keeping numeric interpretation with its owner.

Cover public CLI/MCP binary archives, lookalike dictionaries, authored
32/64-bit IEEE payloads, signed zero, and XML parsing/overflow uncertainty.
Document canonical labels and unrepresented NaN payload/sign details.

Fixes #4919. Related: #4906, #4852.

Validation: baseline 2 failures/5 controls through public CLI/MCP; stable
final 439 passed across25files, check:fast, docs:check and discovery.
Native macOS plutil and existing codesign controls executed; no
Linux/Windows run or real Interface Builder engine qualification.
2026-10-18 07:55:51 +08:00
morluto 69324b0f86 fix(native): project decoded entitlement scalars through plist owners
Consume shared value and exact-number projections before JSON validation
so decoded plist scalars cannot leak as ordinary dictionaries. Carry
projection limitations into signature evidence with entitlement context.

Related: #4906. Controlled command-runner regression passed. Real
codesign refused the authored non-finite entitlement; this does not claim
real entitlement support. Existing real signature CLI controls passed.
2026-10-18 07:48:25 +08:00
morlutoandMert Kaya 6c98708793 fix(plist): preserve observed non-finite XML reals
Recognize complete CFPropertyList literals at the XML owner and retain
canonical NaN and signed infinity identity as typed scalars. Keep scalar
wrappers distinct from lookalike dictionaries, share limitation text and
document alias normalization. Incomplete markup retains its existing
compatibility fallback and limitation. Bound token matching before case
normalization using the accepted grammar.

Adapted from PR #4908. Fixes #4906.

Validation: public CLI/MCP regression baseline 2 failed, 4 passed.
Final focused validation 430 passed across 25 files, including controlled
entitlement response coverage in the following commit. Real macOS plutil
CLI, check:fast, docs:check and discovery passed. Binary value identity
remains tracked in #4919; malformed literal policy remains #4852.

Co-authored-by: Mert Kaya <47145541+mertkayacs@users.noreply.github.com>
2026-10-18 07:48:18 +08:00
morlutoandMert Kaya d205c528e3 test(pcap): verify UTC metadata against authored capture records
Use capinfos epoch output and compare public CLI/MCP results with UTC
instants derived independently from the known authored PCAP records.
Avoid comparing product output with the same host-local display text.

Related: #4907, #4909. Syntax checked; live Linux verification not run.

Co-authored-by: Mert Kaya <47145541+mertkayacs@users.noreply.github.com>
2026-10-18 07:40:35 +08:00
morlutoandMert Kaya 796faa0e4e fix(pcap): preserve exact UTC capture instants
Request epoch timestamps from capinfos and normalize them once at the
metadata boundary, preserving nanosecond precision and explicit unknowns.
Bound numeric parsing to the producer representation and cover negative
fractions, calendar limits, malformed output and MCP projection.

Adapted from PR #4909. Fixes #4907.

Validation: 47 passed, 24 platform-specific skips across five focused
files; check:fast and test discovery passed. No live Wireshark or
Linux/Windows capture execution was performed on this macOS host.

Co-authored-by: Mert Kaya <47145541+mertkayacs@users.noreply.github.com>
2026-10-18 07:40:25 +08:00
morluto f0b9ef34c4 fix(verify): request command JSON for Cutter inspection
Use --json-output for the verifier's ij observation. Exercise the actual
verifier-selected arguments through the public CLI and authenticated fixture
bridge, asserting command JSON mode and retained Evidence. The old bare --json
selection fails this control. A live Cutter GUI was not qualified here.

Fixes #4912
2026-10-18 07:34:08 +08:00
morluto f4456dc196 fix(verify): restore owned firmware fixture hashing
Replace the removed hashFirmwareFile export with shared stable verifier hashing.
Keep hashing under the file scope and close MCP/file owners independently before
removing their fixture parent. Preserve the primary failure if cleanup also fails.

The actual firmware entrypoint now reaches its prerequisite gate; descriptor
retry and mixed-cleanup controls pass. Real Linux firmware-engine qualification
was not run here. Refs #4912.
2026-10-18 07:34:07 +08:00
morluto 7d79a6a85b fix(verify): own asynchronous Ghidra prerequisite inspection
Await installation inspection through one shared helper with a real resource
scope at all 13 verifier call sites. Retain exact close owners, complete cleanup,
and preserve primary plus cleanup failures. Keep compiler-only loading lazy and
use the namespace workflow's selected environment.

Add admission contract and native-file lifecycle controls, plus a real script
prerequisite rejection check. Controlled Java/header observations do not qualify
a real Ghidra engine. Combined verification selection: 122 tests passed across
five files; static/format/discovery passed; two scoped source reviews clear.

Refs #4912
2026-10-18 07:33:58 +08:00
morluto 823521085a fix(setup): transfer unscoped registration cleanup ownership
Preserve original RegularFileCleanupFailure capabilities when registration
inspection or maintenance planning has no caller-supplied resource scope.
Keep scoped diagnostics with the scope that already retained the descriptor.

Cover configuration and policy sources, completed and malformed-UTF8 reads,
all four exported reader/planner boundaries, exact descriptor retry after
pathname replacement, no subsequent execution, and scoped cleanup retry.

Validation: 79 tests across four focused files; the historical baseline fails
all 16 new ownership-transfer controls with eight existing/scoped controls
passing. check:fast, format:check, test discovery and six isolated completed
public doctor comparisons passed. Scoped source review found no blocker.

Real temporary descriptors and injected close rejection on macOS arm64 /
Node24.18.0; no native Windows/Linux close faults or provider verification.

Fixes #4910
2026-10-18 07:22:14 +08:00
morluto abb54ce374 test(setup): cover configuration identity regressions
Add direct controls for literal AppData roots, denied MSIX target rechecks,
and unrecognized Claude package deployments. Verify setup refuses mutation
and registration diagnostics remain invalid for unknown package readers.

Six new controls fail at 6d5281407 before the existing 9abdad335 fix; one
existing-behavior guard passes. All seven pass on current main. Keep native
Windows/app behavior distinct from macOS filesystem and platform-selected
adapter checks.

Validation: 82 tests across four configuration/CLI files, 42 header/skill/
lifecycle tests, 144 Ghidra/doctor consumer tests, check:fast, format:check,
and test-discovery verification passed. No provider/target execution.

Refs #4378, #4432, #4496, #4501, #4384, #4676
2026-10-18 07:16:10 +08:00
morluto d818c3d263 fix(doctor): preserve unresolved client configuration evidence
Use entry metadata to discover registration evidence independently of target
readability. Preserve failed metadata inspection as a named invalid diagnostic
instead of treating all lookup failures as absence. Remove the duplicate
boolean existence wrapper; configuration parsing remains the read owner.

Cover native permission-denied targets and metadata, dangling configuration
entries, maintenance exclusion, and public doctor output without mutation.

Validation: 73 focused tests across four files, check:fast, format:check,
and six isolated completed public doctor cost comparisons passed on macOS
arm64 / Node24.18.0. Native Windows/Linux permissions were not verified;
POSIX permission controls skip unsupported hosts and root.

Fixes #4905
2026-10-18 07:10:44 +08:00
morluto d0253c99d6 fix(setup): preserve the selected destination during configuration publication
Replace configuration-specific write-file-atomic delegation with a verified
input snapshot, exclusive private staging, and an explicit canonical-parent
destination. Existing files publish by rename; first-run files use an exclusive
link so a newly created destination is retained. Both consumers read back the
publication owner's selected path.

Retain descriptor and staging cleanup capabilities through recovery and host
retry. Preserve mode and ownership, remove temporary permission handoffs, and
use positional restoration writes. Keep UTF-8 diagnostics with failed cleanup.

Validation: 284 focused tests passed, one skipped across 14 files; check:fast,
format:check, docs:check (including five consistency tests), two source reviews,
and 12 completed isolated public CLI setup/uninstall cost controls passed.
Native Windows/Linux, real editor concurrency, and providers were not verified.
Final-check/publication races and same-inode concurrent writers remain unexcluded.

Fixes #4904
2026-10-18 07:06:15 +08:00
morluto 2fb5e204a3 fix(uninstall): retain configuration publication ownership through recovery
Replace pathname writes with the shared configuration publication owner.
Reject replacement readback and restore through the exact staged descriptor.
Retain initial-read, readback and publication cleanup owners for retry. Share
nested cleanup handling with setup without granting authority to opaque
cancellation reasons. Separate host admission from nested file cleanup gates.

Preserve uninstall file modes, including readable read-only configurations.
Hold the exact staging permission owner through writable-descriptor handoff
and failed mode restoration. Remove the superseded writeText effect contract.

Validation: 206 focused tests across 9 files, check:fast, formatting and
docs:check passed. Three uninstall replacement controls fail on baseline
7e4eed9dd while thirteen setup controls pass. Twelve completed CLI uninstall
tasks preserve equivalent outputs, config, backups and modes0400/0640.
Two read-only source reviews completed. Native Windows/Linux and real editor
races remain unverified; in-place writer exclusion is still not provided.

Closes #4902
2026-10-18 06:50:22 +08:00
morluto 7e4eed9dd4 fix(setup): bind recovery to the published configuration file
Retain the staged file descriptor through atomic publication, readback and
recovery. Refuse detected replacements or edits and restore through that
owned descriptor instead of reopening a pathname. First-run readback failure
now retains the file for manual recovery because pathname deletion cannot
prove exact ownership. Keep both descriptor owners when nested closes fail;
opaque close rejections do not transfer ownership.

Validation: 192 focused checks in 9 files, check:fast, formatting and docs:check
passed. Three replacement regressions fail on the baseline while six existing
recovery controls pass. Six completed setup tasks preserve equivalent config
and backups. Two read-only source reviews completed on macOS arm64 / Node24.
Native Windows/Linux and real concurrent editor behavior remain unverified.
State checks do not exclude an in-place writer after the last check.

Closes #4900
2026-10-18 06:33:04 +08:00
morluto 19329e43bb fix(setup): preserve launcher identity and uncertainty
Use one registration classifier for doctor, maintenance and uninstall.
Preserve unknown identity when launcher resolution fails, and compare native
paths without collapsing symlink traversal. Keep Node argv script selection
separate; ambiguous direct-launch interpretations cannot authorize removal.
Remove the superseded boolean ownership parser.

Validation: 183 focused tests passed, 2 Windows cases skipped; check:fast
and docs:check passed. Five public regressions fail on the baseline, including
foreign registration removal in an isolated fixture. Source review and authored
Node shebang controls completed on macOS with Node 24.18.0. Native Windows,
Linux and real mise upgrades remain unverified.

Closes #4895
Closes #4896
2026-10-18 06:18:34 +08:00
morluto 01cdbbdc55 fix(update): bound package-manager output retention
Reuse supervised raw-byte capture with a 4 MiB aggregate budget. Drain package-manager output through physical close, reject oversized complete replies, and let verbose installs finish with explicitly bounded failure diagnostics. Preserve existing command resolution, selected environment/cwd, runtime prerequisites and signal policy.

Own human progress backpressure and final write acknowledgements without ending borrowed stderr. Continue draining after destination failures and observe outstanding write rejections through their error event.

Validation: 152 checks across 12 focused files, followed by a 44-check rerun including two additional no-deadline waiter controls; typecheck/lint, docs and discovery pass. The public oversized-metadata regression fails on 4f7b5fb60. Fixtures execute owned Node/fake npm/Bun commands; no actual package install or registry request. Native Windows/Linux and exhausting-output/OOM scenarios were not run.

Small completed CLI checks return identical replies and token estimates. Six sequential baseline/candidate tasks separate first-process and two warm repeats; no speedup, true-cold or large-install memory claim. Existing descendant cleanup and signal behavior is outside this output-bound fix.

Closes #4888.
2026-10-18 05:57:57 +08:00
morluto 182e9a268c refactor(process): allow awaiting physical output closure
Allow the existing supervised output wait to omit its deadline while preserving bounded waits and abort-listener cleanup for current consumers. Updater package-manager commands can await physical stream completion without an invented install timeout.

Refs #4888.
2026-10-18 05:57:36 +08:00
morluto 4f7b5fb602 fix(cli): route update flag through verified updater
Normalize the unambiguous root --update alias before output handling and
through direct createCli().serve calls. Reuse the existing output-value scan
and canonical update command, including selected environment, installation
ownership, Windows-aware launch, verification, maintenance and failure facts.
Retain option data and help/check precedence. Reject remaining Incur install
requests: update:false does not disable its explicit installer in0.6.1.
Remove the superseded output-only normalizer name and update its consumers.

Validation:123focused checks/7files pass, including compiled CLI with an owned
rejecting fake npm and direct serve across5formats. Original argument owner
fails6new controls/7pass; isolated baseline fails8workflow checks/2controls
pass. Static/docs/discovery1561 pass. Both scoped source reviews are clear.
Six completed equivalent current-version workflows compare baseline canonical
update with the alias; complete replies identical. Native Windows upgrade,
real package-manager installation and full install costs remain unverified.

The first baseline escaped a Vitest-only builtin mock and installed REA in the
pinned Node global prefix. Removed the newly added package and verified original
Node/npm/Corepack tools. Re-ran using the real builtin spy with synchronized
ESM exports, an independently rejecting PATH shim and owned npm prefix/cache.
Keep heavy tests/profiling stopped. Independent updater output bounds remain
tracked in #4888 rather than changing collection policy in this routing fix.

Fixes #4883
2026-10-18 05:33:21 +08:00
morluto c555caad65 fix(setup): resolve Hermes named-user homes before planning
Resolve the current named POSIX account through OS identity rather than the
caller-selected HOME/USERPROFILE. Preserve the home prefix for repeated
separators using the same joining semantics as the existing ~/ branch.
Require an absolute HERMES_HOME for unresolved names and Windows named forms
before exposing config or skill write destinations. Reuse the shared Hermes
path owner and existing diagnostics across setup, doctor, skills and uninstall.

Validation: 113 checks across seven files pass, including the compiled CLI
setup/doctor/skill/repeat/uninstall workflow over an owned profile beneath the
actual account home and a separate caller home. The isolated unchanged baseline
fails the five new checks and passes44existing checks; final repeated-separator
controls also fail that baseline. Static/docs/discovery1559 pass. Both source
reviewers clear after correcting repeated-separator composition.

Twelve completed setup dry runs compare normal and named overrides, with exact
normal-home replies unchanged. Measurements have source hashes and separate
first/warm runs; no speedup, full Hermes agent/model or native Windows/Linux
qualification claim. No provider software was installed; heavy tests/profiling
remain stopped.

Fixes #4841
2026-10-18 05:17:31 +08:00
morluto 9ac76d3d94 fix(javascript): invalidate stale facts across direct eval
Index unshadowed direct-eval sites once at the semantic owner. Distinguish
lexical rebinding, non-strict eval-introduced names, and possible mutation
of reachable objects. Reuse existing effect timing and capture proofs;
retain original definitions and module origins without inventing assignments
or parsing or executing eval payloads. Query object uncertainty conservatively
across the owning activation so aliases remain covered when names are shadowed
or leave block scope. Retain no binding-by-eval table.

Use the shared facts for values, read provenance, reference effects and child
argument shapes. Preserve indirect/optional/shadowed eval controls, unrelated
activations, immutable shapes, strict-mode bindings and prior primitive
captures. Remove obsolete reference plumbing and the source-wide eval blocker.

Validation: 306 focused checks across 16 files pass, including compiled CLI
and stdio MCP return projections, constructor uncertainty and child-process
consumers. Against the preceding SQLite commit, 9 of 23 selected checks fail
and 14 controls pass. Static/docs/test discovery (1559 files) pass. Two source
reviews found no remaining scoped blocker after alias and sloppy-name fixes.
No analyzed JavaScript, SQLite engine, heavy test or profiler was executed.
Conservative object reachability can intentionally lose exactness; large-case
opaque-query CPU and memory remain unmeasured.

Fixes #4875
2026-10-18 05:08:37 +08:00
morluto fc847ffe35 fix(javascript): preserve SQLite constructor provenance
Use Database spelling only to select a candidate. Reuse shared expression
provenance and indexed mutation facts to omit proven local constructors and
retain known SQLite API origins separately from uncertain names. Match ESM
default/namespace and CommonJS export forms without collapsing their identity.
Keep type-only import origins as ambiguous runtime values, including aliases.

Carry candidate interpretation through the worker schema and graph nodes and
edges with low confidence, explicit limitations and distinct identities.
Remove the name-only SQLite storage classifier. Other storage APIs retain
existing handling; PR4863's separate Map endpoint work is not integrated.

Validation: initial9negative controls fail unchangedf5fcf5216 while4positive
controls pass; final67focused checks/8files plus60provenance-consumer checks/
4files pass. Static/docs/discovery1557 pass. Two source reviewers clear after
correcting namespace constructor identity. Public CLI checks worker/schema
projection and separate exact/candidate identities with the same filename.

Twelve small completed CLI workflows preserve exact known-API replies and
remove false local-class storage facts. No speedup/large-case memory or real
SQLite execution claim. Direct eval's existing generic invalidation defect
remains tracked separately in #4875. Heavy tests/profiling remain stopped.

Additional consumer validation caught and corrected runtime import-equals
classification: require-mode import origins remain runtime values. Three new
SQLite controls cover runtime and erased import-equals forms; compiled
child-process CLI/MCP stdio workflows pass after the correction.

Fixes #4854
2026-10-18 05:06:46 +08:00
morluto f5fcf5216f fix(apktool): retain decode facts across interrupted read stages
Keep one typed accumulation of observed decode facts instead of constructing
partial Evidence only after string projection. Record decoded client/target
before the post-command cancellation checkpoint, parsed metadata before
manifest reads, raw manifest before interpretation, and inventory prefix
before rethrowing its exact original cause. Omit unexamined fields.

Return inventory success or exact failure plus completed prefix through
Result. Keep resource-cap stops distinct from physical failures. Attach
truncation and inventory limitations at their producing stages. Validate
complete results with the named domain schema; centralize partial Evidence
assembly in the existing failure/cleanup boundary.

Validation: six authored stage controls fail unchanged34a88d6c1; 131 focused
checks across7files, check:fast, docs:check and discovery1556 pass. Covers
manifest bytes/XML, interrupted inventory, late cleanup cancellation,
denied removal/retry and existing table interruption. Two source reviewers
clear after moving the command handoff checkpoint. No separate runtime
qualification of that precise handoff race.

Public CLI manifest failure retains metadata/raw manifest with unknown
fields omitted and original failure framing unchanged. Twelve small normal
CLI tasks retain exact replies. No speedup or memory-saving claim; failure
reply estimate189 to610cl100k tokens preserves completed Evidence. No real
Apktool, large-case memory or whole-agent qualification. Heavy tests remain
stopped.

Fixes #4882
2026-10-18 04:36:30 +08:00
morluto 34a88d6c16 fix(apktool): retain completed facts after locale read failures
Return completed string projections with the exact failure cause in Result.
Force partial coverage and disclose the interrupted selection and remaining
unattempted selections without inventing table status or counts.

Assemble execution once from success or partial projection. Preserve the
original failure through existing ownership/cleanup classification, then
retain completed Evidence before snapshot fallback. Existing producer
observations retain precedence. Remove the throw-only projection contract.

Validation: four authored filesystem controls fail unchanged f1edf0da0;
99 focused checks across six files, check:fast and docs:check pass. Covers
late cancellation, denied workspace cleanup and retry, opaque cancellation
between tables, exact physical cause, and CLI/MCP contracts. Public CLI
failure retains completed facts; original failure framing is unchanged.
Two source reviews found no blocker.

Twelve small normal-path CLI tasks preserve exact replies. Measurements
show no speedup; failure reply grows to retain Evidence (cl100k estimate
189 to 696). No real decoder, whole-agent, or large-case memory claim.
Heavy tests and profiling remain stopped.

Fixes #4880
2026-10-18 04:29:41 +08:00
morluto f1edf0da0c fix(cli): preserve APK locale-list schema bounds
Reuse the named domain locales field instead of reconstructing an optional
array from its element type. Preserve min/max selection bounds through the
CLI parser and public JSON Schema while retaining the repeat-flag guidance.
The application service already validates the input; provider authority is
unchanged.

Add a public schema regression and compare advertised Ajv2020 acceptance
and CLI parser acceptance with the owner for omission, empty, valid,
maximum, oversized and invalid-element selections.

Validation: regression fails unchanged d37c34647; 21 focused checks across
4 files, check:fast and docs:check pass. Source review found no blocker.
No real-provider or performance qualification claim. Heavy tests remain
stopped.

Fixes #4876
2026-10-18 04:21:19 +08:00
morluto d37c346477 fix(apktool): retain observed table counts through output limits
Separate physical string-table facts from per-selection output allocation.
Use one owner for legacy and batched projections, preserving caller labels
and normalized-directory reuse. Retain complete counts and at most one
bounded table prefix for later aliases. A skipped cached alias keeps its
known counts; unexamined tables keep explicit unknown counts.

Remove the provider's duplicated parsing and status branches. Charge the
primary legacy copy before selecting output entries and derive limitation
text from the actual emitted count. Reuse the named request input type and
clarify nullable count descriptions without changing their schema types.

Have the workspace report retained raw-prefix bytes, including a stripped
UTF-8 BOM, instead of reconstructing source cost from decoded text.

Validation: 145 focused checks across 8 files; baseline count control fails
unchanged fd6da7092; check:fast, docs:check and discovery 1556 pass. Twelve
small public CLI decode workflows preserve exact normal-path replies and
request/reply token estimates. Timing variation is noise-level; no speedup,
large-case memory or real Apktool qualification claim. Heavy tests/profiling
remain stopped.

Fixes #4874
2026-10-18 04:19:10 +08:00
ShuaixinHuangandmorluto 9e372c6bc9 fix(cli): preserve spaced output flag values (#4823)
* fix(cli): preserve spaced output flag values

* docs(readme): fix French heading spacing

* fix(ci): retain portable build cache until snapshot upload

* test(browser): assert composed Electron cleanup failures

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-18 04:02:45 +08:00
morluto fd6da70920 fix(cli): report string capacity failures during result formatting
Recover completed ordinary command results when pinned Incur formatting
exceeds the runtime string limit before stdout begins. Emit a small typed
memory constraint with an explicit unknown formatted size, the runtime limit
and JSON/filter remediation. Preserve full-output framing and leave normal
CTA, human and token-window formatting with Incur. Never rerun analysis.

Capture canonical failure identity at its logging owner, including thrown
AnalysisError projections. Recovery reads compact captured facts without
revalidating or retaining large diagnostics. Keep destination failure ownership,
prevent a second stdout document and retain quiet SIGPIPE status during recovery.

Validation: 111 distinct focused tests across 10 files; check:fast,
docs:check and discovery of all 1555 test files. Sixteen authored formatter
controls fail unchanged e103ae0ce and pass with this fix. A separate closed
reader control caught and now covers recovery EPIPE handling. The heavy JSON
string-limit regression remains excluded; no large Electron reproduction ran.

Twelve small public CLI artifact inspections preserve identical raw replies
and token estimates in TOON and JSON. Timings are noise-level, with no speedup,
cold-cache, large-result memory or real-provider qualification claim. Sentinel
and generator protocols keep their existing ownership.

Fixes #4806
2026-10-18 04:00:15 +08:00
Abhay Tiwari 6668424343 feat(apktool): share one APK resource decode across selected locale tables (#4777) (#4804)
Project default and multiple selected localized string tables from one
immutable decode pass. Remove the need for repeated APK snapshotting,
digesting, version probing, apktool d invocations, and workspace enumeration.

- Contract: add optional locales array to decode_android_resources input
  schema (bounded to 20 locales) and structured string_tables to output schema
  with per-table status, strings, total_count, unparsed_count, and coverage.
  Preserve legacy strings and locale fields for complete backward compatibility.
- Bounds: enforce 32 MiB aggregate XML read cap and 10,000 aggregate string
  entries cap across batched projections. Handle missing, oversized, and
  malformed tables per-table without failing or losing completed tables.
- Lifecycle: maintain strict descriptor cleanup and provider-owned workspace
  removal in finally, preserving typed cancellation and failure details.
- Adapters: add --locales repeatable option to CLI and update MCP contract.

Validation: 10 service tests, 3 CLI boundary tests, and MCP boundary schema
tests pass. Fast typecheck/lint and turbo docs:check:built pass.
Fixes #4777
2026-10-18 03:57:17 +08:00
pablodiazjorge 3b5605ecd2 fix(plist): keep negative-zero reals distinct from positive zero (#4839)
Plist inspection reported a serialized -0.0 real as the JSON number 0.
- The shared plist projection reports it as a typed real scalar whose value is the string -0, counted in a limitation for keyed archives and inspect_plist.
- The plutil JSON path applies the same value when it decodes -0.
- The XML decoder no longer returns -0 for a mixed-content integer.

Fixes #4834
2026-10-18 03:56:43 +08:00
Prasad Navale 09de00b410 fix(javascript): admit CommonJS bare scoped package directory resolution (#4838) 2026-10-18 03:54:51 +08:00
morluto e103ae0ce5 fix(clients): advance TOML statement lines without document rescans
Reuse the writer's monotonic statement index to find the last physical line
of an assignment. Remove the full line-start scan performed for every key.
Preserve source edits, multiline values, BOM, CRLF and final-line handling.

Add LF/CRLF controls with adjacent assignments, multiline values, an empty
value and EOF without a newline. Validated 85 focused tests across six files,
check:fast, docs:check and discovery of all 1553 test files.

Twelve bounded public CLI uninstall tasks preserve equivalent replies,
configuration and original backups. Warm 10000-assignment elapsed medians
were 0.538/0.520 s and CPU 0.817/0.744 s before/after; small-input timings
were noisy and slower in the candidate. No general speedup, cold-cache,
real-client or analysis-provider claim. Heavy profiling remains stopped.

Fixes #4873
2026-10-18 03:41:32 +08:00
hhang c43bc0bf66 fix(clients): preserve large TOML documents without argument spreads
Fixes #4856
2026-10-18 03:38:12 +08:00
morluto 3460ceac3f fix(browser): retain exact scenario cleanup ownership
Transfer canonical leases through opening, session and storage boundaries. Keep response deadlines separate from actual completion, drain independent owners, and join late opening failure cleanup before confirming recovery. Remove error copies in favor of canonical primary/cleanup composition.

Preserve exact caller reasons, completed observations and independent physical failures through final-close cancellation and retry. Closes #4869.

Validation:155 tests pass across15 focused source, service, CLI subprocess and MCP files; static/docs/discovery1551 pass. Two original-revision controls fail as expected. Bounded authored-session public CLI comparison preserves equivalent Evidence and1282 reply token estimates; no real Chromium qualification or forced-termination recovery claim (#4659).
2026-10-18 03:22:29 +08:00
morluto 16924e275e fix(artifacts): serialize concurrent cleanup lease releases
Share the in-flight release for one exact owner, preserve failed attempts for retry, and notify dependent errors only after confirmed completion.

Validation: concurrency control fails on3fcba0796 with two cleanup attempts; isolated foundation passes38 selected lease/scope/resource tests. Authored lifecycle controls, no native provider qualification.
2026-10-18 03:21:02 +08:00
morluto 3fcba07962 fix(process): preserve file outcomes through exact cleanup recovery
Classify canonical physical file outcomes through the provider mapper before composing cleanup. Retain unknown capabilities as unresolved and replace runtime failure maps with exact leases, including mixed cleanup and partial retry.

Keep completed-read cancellation output and cleanup history. Closes #4865.

Validation: 179 unique focused tests passed across 13 selected files, 7 Linux-only skips; static checks, docs checks and 1551-file discovery passed. Authored Node producers and controlled real-descriptor faults do not qualify native providers.
2026-10-18 02:55:41 +08:00
morluto daa641aa3c fix(artifacts): retain canonical cleanup leases for scope owners
Pair each retry owner with its exact lease and reuse that lease for release and recovery. Remove variant-based lease inference.

Validation: isolated scope foundation passed 23 selected tests; final candidate passed the broader focused suite and static checks. Real-provider behavior was not qualified.
2026-10-18 02:55:19 +08:00
morluto 8a247fc70f fix(providers): preserve artifact producer cancellation facts
Wire sigrok and tshark finalization to the effective signal. Services and file-failure adapters retain exact opaque reasons and completed Evidence instead of guessing from error names. Preserve typed primary outcomes through failed cleanup and recovery.

Validation: latest per-file coverage is 114 passing tests across nine selected files with 14 explicit Linux-only skips; core foundation passes independently. check:fast, docs and discovery pass. Four finalization controls failed before the signal ownership fix. Bounded completed authored-producer service workflows have identical Evidence with no performance improvement claim. Real sigrok/tshark and native Linux/Windows were not verified.

Closes #4858
2026-10-18 02:38:30 +08:00
morluto 147ae9718a fix(process): preserve artifact cancellation through finalization
Consume owner-recorded cancellation reasons, recheck admission after pending cleanup, and keep typed cancellation with completed observations through final workspace cleanup and retry. Existing physical failures remain primary.

Validation: 18 core tests pass; 20 combined boundary controls fail on baseline before consumer migration, and four final-close service controls failed before finalization signal ownership. Focused integration, static/docs and bounded service comparisons pass. No native Linux/Windows or real sigrok/tshark qualification.
2026-10-18 02:37:53 +08:00
morluto 300905c11b fix(android): retain archive admission ownership through cleanup retries
Borrow the archive descriptor from the canonical artifact file workflow. Preserve primary configuration failures, completed archive facts and exact cancellation through final close, retain failed descriptor leases, and join provider and doctor shutdown without nested resource admission.

Validation: three baseline failed-close regressions; 65 passing tests across seven files, nine real CLI and nine real MCP tiny APK inspections, check:fast, docs and discovery. Two read-only reviews clear. Bounded equivalent workflow costs recorded without a speedup claim; no actual OS close fault or native Windows/Linux qualification.

Closes #4846
2026-10-18 02:19:12 +08:00
morluto f1d18859eb fix(artifacts): preserve opaque ZIP cancellation reasons
Use the canonical artifact cancellation checkpoint for enumeration, open and extraction instead of dropping the caller reason in a ZIP-specific helper.

Validation: both new ZIP reason controls fail on the unchanged baseline and pass on the candidate; the focused seven-file run passes 65 tests. Native Windows/Linux were not run.
2026-10-18 02:18:55 +08:00
morluto 662fad9072 fix(cli): finalize doctor cancellation after failure logging
Make the signal boundary own logging and projection as it does for other cancellable commands. Preserve SIGINT/SIGTERM exit disposition after joined doctor cleanup without changing generic failure classification.

Validation: both public signal controls fail on the baseline with exit1 and pass with130/143 after the fix; selected Java groups are empty. Twenty focused tests across five files, check:fast, docs and discovery pass. Authored launcher fixtures do not establish real JVM or native Windows/Linux behavior.

Closes #4848
2026-10-18 02:07:41 +08:00
morluto ceeff5a417 fix(android): own Java prerequisite probing and readiness shutdown
Run the selected module-listing command through owned supervision, preserve completed module facts through finalization, and retain failed cleanup in provider/doctor scopes. Join complete availability calls on shutdown, preserve opaque interruption reasons, and validate numeric Java facts before reporting them.

Validation: 223 passing focused tests across 21 affected files, one platform skip; controlled baseline returns before child exit; real doctor cancellation leaves the selected Java group empty; nine real CLI and nine MCP tiny-APK inspections pass; six bounded MCP cost comparisons preserve normalized results. Native Windows/Linux and actual JVM failure modes remain unverified. Separate CLI exit disposition and archive failed-close findings remain open.

Closes #4845
2026-10-18 02:00:29 +08:00
morluto 964decd989 fix(providers): preserve live cleanup ownership through retries
Share exact command/helper cleanup adoption across Java probes. Preserve complete live resource labels and explicit unknowns through inspection leases, and retain unresolved ownership when a cleanup capability is unavailable.

Validation: focused cleanup, provider/doctor and Kuna consumer checks; shared Ghidra lifecycle and public doctor regressions. Native Windows/Linux provider behavior remains unverified.
2026-10-18 01:59:10 +08:00
morluto 3ad1032b11 fix(android): finalize startup diagnostics before reporting failure
Record the first transport failure before SDK teardown, then capture terminal stderr after owned retirement. Preserve exact opaque cancellation reasons and keep foreign cleanup ownership separate through retries.

Validation: 119 focused tests across eight files; five selected baseline regressions; check:fast, docs:check, test discovery; nine real CLI and nine real MCP tiny-APK inspections. Native Windows/Linux startup failures and actual JVM OOM remain unverified.

Closes #4844
2026-10-18 01:28:36 +08:00
YsopwnableandShuaixinHuang 3ddb2e4cf7 fix(ci): handle cache eviction and composed cleanup assertions (#4824)
* fix(ci): handle eviction when staging portable build cache

* docs(readme): fix French heading spacing

(cherry picked from commit 0ccd0e6999)

* test(ci): verify observed cache coverage during eviction

* test(electron): assert composed launch cleanup diagnostics

(cherry picked from commit e1466dc480e715b427cddfc3569b2baf03574405)

---------

Co-authored-by: ShuaixinHuang <1836656514@qq.com>
2026-10-18 01:08:14 +08:00
morluto 7016951394 fix(process): retain diagnostics through bounded output finalization
Separate verified process cleanup from output closure. Bound additional retention from owned stop entry through cleanup retry, drain before detaching capture, and mark unreadable or abandoned output incomplete. Release owned event-loop references on drain timeout without changing the proven cleanup disposition; preserve borrowed handle reference state.

Cover held and failed cleanup, late tail, bounded drain, stream errors, borrowed authority, and a real POSIX inherited-stderr release barrier. The real pipe control drops its final ten bytes on the baseline and retains them with this change. Focused consumer checks passed, including simulated Windows ownership; native Windows and real provider failure scope remain unverified.

Android still constructs its startup error snapshot before retirement; that consumer correction remains separate work.

Fixes #4840
2026-10-18 01:03:19 +08:00
morluto 3fa972af7c fix(android): preserve typed JVM memory failures during loading
Translate actual OutOfMemoryError at the bridge boundary before upstream stringifies it. Preserve observed heap bytes, missing reasons, coroutine context and cancellation; retain SessionHolder ownership and load options. Restrict heap-specific remediation to known heap reasons and parse named bridge failure contracts once.

Coordinate bridge protocol v4, clarify doctor configuration versus target capacity, and preserve the memory primary across cleanup failure/retry. Verify controlled immediate/late/inline continuation outcomes with the pinned JAR and real JDK, focused tests, and tiny real CLI/MCP method workflows.

Actual heap exhaustion, the reported APK, pre-handshake failures, Linux and Windows remain unverified; no stress or profiling run.

Fixes #3517
2026-10-18 00:48:12 +08:00
morluto 9fa6d2b254 fix(android): preserve primary failures through cleanup retries
Compose cleanup failures with the canonical primary error and bind current resource attribution to the retained acquisition record. Remove the stale diagnostic alias and duplicate failed-handle representation.

Validate the strengthened controlled cleanup regression in an isolated checkout; it fails on the baseline's stale resources and passes with this commit. Real JVM cleanup failure is unverified.

Fixes #4835
2026-10-18 00:47:46 +08:00
morlutoandTaksh 63c65a2243 fix(android): recover original bodies when Java source is omitted
Resolve smali using frozen raw method names and full DEX descriptors. Select
one complete block before truncation for missing Java, static initializers
and decompiler marker fallbacks. Reject ambiguous or incomplete blocks and
preserve upstream disassembly failure notes.

Bind method replies to the selected descriptor with bridge protocol 3.
Remove consumer placeholder/error-prefix interpretations and the superseded
blanket overload fallback rejection. Producer failures now use the shared
failed-tool path before truncation. Migrate protocol fixtures and consumers.

Adapt the direct selector guard from PR #3450; recover original bytecode
instead of inferring a holder from generated comments.

Validation: 122 focused tests in 9 files; real pinned-JAR baseline regression
fails and candidate passes, including overloads, aliases after generation,
branches/handlers, 16-byte output and negative selection controls. Nine real
CLI and nine MCP fixture calls pass with parity/identity/digest/no-mutation
checks. Static/docs checks and two source reviews pass. Six completed real
MCP cost workflows recorded; same calls with improved source, no speedup
claim. Tiny authored APKs, macOS/JDK 21 only; reported editor APK and native
Linux/Windows unverified. Heavy profiling and stress remain stopped.

Fixes #3443

Co-authored-by: Taksh <takshkothari09@gmail.com>
2026-10-18 00:25:36 +08:00
morlutoandbalkan16 742b6f587f fix(android): preserve native and abstract body absence
Freeze body-absence access flags with stable parsed method identity, before
code generation can rewrite method metadata. Skip source slicing for these
methods and return unavailable empty source with explicit native/abstract
markers, rather than attributing neighbouring members to the selected method.

Adapt the access-flag guard and tiny Smali fixtures from PR #3527. Extend real
metadata and public CLI/MCP regressions for native, static native, abstract,
concrete overloads and re-reading frozen facts after neighbouring generation.
Rename the expanded method-body verifier and remove its old entry point.

Validation: 72 focused tests in 3 files; real pinned-JAR baseline control
fails and candidate passes without triggering code generation; 6 real CLI
and 6 MCP fixture calls with parity, identity and unchanged APK; docs/static
checks and two source reviews. Six measured real MCP workflows, no speedup
claim. Tiny authored APK only; reported BinaryEye, Linux/Windows and browser
workflows unverified. Heavy profiling/stress remain stopped.

Fixes #3488

Co-authored-by: balkan16 <57933796+balkan16@users.noreply.github.com>
2026-10-17 23:53:23 +08:00
Prasad Navale 3c1cce78ee fix(domain): admit Qualcomm Hexagon (EM_HEXAGON, 164) ELF binaries (#4819)
* fix(domain): admit Qualcomm Hexagon (EM_HEXAGON, 164) ELF binaries

Resolve missing ELF admission case for Qualcomm Hexagon DSP targets (e_machine = 164, EM_HEXAGON).

- Add 'hexagon' to BINARY_ARCHITECTURES and HeaderArchitecture
- Map EM_HEXAGON (164) in elfArchitecture() to 'hexagon'
- Include 'hexagon' in GhidraProvider's SUPPORTED_ARCHITECTURES
- Gracefully decline 'hexagon' with Ghidra-directed diagnostic in Hopper and IDA
- Add unit tests for Hexagon ELF parsing and adapter admission checks

Fixes #4781

* fix(hopper,ghidra): add Ghidra guidance to Hopper Hexagon refusal and add Ghidra admission regression test
2026-10-17 23:52:03 +08:00
morluto b27e1a74c9 docs(i18n): complete Malayalam locale registration and links
Resolve eight nested README links from their owning directory. Preserve the
shared README/website inventory contract by registering Malayalam with an
explicitly untranslated catalog and the existing visible, noindex English
fallback. Regenerate the shared website navigation and catalog references.
Existing parsed translations remain unchanged.

Validation: docs check, full website check (401 HTML pages, 27790 local
references, 19 tests), all 19 generated Malayalam fallback pages, and two
source reviews. No Malayalam website translation or browser layout claim.

Fixes #4821
2026-10-17 23:39:40 +08:00
morlutoandAbhay Tiwari dc2e8117b6 fix(apktool): distinguish document failures from entry omissions
Return tagged string-document parsing failures instead of fabricating one unparsed entry. Preserve XML failure reasons and publish unknown counts with partial coverage; count only examined unsupported entries in validated documents. Migrate every consumer, remove the superseded throwing XML wrapper, and retain completed failure Evidence through cancellation and failed cleanup.

Adapt the document-versus-entry distinction from PR #4804 using the shared Result and XML owner. Multi-table projection and its aggregate bounds remain under review in #4777.

Validation before incoming main rebase: 229 focused tests in 15 files, four baseline controls fail, static/docs checks and two source reviews. Twelve tiny actual MCP workflows and four CLI controls; authored launchers only, no real Apktool/JVM/Windows qualification.

Fixes #4820

Co-authored-by: Abhay Tiwari <abhaytiwari0821@gmail.com>
2026-10-17 23:27:29 +08:00
rodricksz4h5 bec88b9d77 fix(javascript): retain CommonJS exports defined with Object.defineProperty (#4725)
* fix(javascript): retain CommonJS exports defined with Object.defineProperty

tsc, Babel and Rollup emit Object.defineProperty(exports, name, descriptor) for CommonJS re-exports. The semantic projection only recognised assignments, so these exports were missing from export_names and exposes edges. Recognise the call on exports or module.exports with the existing unmodified-global and receiver checks, track its receiver lifetime like an assignment, and link a data descriptor's value like the assigned value; an accessor's result stays unknown.

* fix(javascript): unwrap assignment chains in defined CommonJS export values

A descriptor value such as holder.parse = require("./parser").parse lost its require origin because only the assignment path took the rightmost assigned value. Do it once in the shared export builder.
2026-10-17 23:23:47 +08:00
ShuaixinHuang 6b0113a534 fix(browser): preserve URL paths when removing credentials (#4731)
* fix(browser): preserve URL paths when removing credentials

* test(browser): verify URL userinfo through real V8 captures
2026-10-17 23:23:37 +08:00
ShuaixinHuang 68c3c5b538 fix(native): preserve leading U+FEFF in Apple metadata names (#4730)
* fix(native): preserve leading U+FEFF in Apple metadata names

* test(native): run property metadata regressions on macOS
2026-10-17 23:23:27 +08:00
kraken-503 af67ad5047 docs: add ml translation (#4792)
* docs: add malayalam translation

* docs: add ml hyperlinks to other docs

* docs: fix ml hyperlinks

* docs: fix ml hyperlinks

* docs: add english hyperlink in README_ml.md

* docs: fix README_ml.md

* docs: fix README_ml.md

* Update links and headings in README_ml.md
2026-10-17 23:22:58 +08:00
Shivam Dwivedi c1fe0d653d fix(setup): recognize shared CLI launcher aliases (#4803) 2026-10-17 23:22:43 +08:00
morluto d38f099692 fix(apktool): disclose platform and release advisory windows
Carry the selected host into the version owner, disclose the Windows follow-up before 2.9.3, and bound the 3.0.x resource type-name validation warning before 3.0.2. Share warning formatting and preserve reported spelling, prerelease semantics and exact dirty-release handling.

Verify the upstream producing-boundary patch instead of assuming a file-writer sanitizer. Explicitly label inclusion of the tagged 3.0.0 release line as source-derived; version and host guidance do not establish engine contents or backports. Retain warnings with completed Evidence during cancellation and failed cleanup.

Validation: 222 focused tests in 15 files, 16 baseline controls fail, static/docs checks and two source reviews. Twelve tiny actual MCP workflows and six CLI controls with authored launchers; no real Apktool/JVM, native Windows, exploit or true cold verification.

Fixes #4818
2026-10-17 23:16:13 +08:00
morlutoandGovindu Sasanka 63696d2773 fix(apktool): retain advisory facts from the version observation
Parse the launcher version once to own both compatibility and CVE-2024-21633 advisory facts. Inspection and decoding consume those facts, including completed Evidence retained during cancellation. Preserve source spelling, exact dirty-release handling, prerelease semantics, and existing version-only failure diagnostics.

Adapt the Apktool warning policy from PR #3440. State that a reported version does not establish engine contents or downstream backports; avoid recommending 2.9.2+ as generally safe. Other advisory applicability and release-range questions are tracked in #4818.

Validation: 194 focused tests in 15 files; final 87 source checks; five baseline controls fail; static/docs checks; two source reviews. Twelve tiny actual MCP workflow comparisons and four CLI controls; authored launcher only, no real Apktool/JVM/Windows or exploit verification.

Fixes #3438

Co-authored-by: Govindu Sasanka <31107592+GovinduSasanka@users.noreply.github.com>
2026-10-17 23:08:00 +08:00
morluto 6385ef4355 fix(apktool): derive locale size from resource configuration fields
Allow the 23-character script-region-variant representation emitted by Apktool, retaining the shared admission grammar and request aliases. Exact discovery and CLI/MCP selection consume the same bound.

Validation: 67 focused tests across four files; five controls fail on the baseline; static/docs checks and two source reviews. Twelve ordinary tiny CLI/MCP task comparisons preserve Evidence; two actual authored-launcher long-locale CLI/MCP calls select the table. No real Apktool/JVM or native Windows verification.

Fixes #4817
2026-10-17 23:00:26 +08:00
morlutoandOke Iyanuoluwa E. 98886adddf fix(apktool): disclose tables outside exact locale selection
Inventory observed entries through the owned artifact traversal, preserve other configuration names, and disclose non-regular tables with partial coverage. Share request alias normalization with exact table classification and preserve filesystem failures.

Adapt the disclosure policy from #4738. Correct the fixture command separator so all advertised tables are actually created.

Validation: 164 focused tests; check:fast; test discovery; docs:check. Two regression controls fail on the baseline. Twenty-four tiny authored CLI/MCP comparisons; no real Apktool or Windows verification.

Fixes #4612

Co-authored-by: Oke Iyanuoluwa E. <oke.iyanuoluwa12@gmail.com>
2026-10-17 22:55:08 +08:00
morlutoandPeter cc038da218 fix(apktool): attribute decode refusals to the selected APK
Carry caller-selected target identity separately from launcher selection. Only fully collected ordinary nonzero decode exits represent target refusals; startup, preparation, capture and signal failures retain provider attribution. Reuse one command/preparation output projection for probe and decode errors.

Adapt the target-path correction from PR #3462 (08d3d0d3a0) and extend its producer-boundary classification and regression coverage.

Validation: 131 focused tests in 12 files, final 50 source checks, build, typecheck/lint and docs. Ten regression controls fail on baseline 4844ae812. Twenty-four tiny actual CLI/stdio MCP refusal tasks preserve every projected field except the corrected message/path. Authored launchers only; no real Apktool/JVM or native Windows claim. Heavy stress/profiling remained stopped.

Closes #3340

Co-authored-by: Peter <304086198+peter-research@users.noreply.github.com>
2026-10-17 22:44:00 +08:00
morluto 4844ae812b fix(apktool): own file and workspace finalization
Replace raw descriptor and workspace bookkeeping with artifact file ownership, canonical chunk reads, private roots and cleanup leases. Retain failed owners, fence replacement acquisition, release successful peers once, and preserve completed Evidence and physical failures through final cleanup and cancellation.

Use the shared directory reader and enforce the examined-entry budget before special-file omission. Keep command and artifact retry ownership separate by exact lease identity; remove superseded reads, implicit traversal and suppressing closes.

Validation: 163 focused tests in 20 files, build, typecheck/lint, docs and discovery passed. Six public controls fail on clean baseline 11bcf4425. Tiny FIFO, descriptor denial/retry, late allocation and mixed outcomes are controlled fixtures. Twenty-four completed CLI/stdio MCP comparisons return byte-identical replies; no speedup claim. Actual Apktool/JVM, natural OS denial and native Windows remain unverified; heavy stress/profiling stayed stopped.

Closes #4812
2026-10-17 22:36:51 +08:00
morluto 11bcf44252 fix(apktool): retain command cleanup ownership through recovery
Admit provider operations through one command resource scope, retain exact process and preparation-helper cleanup capabilities, and bind original failures to their live cleanup leases. Keep unknown recovery capabilities unresolved and fence their workspaces.

Interpret version output at command completion, preserve opaque cancellation and compiler output, and attribute prerequisite failures to the consuming operation. Prepare Darwin ownership outside the short version deadline.

Validation: 106 focused tests across 11 files; final 44 source regressions; build, typecheck/lint, docs and test discovery. Eight new controls fail on the clean baseline. Tiny authored launchers exercise process cleanup and public CLI/stdio MCP; actual Apktool/JVM and native Windows remain unverified. Heavy stress/profiling remained stopped.

Closes #4808
2026-10-17 22:22:47 +08:00
morlutoandJ0UH 376eeca770 fix(ghidra): prepare ownership before Java probe deadline
Allow independently bounded Darwin helper compilation to finish before the five-second Java deadline starts. Keep Linux process-table preparation under command supervision.

Share preparation failure translation between both stages so physical compiler outcomes, exact cancellation reasons and cleanup capabilities retain one owner. Adapt contributor PR #4795 and extend preparation handoff and retry controls.

Fixes #4793

Co-authored-by: J0UH <217747670+J0UH@users.noreply.github.com>
2026-10-17 22:06:16 +08:00
morluto f4322b15ed fix(javascript): bound semantic unknown retention at projection
Admit unknowns before canonical construction and provenance interning. Offer independent per-file and application capacities through CLI, Evidence and the owned analysis worker, and reject over-allocation during streamed decoding.

Report examined omitted emission occurrences by family and reason without retaining discarded identities. Preserve omission-free graph identities and distinguish node truncation from overall partial coverage.

Fixes #4797
2026-10-17 21:57:31 +08:00
morlutoandAniketXD 926e18b3fc fix(native): report PE resource subject architecture
Reuse the canonical COFF decoder and one optional-header magic rule. Preserve unknown architecture for conflicting headers and unproven managed OS overrides; assert subject parity through CLI and MCP.

Adapted from AniketR10/rea-aniket PR #4801 (250c9d726f), with expanded ARM and late-cancellation coverage.

Fixes #4800

Co-authored-by: AniketXD <aniketrawat826@gmail.com>
2026-10-17 21:44:54 +08:00
morluto e7df00e807 fix(electron): retain process and transport cleanup owners
Retain independent teardown leases, block capture admission until recovery, and preserve cancellation through failed finalization. Reuse pending close operations and require fresh Windows lineage plus passive family absence proof without cached descendant kills.

Fixes #4805
2026-10-17 21:40:46 +08:00
morlutoandhhang f9de41371f fix(electron): own fuse cleanup through adapters and shutdown
Adapt the prepared descriptor lifecycle repair from xu91102/rea dbb7db16. Bind exact file cleanup leases, retain typed physical failures and cancellation reasons, and join CLI/MCP finalization and active preflight. Share canonical Evidence construction and retain completed reports across reader handoff cancellation.

Scope active capture admission through shutdown and gate launch after close. Preserve successful descriptor peers and retry only unresolved owners. Separate process/transport retry ownership remains tracked in #4805.

Validation: 168 focused checks passed, one Windows-only skip; built CLI and actual stdio MCP controlled descriptor recovery; build, static and docs checks. No natural OS fault or real Electron launch/teardown claim. Ordinary full replies and Evidence IDs remain byte identical across 30 small CLI tasks.

Fixes #4658

Co-authored-by: hhang <169027046+xu91102@users.noreply.github.com>
2026-10-17 21:24:00 +08:00
2d84c800bc perf(android): filter class pages before bridge serialization (#4791)
Filter class names at the native owner before serialization, and resume matching pages through contiguous inventory positions without a second index or query-result cache. Preserve selected source Evidence, query identity, complete top-level coverage and shared CLI/MCP behavior.

Fixes #4776.

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-17 13:21:37 +00:00
Ysopwnable 060eb43b54 docs: celebrate 300,000 GitHub stars 🎉 (#4778) 2026-10-17 21:12:16 +08:00
morlutoandKaoru0822 bf54c46264 fix(electron): share executable architecture facts for fuse inspection
Own ELF machine and canonical family in the fixed-header parser. Fuse inspection consumes validated ELF facts and complete COFF machine values while retaining null for unknown or incomplete headers. Binary target, MIPS and Go consumers reuse the parsed facts.

Adapt public CLI controls and fixture builders from #4788; extend x86, ELF byte order, malformed and unknown headers, MCP projection and byte preservation coverage. 430 focused tests passed, one Windows-only skip. Five authored inert fixtures have full actual CLI/stdio MCP Evidence parity; real Linux/Windows Electron apps remain unverified.

Fixes #4786

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-17 21:10:07 +08:00
Ysopwnable bd3210eb24 fix(ci): format keyed archive CLI regression test (#4796)
Apply the locked formatter to the keyed-archive CLI/MCP regression test. Preserve all assertions and runtime behavior.
2026-10-17 13:05:59 +00:00
morluto 22a0746f90 fix(wasm): retain cleanup ownership through WABT recovery
Bind runtime and descriptor cleanup to exact retry leases and retain command supervisors until stop is confirmed. Preserve primary errors, partial Evidence and finalization cancellation reasons.

Validate with 88 focused tests, official WABT 1.0.42 CLI/MCP verifier (51 cases), build, static and documentation checks. Command stop failures use controlled source seams; real permission failures and target execution are unverified.

Fixes #4787

Fixes #4798
2026-10-17 21:02:50 +08:00
morluto 7aa4b98c98 fix(plist): own XML values and numeric facts in one decoder
Interpret the validated, depth-bounded DOM directly. Collect source number
facts during the same traversal and share them with keyed archives, Interface
Builder and native plist projections. Remove the second XML parse, key aliases,
source-location rewriting and both duplicate XML numeric scanners. Preserve
character references, container/null behavior, safe own-property construction
and prototype omission accounting.

Decode signed hexadecimal integers and combine real text/CDATA. Retain exact
integer identity when a finite integer exceeds even Number's finite range,
using the shared scalar projector. Keep unsupported mixed integer syntax
conservative and report incomplete metadata.

Adapt @apitoagents's hexadecimal regression and literal-parsing proposal from
PR #3475. Native plutil controls establish the supported hex/real cases and
confirm that the synthetic 401-digit overflow case exceeds native bounds;
retaining accepted input does not claim native-format support.

Validation: 274 focused checks in 30 files pass, one skipped; build,
check:fast and docs:check pass. Built CLI/actual stdio MCP, native plist and
Interface Builder controls cover kinds, references, exact digits and digests.
Eight baseline checks fail; native binary conversion independently verifies
updated CDATA real expectations. 48 tiny completed CLI cost comparisons
preserve ordinary XML/binary and all three native replies byte for byte.
Two read-only reviews clear. No statistical speed claim, heavy profiler,
investigated-target execution or real deep-provider verification.

Fixes #3373
Fixes #4042
Fixes #4789
2026-10-17 20:40:55 +08:00
morluto 78cf6100f3 fix(plist): preserve complete XML dictionary key identities
Use the validated DOM's character-data key names for both identity and
prototype omission. Replace first-child reinterpretation with canonical,
collision-free aliases inside the pinned decoder boundary; restore names
through the existing dictionary projection. Alias every spelling of the
same name to preserve duplicate assignment order. Handle empty key spellings
and keep scalar key elements outside aliasing. Preserve value text and bytes.

Adapt @thangldw's tests and proposal from PR #3426. Avoid the proposal's
closing-content scanner and re-escaping source names. Add duplicate, empty,
alias-collision, scalar-key, native fallback and Interface Builder controls.

Validation: 180 focused checks across 20 files pass, including built CLI and
actual stdio MCP, build, check:fast and docs:check. Baseline public inspection
truncates main to ma. 24 tiny completed CLI cost controls preserve ordinary
XML/binary full replies and Evidence IDs byte for byte. No statistical speed
claim, heavy profiling, investigated-target or real deep-provider execution.

Fixes #3371
2026-10-17 20:28:01 +08:00
morluto 2cf9c28f27 fix(apple): preserve decoded keyed archive scalar identity
Move plist projection into the graph owner and identify generated scalar
wrappers by producer identity through every dictionary/UID consumer. Remove
the intermediate JSON clone that erased those facts. Literal wrapper-shaped
dictionaries remain dictionaries; scalar $top values fail as malformed.
Keep numeric projection after graph interpretation, preserve unknown counts,
and retain binary prototype-key partial facts with an explicit limitation.

Adapt @Jax367's relevant scalar proposal and public regression from PR #4317.
Add scalar-root, literal-root and binary prototype controls. Correct only
the captured Foundation data node expectation and its manifest digest;
original producer bytes and source are unchanged. Unrelated PR changes excluded.

Validation: 175 focused checks across 23 files pass, one skipped; build,
check:fast and docs:check pass. Public CLI and stdio MCP cover both encodings.
Baseline scalar controls fail; 24 tiny completed CLI cost comparisons retain
ordinary full replies and Evidence IDs byte for byte. No statistical speed
claim, heavy profiling, target execution or real deep-provider verification.

Fixes #4295
Fixes #4785
2026-10-17 20:20:53 +08:00
morluto 5d6029ea62 fix(apple): derive launchd domains from owning directories
Use the matched Contents/Library/LaunchAgents or LaunchDaemons suffix to
select a plist's domain. Remove the independent scan of every ancestor.
Keep regular-file, application-root, sidecar and suffix admission rules.

Adapt @bnkaiteam's proposal and ZIP boundary regression from PR #3504,
adding public CLI/stdio MCP controls for nested bundles, case variants,
misleading ancestors and excluded paths.

Validation: 53 focused checks, build, check:fast and docs:check pass.
Two new controls fail before the fix. 12 completed actual built CLI cost
controls preserve the ordinary reply and projection ID byte for byte.
No heavy profiling, target or real deep-provider execution; npm release
and whole-agent costs remain unverified.

Fixes #3353
2026-10-17 20:10:12 +08:00
morluto fc935d0df3 fix(mobile): derive runtime hints from classified components
Use the owning DEX/JVM and Ark/JavaScript component lists for runtime
summaries. Remove the independent filename checks and Harmony path copy.
Preserve native format checks and framework path conventions.

Adapt @eco-null's proposal and regressions from PR #3627. Cover all eight
supported source extensions and unrelated content, plus the Android
inverse mismatch discovered during consumer review: .dex/.class files
had no bytecode components but produced runtime hints.

Validation: 44 focused checks, build, check:fast and docs:check pass.
Six Harmony assertions and one Android built CLI assertion fail before
this fix. 36 actual built CLI cost controls preserve four ordinary
complete replies and projection IDs byte for byte. No heavy profiling,
target or real deep-provider execution; npm release unverified.

Fixes #3350
Fixes #4782
2026-10-17 20:02:26 +08:00
morluto a425b3e528 fix(mobile): classify authenticated file occurrences as components
Select file and slice identity once before Android, Apple and Harmony
classification. Keep directory/link facts in source inventory and Apple
anatomy rather than deriving code roles, runtime hints or bridges from them.
Preserve occurrence order, multiplicity and existing unavailable facts.

Adapt @flyl1u's proposal and public CLI/stdio MCP controls from PR #3830,
extending the controls and structural fix to Apple and slice/link semantics.

Validation: 80 focused checks, build, check:fast and docs:check pass.
Four package controls fail before the fix. 72 tiny completed source cost
tasks preserve all regular-file control replies and projection IDs byte
for byte. No heavy profiling, target or real deep-provider execution;
full agent/public-task costs and published npm package unverified.

Fixes #3349
2026-10-17 19:58:18 +08:00
morluto 149a0fc06c fix(apple): derive bundle anatomy from directory facts
Build separate directory and authenticated-file indexes before applying
anatomy name preferences. Share those facts across application roots,
nested bundles, layout, metadata, executable candidates and signing paths.
Remove mixed child-name interpretation and downstream membership repairs.

A leaf Contents/Versions resource no longer changes a shallow layout, and
leaf files/symlinks cannot manufacture application roots. Real explicit
and implied directories retain their existing semantics and spelling.

Adapt the directory-identity proposal and source regressions from PR #3505
by @bnkaiteam, extending it to metadata file selection and root admission.

Validation: 57 focused checks, build, check:fast and docs:check pass.
30 tiny completed source projection cost controls preserve ordinary deep
and versioned replies byte for byte. No real-provider/target execution,
heavy profiling or published npm verification.

Fixes #3359
Fixes #4779
2026-10-17 19:53:45 +08:00
morluto c192c58815 fix(apple): retain unavailable application components
Preserve unread file and slice facts before Apple attribution. Report partial
component coverage while keeping manifest count completeness independent.
Directories and symlinks retain their existing representation.

Consolidate occurrence identity interpretation and component schemas across
Apple, Android and Harmony; remove superseded producers. Android/Harmony
completed source replies and projection IDs remain byte-identical in controls.
Apple's additive unavailable_components field changes its projection IDs.

Adapt the unavailable-component proposal and real ZIP regressions from
PR #4692 by @rodricksz4h5, adding directory, CLI, MCP and non-encrypted controls.

Validation: 77 focused checks; check:fast; docs:check (5 README checks).
18 tiny completed source cost controls; no real-provider/target execution
or heavy profiling. Published npm package and large-input costs unverified.

Fixes #4688
2026-10-17 19:46:20 +08:00
e5ee3ac658 fix(apktool): preserve decoded APK Evidence subject (#4771)
Bind shared CLI/MCP Evidence to the digest already computed from the owned
APK snapshot. Exercise source mutation during decoding through the shared
application workflow and verify subject identity in the real Apktool lane.

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-17 11:36:21 +00:00
morluto 969aafcf28 fix(ghidra): compare final DOS relocation windows
Adapt the correction and authored overlap control proposed by @berelels in #4156 to the bounded prepared-image owner. Retain each fixup's application facts, then read every memory window after all writes. Carry the producer's load segment into comparison and advance only the MZ evidence revision.

Validation: 129 focused parser/comparison/MCP checks, 38 shared MCP harness checks and 41 selected provider checks passed, with 15 checks overlapping those runs. Fourteen lifecycle checks also passed, plus static/build/docs checks. Two baseline controls failed before the correction. Twelve completed normal source comparisons retained byte-identical replies; no real Ghidra or maximal-relocation performance qualification is claimed.

Fixes #3981
2026-10-17 19:30:11 +08:00
Kaoru0822andClaude Opus 5.5 34239d445b fix(flutter): keep completed inspection facts when cancelled during snapshot cleanup (#4766)
FlutterBuildProvider.execute checks the caller signal after its awaited
snapshot-root removal. When the signal was aborted during a successful
first removal, that check replaced the completed AnalysisExecution with a
bare AnalysisCancelledError, so identify-flutter-build and
inspect-dart-aot reported a typed cancellation without
details.partial_observation and the shared CLI/MCP Evidence assembly
never saw the facts.

The post-cleanup check now keeps the caller's abort reason and retains a
completed result as the cancellation's partial observation. The Evidence
composition from executeProviderOperation moves into
createAnalysisExecutionEvidence beside the provider contract, so the
success path and the retained cancellation use one composer and carry
identical Evidence. Failed results keep their existing classification.

Fixes #4717

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 04:27:55 -07:00
Kaoru0822andClaude Opus 5.5 83d2780008 fix(ida): refuse universal Mach-O targets in attached profiles (#4765)
Attached IDA binds the open database to the target only through the
recorded whole-input SHA-256, path and image base. Every slice of a
universal Mach-O shares that digest, so an attached database of another
slice passed the identity check and its results were attributed to the
host-selected architecture. Attached admission now reports the target
format as unsupported, with lipo -thin and headless recovery, before any
upstream contact. Headless mode keeps thinning the selected slice.

Fixes #3871

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 04:27:46 -07:00
Kaoru0822andClaude Opus 5.5 8589b7e778 fix(evidence): verify staged output identity after its final write (#4764)
* fix(evidence): verify staged output identity after its final write

On macOS FAT32 and exFAT volumes an empty file reports a placeholder file
ID, and the first write assigns its cluster-derived ID. The staged JSON
writer sampled the descriptor identity right after exclusive creation, so
publication and cleanup both rejected REA's own staged file as replaced:
every export failed with cleanup_incomplete and retained a .rea-write-*
directory.

The staged output owner now observes the descriptor identity immediately
before closing it, after the last write, and uses that identity for the
publication and cleanup pathname checks. A failed observation leaves the
identity unknown, so cleanup still refuses pathname removal.

Fixes #4613

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

* test(evidence): cover failed final staged identity reads

Reject the staged descriptor's final stat and verify that the writer
still closes the descriptor, refuses publication, and retains the
unverified staged content with incomplete cleanup.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 04:27:38 -07:00
Kaoru0822andClaude Opus 5.5 63c530998e fix(flutter): report cancellation during snapshot cleanup with completed observations (#4762)
The one-shot Flutter CLI commands passed their command signal to the
inspection but not to finishAnalysisCommand, whose post-close cancellation
guard only runs when a signal is supplied. When the provider's first
snapshot-root removal failed, close() retried it after the inspection had
returned; SIGINT during that retry set exit status 130 while the command
still printed ordinary successful Evidence.

Pass the command signal to the finisher so an abort observed after the
awaited cleanup retry yields typed cancellation that retains the completed
Evidence as partial_observation. A failed retry still reports
cleanup_incomplete ahead of the later signal.

Fixes #4640

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 04:27:23 -07:00
Kaoru0822andClaude Opus 5.5 f03a55909d fix(mcp): accept authentic native Evidence for managed boundary verification (#4761)
The MCP verify_managed_native_boundaries handler resolved native
observations through an inspect_macho/analyze_function allowlist before
calling the shared verification service, and reported any other authentic
operation as evidence_integrity_mismatch. The domain already owns which
native operations it can read symbols from and counts the rest as
unsupported, which is what the CLI returns. The MCP resolver now only
authenticates native observations and leaves operation support to the
shared workflow, so both adapters return the same partial verification.

Fixes #4639

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 04:27:06 -07:00
Kaoru0822andClaude Opus 5.5 f1ab5959bd fix(javascript): bind response endpoints only from writes that reach the read (#4759)
collectResponseConsumers copied the linked request's endpoint onto a
response consumer whenever the receiver binding had exactly one
initializer (added in 0323ce0a2, #3822). A single initializer is a source
fact, not proof that the write reaches the read: an incoming parameter
that is overwritten only conditionally, or only after the read, still
lets the consumer read a caller-supplied response, yet it received the
request's exact endpoint.

The sole write now also has to cover the read, using the same rule the
call resolver applies to incoming values: either it is the binding's
stable unconditional declaration initializer, or the shared read-position
owner (semanticBindingAtRead) reports a definite preceding initializer.
Otherwise the endpoint stays null.

Fixes #4606

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 04:26:55 -07:00
Kaoru0822andClaude Opus 5.5 31c87b40ee fix(javascript): report oversized recovery inputs as file-size limits (#4757)
scanRecoveryFile raised a plain AnalysisOutputError when a selected file
exceeded the remaining operation-wide input allowance, and
snapshotRecoveryInput folded that, read failures and UTF-8 decode
failures into one invalid_value request issue. A 64 MiB + 1 input was
therefore reported as a retryable invalid argument whose only record of
the 67108864-byte budget was message text that also called it an
invalid analysis output, and malformed UTF-8 received the same reason.

The scanner now marks its byte-bound refusals with the bound it
enforced. snapshotRecoveryInput, which owns the meaning of that bound
as the remaining total input allowance, reports it as a non-retryable
file-size AnalysisResourceConstraintError with maximum_input_bytes and
remaining_input_bytes, and decodes UTF-8 separately as an invalid_format
issue. Other scanner consumers still receive an AnalysisOutputError, and
the limit stays enforced.

Fixes #4629

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 04:26:39 -07:00
Kaoru0822andClaude Opus 5.5 6a407619fe fix(javascript): report malformed runtime context events as excluded (#4755)
V8 Inspector capture counted an execution-context created or destroyed
event whose context ID was missing or not a safe integer as retained,
but recorded nothing about it. The capture counters claimed the event
was kept while the context list silently omitted it, and no unknown
identified the loss.

The producer that parses the event and owns the capture counters now
records an invalid-context count, mirroring the existing invalid-script
accounting. The projection reports a non-zero count as an explicit
unknown, so the retained count and the reported omission agree. No
context is invented from a malformed ID, and all-valid captures are
unchanged.

Fixes #4685

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 04:26:25 -07:00
Kaoru0822andClaude Opus 5.5 95b561f2e9 fix(native): retain Apple dispatch metadata descriptor cleanup ownership (#4745)
* fix(native): retain Apple dispatch metadata descriptor cleanup ownership

inspect_native_dispatch_metadata opened the target with raw
openRegularFile and closed it in a finally block, so a rejected close
replaced integrity, capacity or cancellation failures and left no owner
to retry. The read now uses the shared owned regular-file operation in a
client resource scope; the physical failure stays the cleanup primary,
client close retries the exact descriptor, and a confirmed release
clears the original error's cleanup uncertainty. Decoding runs after the
descriptor is released.

Fixes #4743

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

* fix(verify): give the Apple dispatch verifier an owned file scope

Pass an ArtifactResourceScope to inspectAppleDispatchMetadata and close
it after inspection so a failed descriptor close is retained and retried
rather than reaching an undefined scope.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 04:20:40 -07:00
morluto 2f9b9c48b8 fix(ghidra): stream DOS MZ overlay attestation
Keep initialized image facts and full source identity separate at the snapshot owner. Stream overlay bytes into original and modified digests, retain format-bounded image bytes, and consume prepared relocation/mapping facts. Preserve completed comparisons, exact cancellation and cleanup retry through descriptor finalization. Keep explicit source refusals as typed primary failures.

Validation: 119 focused parser/comparison/MCP checks, 40 selected provider checks and 14 lifecycle checks passed; static checks, runtime build, discovery and docs checks passed. Twelve small completed source comparisons retained byte-identical full replies. Real Ghidra, large input and full public CLI/MCP performance remain unverified. The separate overlap timing issue #3981 remains open.

Fixes #4770
2026-10-17 19:18:34 +08:00
morluto 3fc007534b fix(native): validate NativeAOT PE virtual ownership
Reject contradictory headers, overlapping virtual sections, DOS header reuse and image intervals outside the 64-bit address domain before metadata interpretation. Preserve empty and adjacent ranges, source section order, and raw padding semantics. Advance the metadata profile revision and retain provider snapshot refusal facts.

Validation: 37 parser checks and 35 selected provider checks passed; check:fast and build:cached passed. Six tiny authored-fixture summary/detail tasks returned byte-identical 791-token replies. No real-engine or Windows loader qualification.

Fixes #4763
2026-10-17 19:02:29 +08:00
morluto 9c56d09c05 fix(ghidra): preserve load-image completion through cancellation
Consume the named inventory observation once and compare typed facts through one final cancellation check. Retain measured or completed load-image facts and exact reasons, preserve source partials and cleanup, and remove duplicate interpretation and format-specific abort guards.

Validation: 131 contract/projection checks, 40 selected provider checks, and 24 selected private-file/socket lifecycle checks pass; typecheck, lint and builds pass. Includes the original confirmed descriptor-close boundary and late result consumption. Tiny authored fixtures and simulated installation/engine facts; no real Ghidra, investigated target or heavy profiling.

Equivalent completed DOS/raw/NativeAOT tasks retain byte-identical full replies and one source read. First processes and two alternating warm repeats; no statistically established performance improvement.

Fixes #3977
2026-10-17 18:52:42 +08:00
morluto a43ce8a98f fix(ghidra): retain measured facts through NativeAOT failures
Attach each caller’s measured observations before source failure and cleanup projection. Preserve those facts through admission, peer and draining cancellation, cached results, cleanup fences and later lease resolution.

Validation: selected NativeAOT provider controls and typecheck/lint passed in the load-image audit tree. Related consumer validation follows in the next commit.

Refs #3977, #4753
2026-10-17 18:52:42 +08:00
ae029fe2f8 perf(ghidra): aggregate binary overview at the provider boundary (#4754)
Compute overview counts at the Ghidra inventory owner, preserve complete inventory access and coverage, and share CLI/MCP projection. Keep native aggregate Evidence separate from workflow snapshot identities.

Fixes #4746

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-17 10:44:24 +00:00
morluto 5120eb4412 fix(ghidra): join shared NativeAOT derivation and retain outcomes
Replace the private promise race with a named snapshot owner using SharedStartupAttempt. Keep peers independent, stop and join final departure, preserve source failures and completed facts through draining waits, and fence replacement behind retained cleanup. Close joins derivation before releasing the underlying client.

Validation: 31 selected simulated-provider lifecycle and NativeAOT checks, 80 shared-contract/schema/projection checks, typecheck, lint and builds. Completed tiny summary/detail/close comparison preserves full replies and one snapshot read. No real Ghidra, investigated target or heavy profiler execution.

Refs #4753
2026-10-17 18:37:57 +08:00
Ysopwnableandabdooo2235 f0d175dace fix(verifiers): read packed bridge without extraction
Parse only the exact regular packed Python bridge member without extracting into the caller-owned private runtime root. Preserve required Ghidra admission, typed callers, native lease ownership and cleanup on success or failure.

Validation: 263 focused real tar/Python/ownership and CI selection checks, 42 official WABT packaged CLI/MCP cases, full check:ci/check:fast/actionlint, real-package alternating measurements and exact-head hosted CI passed. The required Windows lane exercises the retained native lease without skipping missing authority.

Fixes #4134.

Co-authored-by: abdooo2235 <abdalrhmantheiv15@gmail.com>
2026-10-17 10:35:31 +00:00
morluto 73965f9431 fix(ghidra): retain snapshot outcomes and join reader cleanup
Use the shared artifact resource scope for snapshot reads. Preserve completed facts, exact cancellation reasons and descriptor cleanup leases through session and public error projection; keep parent cleanup pending until all retained resources release.

Validation: 22 selected snapshot lifecycle checks and 85 contract, projection and bounded-reader checks; typecheck, lint and runtime builds. Uses small authored Node/socket fixtures, not real Ghidra or investigated target execution.

Refs #4752, #3977
2026-10-17 18:25:59 +08:00
morluto 5e13e0a876 refactor(files): allow reader-owned cancellation failures 2026-10-17 18:25:52 +08:00
morluto 4a076f368c fix(native): bound NativeAOT reads to declared PE headers
Validate the consumed optional-header extent before interpreting mapping fields. Require complete header-backed read intervals and prevent failed header reads from falling through to section mappings. Advance NativeAOT profile interpretation identity to v4.

Refs #4750
2026-10-17 18:07:29 +08:00
morluto d9099190ab fix(native)!: retain UI outcomes through finalization
Carry caller and runtime deadline cancellation through helper cleanup, preserve command-owner reasons and physical failures, and retain collected scenario reports with typed terminal errors.

BREAKING CHANGE: failed or cancelled native UI scenarios now return typed errors with the collected report in partial_observation instead of successful reports containing failed or cancelled steps.

Refs #4227
2026-10-17 17:57:50 +08:00
morluto 04221e5f01 fix(native): bind UI runtime cleanup to exact owners
Join admitted helper work before runtime removal, retain one preparation outcome and root, and bind originating and retry errors to exact runtime release leases with live resources.

Refs #4227
2026-10-17 17:57:50 +08:00
Ysopwnable 2092f90b7e perf(ci): upload only current portable build cache entries
Stage only the current portable Turbo task archives, metadata and available manifests for same-run artifact consumers. Share task identity/hash validation with cross-run keys and fail publication on incomplete preparation, while retaining existing cache authority and consumer build/prepack verification.

Validation: 252 focused tests, full check:ci, docs:check, check:fast, actionlint, real Turbo output equivalence and invalidation, restored CLI/MCP scenarios and full Linux package verification passed. Fault injection detected historical-entry inclusion. Exact-head hosted CI and available review passed; measurement limits are recorded in the PR.

Refs #4744.
2026-10-17 09:44:11 +00:00
morluto 0f7fd005db fix(native): retain LLDB readback ownership through cleanup
Own journal, capture, result and PID descriptors through confirmed release. Preserve parsed facts and exact cancellation reasons while retaining unresolved children and gating runtime removal on their completion.

Refs #4742
2026-10-17 17:40:49 +08:00
gongfppandgongfpp 5a7e8a166a fix(mcp): normalize browser scenario input errors (#4661)
Co-authored-by: gongfpp <22683417+gongfpp@users.noreply.github.com>
2026-10-17 17:36:32 +08:00
gongfppandgongfpp 9534e42ec8 fix(adb): mark unknown permission coverage partial (#4663)
fix(adb): mark unknown package coverage partial

Co-authored-by: gongfpp <22683417+gongfpp@users.noreply.github.com>
2026-10-17 17:36:19 +08:00
6b27d8f4e3 fix(electron): retain fuse descriptor ownership through failed final close (#4668)
* fix(electron): retain fuse descriptor ownership through failed final close

Static Electron fuse inspection admitted a file descriptor and closed it
in a bare finally: a rejecting final close() replaced the in-flight read
error, was classified by errno (e.g. EACCES) as a read permission failure,
and silently dropped ownership of the still-live descriptor.

Route the read through withOwnedRegularFile so a failed close keeps its
exact owner, preserves the primary error in the outcome, and surfaces as
RegularFileCleanupFailure. The provider now maps that to a primary error
with its own classification plus a ProviderCleanupError reporting
incomplete cleanup, instead of an access-denied read failure.

Regression tests fault close() (EACCES) with and without a primary read
error (EIO): both fail on the pre-fix revision and pass with the fix.

Fixes #4658

* fix(electron): keep cleanup report and partial evidence through cancel and successful reads

Address automated review on #4668:

- Handle RegularFileCleanupFailure before the cancellation check in
  failureFor: a cancelled operation whose final close rejects now reports a
  typed cancellation composed with the incomplete-cleanup error, instead of a
  bare AnalysisCancelledError that silently dropped the descriptor owner.
- Attach the completed fuse image as an explicitly partial Evidence
  observation when only the final close fails, instead of discarding the
  successful read. The abort-after-completion path keeps the same evidence on
  the composed cancellation error.
- Add provider-level regression tests: combined read+close faults, abort
  synchronized with the faulted close boundary, and partial-observation
  retention. Abort-during-close and partial-evidence tests fail pre-fix.

29/29 electron tests green; npm run check:fast clean.

---------

Co-authored-by: nosmile99 <nosmile99@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-17 17:36:06 +08:00
Oke Iyanuoluwa EnochandClaude Opus 5.5 f85a932d71 test(paths): pin Windows separator normalization before native admission (#4733)
rea-agents 6.5.0 passed an absolute caller path to Windows native
snapshot admission verbatim, so a spelling with forward slashes or
repeated separators reached the native component walker, which rejects
empty components as "Ambiguous Windows path component is unsupported"
(Win32 50). Current main already resolves every Windows caller path with
win32.resolve (fc791bb60), which collapses those spellings.

Pin that boundary: absoluteCallerPath normalizes four separator spellings
of one absolute Windows path, and on Windows parseBinaryTarget returns the
same canonical sourcePath for each of them. The Windows test fails when
the 6.5.0 verbatim-absolute-path rule is restored.

Fixes #4707

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 17:14:16 +08:00
morluto 69e670d268 fix(native): own call preflight hashing through client close
Reuse stable-file hashing and exact cleanup leases for target verification, join admitted call workflows before tracer close, and block replacement work while descriptor cleanup remains unresolved. Preserve physical failures, exact cancellation reasons and completed call facts; remove the duplicate reader and AbortError-name heuristic.

Validate real inert-file cleanup controls and controlled tracers through provider and MCP boundaries. No LLDB execution, real OS close-failure or Windows qualification.

Refs #4740
2026-10-17 17:12:39 +08:00
Ysopwnable 9d5b931875 perf(tests): reuse MCP connections within source matrices
Reuse test-owned stdio MCP connections within two stateless source-analysis matrices while retaining every original CLI/MCP scenario, schema/Evidence checks and test-finished cleanup. Add cross-input identity controls.

Validation: 23 focused public-workflow tests and complete check:ci passed; stale-response fault detected. Exact-head hosted CI and available review passed. Pinned workload measurements and their limits are recorded in the PR.

Refs #4737.
2026-10-17 09:11:22 +00:00
morluto 715c9c8a84 fix(native): retain signature facts through finalization cancellation
Bind signature inspection to its named domain result and retain completed facts in typed cancellation. Check cancellation after descriptor and root finalization, preserve exact caller reasons, and keep physical failures primary. Use explicit cancellation checkpoints instead of signal-state guesses.

Validate root and descriptor cleanup, retries, late root handoff, opaque reasons, provider projections, and existing real codesign/CLI controls. No real OS close-failure or Windows qualification.

Refs #4735
2026-10-17 17:01:21 +08:00
morluto 4f3b573b09 fix(native): bind signature snapshot cleanup to exact leases
Retain roots and descriptors through shared cleanup leases and bind composed errors to their actual pending owners. Successful retries shrink live attribution and reveal the original typed failure.

Validate mixed root and descriptor failures, partial/final release, existing target binding, and real codesign and CLI controls. No real OS close-failure or Windows qualification.

Refs #4734
2026-10-17 16:52:28 +08:00
morluto e09a503f44 fix(native): retain plist descriptor cleanup ownership
Use shared owned file operations for plist admission and source probes. Join admitted work during close, retain exact descriptor leases through retries, and preserve physical failures, cancellation reasons and completed converter output.

Validate with controlled cleanup faults and native plist CLI workflows on inert data. Real OS close-failure incidence and Windows behavior remain unverified.

Refs #4723
2026-10-17 16:43:14 +08:00
morluto 26bb85c007 fix(core): keep released cleanup dependencies empty
Distinguish unbound resource attribution from a completed dependency set so final release cannot restore historical resource names.

Refs #4578
2026-10-17 16:43:14 +08:00
9744d07211 docs(skills): capture measured analysis optimization workflow
Add a reusable contributor workflow for locating analysis bottlenecks, preserving Evidence and cache ownership, and comparing equivalent real tasks. Keep pinned Kuna results and experimental limits in an optional case study.

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-17 08:33:18 +00:00
ShuaixinHuang a040533470 fix(ghidra): reject byte matches outside searched coverage (#4531)
* fix(ghidra): reject byte matches outside searched coverage

* test(ghidra): cover inclusive and overlay byte match bounds

* test(firmware): assert composed cleanup error evidence

* test(ghidra): assert cancellation during profile shutdown

* test(jeb): retain Linux ownership prerequisite in isolation
2026-10-17 16:18:18 +08:00
ShuaixinHuang abe49808b2 fix(plist): ignore inherited XML entity lookup properties (#4530)
* fix(plist): ignore inherited XML entity lookup properties

* test(firmware): assert composed cleanup error evidence

* test(ghidra): assert cancellation during profile shutdown

* test(jeb): retain Linux ownership prerequisite in isolation
2026-10-17 16:17:53 +08:00
morluto 70de702109 fix(browser): share DOM snapshot admission
Own decoded node columns, attributes and parent validity once for web, Electron and SVG interpretation. Reject dangling, self and cyclic parent facts; preserve source positions and independently unknown fields. Remove fallback conversions and duplicated string/attribute interpretation.

Validation: baseline public CLI controls failed for four malformed cases with valid control passing; final 103 focused checks, 5 final built CLI controls, 1 late-detach cancellation control (20 skipped), check:fast and docs:check passed. Fake CDP producers and generated local files; no real browser/Electron or Windows qualification.

Refs #4709
2026-10-17 15:59:57 +08:00
4e14452ddb fix(native): report unidentified text plists as unknown (#4320)
* fix(native): report unidentified text plists as unknown

Require explicit XML identification in file output instead of treating all
text as XML. Preserve decoded plist values and source-format provenance
when plutil falls back to XML conversion.

Add provider-boundary regressions and real macOS CLI controls for OpenStep,
data-bearing OpenStep, XML, UTF-16 XML and binary property lists.

Fixes #4294

* fix(native): keep XML plists without a declaration labeled xml

`file -b` names XML only when a document declaration is present, so the
narrowed `\bXML\b` check reported a declaration-less XML plist as unknown and
broke the real-plutil `decodes xml plists that plutil cannot express as JSON`
CLI test. When `file` reports only text, read the source head once and label it
XML only when its first non-whitespace character (after a UTF-8 BOM) is `<`;
OpenStep and other text stay unknown.

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

* fix(native): distinguish OpenStep data from XML source markup

Recognize XML markup instead of treating every leading angle bracket as XML.
Keep data-only OpenStep sources unknown, honor explicit JSON identification,
and decode BOM-marked UTF-16 prefixes before detecting declarationless XML.
Add source-format regressions and macOS CLI cases for data-only sources.

---------

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 15:52:25 +08:00
星闌 de9371b85d fix(javascript): preserve effective child-process stdio options (#4336)
* fix(javascript): preserve effective child-process stdio options

* fix(javascript): retain proven callbacks and primitive stdio keys

* fix(javascript): preserve argv shapes and nullish spreads

* fix(javascript): bound callable proofs and retain exposed binding uncertainty

* test(javascript): keep callback acceptance within transport budget

* test(javascript): inspect application coverage for callback proof limits

* fix(javascript): keep spread command argument slots uncertain

* fix(javascript): bound mapped argument content proofs

* fix(javascript): distinguish erased declarations from runtime shadows

* fix(javascript): prove runtime module context before binding isolation

* fix(javascript): share erased declaration and opaque scope proofs

* fix(javascript): keep lowered enum scopes opaque

* fix(javascript): narrow stdio proofs and consolidate regressions

* perf(javascript): reuse lexical references for child shapes

* fix(javascript): retain CommonJS child argument context

* fix(javascript): keep Annex B affected child arguments unknown

* fix(javascript): recognize value import-equals runtime modules

* fix(javascript): retain exact stdio through module and primitive syntax

* fix(javascript): bound inline spread property resolution

* fix(javascript): distinguish copied methods from getters
2026-10-17 15:51:55 +08:00
郭立 Guo Li 0cd4eafd61 perf: reduce MCP launcher and browser capture overhead (#4350)
* perf(browser): 为单次捕获复用脚本资源索引

* docs(setup): 说明多会话直接启动以减少 npm 驻留
2026-10-17 15:51:23 +08:00
Tapraj-PathakandCopilot 509d4c8f56 Fix permissions count issue for pkg=null in ADB analysis and add test cases (#4665)
* Fix permissions count issue for `pkg=null` in ADB analysis and add test cases

* fix(adb): handle null package permission counts

Recognize pkg=null as unknown permission data and keep the provider projection aligned with the parser. Add regression coverage for the parser and provider.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-17 15:45:15 +08:00
morluto 79d2b71896 fix(electron): preserve unknown resource sizes
Use one typed CDP resource-size owner in web and Electron capture. Supplied invalid sizes retain null plus completeness diagnostics; omitted sizes remain unknown without inventing malformed metadata. Preserve other resource facts and valid zero/positive values.

Validation: public CLI regression failed against baseline clamping; 36 focused controls passed after the fix, check:fast and docs:check passed. Fake CDP producer with real CLI/transport and tiny local files; no real Electron/browser or heavy profiling qualification.

Refs #4708
2026-10-17 15:41:07 +08:00
Kaoru0822andClaude Opus 5.5 9be493880c fix(kuna): name the setting that owns a timed-out Kuna run (#4343)
* fix(kuna): name the setting that owns a timed-out Kuna run

A Kuna run past its deadline surfaced the generic "Analysis took too
long. Try a smaller request" message. The request is already one
function or one whole-program scan, and the same run takes as long
again, so the advice was wrong and REA_KUNA_TIMEOUT_MS or
REA_KUNA_PROJECT_TIMEOUT_MS was never mentioned.

Each Kuna command now carries a KunaDeadline: the configured
milliseconds and the setting that owns them, from KunaConfiguration.
A timeout reports, for example, "kuna decompile did not finish within
REA_KUNA_TIMEOUT_MS (120000 ms)." Its remediation says to raise that
setting and restart REA, and for a single-function decompile that the
session's project export can still answer. AnalysisTimeoutError now
accepts a producer message and remediation, which projection uses and
per-waiter scan errors keep.

Refs #1957

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

* docs(kuna): scope the provider_timeout statement to direct Kuna runs

A timed-out project export is reported through the export failure as
capability_unavailable, not provider_timeout.

Refs #1957

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

* fix(kuna): verify native xrefs under the project deadline

The provider's xrefs scan runs under REA_KUNA_PROJECT_TIMEOUT_MS, but the
native oracle in verify:kuna aborted at REA_KUNA_TIMEOUT_MS. A scan that
fit the project deadline but not the function deadline failed
verification although the provider answered it. Use the same deadline.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 00:38:16 -07:00
morluto 748ac7f0df fix(sqlite): preserve column identity before row selection
Read raw column metadata through the built-in PRAGMA in the private empty temp schema. Validate bytes before names become SQL identifiers, and select inventory subsets by exact encoded identity so unrelated malformed names cannot alias valid columns.

Validation: 5 focused identity controls; 15 selected producer/provider controls (33 skipped); public CLI baseline collision, fixed format refusal and valid-subset success; check:fast and docs:check. Generated inert SQLite fixtures on macOS arm64 with Node 24.18.0; no Windows qualification or large-file/stress workload.

Refs #4710
2026-10-17 15:33:59 +08:00
Saim ShafiqueandSaim Shafique 77c8a1ba32 fix(javascript): mark resource-skipped files parse_status skipped (#3852)
* fix(javascript): mark resource-skipped files parse_status skipped

Files above ~5 MB exhaust the worker heap during static analysis (or exceed
the 8 MB static-checkpoint budget with a larger heap). They were inventoried
with text included but never parsed, leaving parse_status null and no
counter, so the summary read as 'nothing there' instead of 'not analyzed'.

Report them as skipped: parse_status 'skipped' on the file observation and a
skipped_javascript_files statistics counter. The analysis-failure reason keeps
surfacing as a limitation entry.

Fixes #3670

* fix(javascript): default skipped_javascript_files when reading old saved evidence

Saved analysis evidence written before skipped_javascript_files existed failed validation against the strict reconstruction statistics schema, so users could not reuse old results for comparisons or runtime reconciliation. Accept the field's absence on read (defaulting to 0) while continuing to write it for new results.

* fix(javascript): preserve unknown skipped count for legacy evidence

The .default(0) gate fix fabricated a measured zero for saved
pre-change evidence, so comparisons would receive a definitive
"no files skipped" fact the producer never established. Use the
codebase's exactOptional idiom instead: legacy reads accept absence
and keep omission (unknown), while the producer still always writes
the field (required by JavaScriptArtifactReconstructionResult).

Tests: legacy payload now asserts the key stays absent/undefined;
current-output test keeps asserting the producer-written counter.

* fix(javascript): add required failure scope and status to graph fixture

---------

Co-authored-by: Saim Shafique <sx4im@users.noreply.github.com>
2026-10-17 16:33:41 +09:00
Mohamed Ayoub Eleuch 8a2a1a2b8b fix(cutter): preserve mixed Unicode output within the frame limit
Count real UTF-8 bytes in bounded chunks and retain authenticated omission for malformed Unicode. Preserve existing wire/resource limits and completion semantics.

Validation: producer/client regressions, fresh real Linux Cutter CLI/MCP output and Evidence recovery, and complete exact-head CI passed. Hosted unrelated CLI timeout and unchanged rerun results remain recorded in the PR.

Fixes #3330.
2026-10-17 07:30:30 +00:00
morluto c7e9e096d3 fix(plist): reject malformed XML before value decoding
Reuse the shared validated XML document for depth and prototype-key preflight, preventing recovery-only values from being projected as observations. Preserve OpenStep and valid XML source semantics.

Validation: 46 focused checks; 5 final syntax controls; 10 encoding and public CLI/MCP overlap checks; check:fast. Public malformed-entity CLI changed from success to typed format failure. Generated inert artifacts only; no real Apple application or native engine execution.

Refs #4714
2026-10-17 15:24:57 +08:00
Aditya kharmale 8c62a4aa36 fix(javascript): preserve case-sensitive Unicode seed identity
Use NFC for case-sensitive feature seeds while preserving the existing explicit insensitive compatibility fold.

Validation: exact/substring/absent controls, fresh public CLI/MCP workflows and complete exact-head CI passed. Hosted CLI-worker failure and unchanged rerun results remain recorded in the PR.

Fixes #3314.
2026-10-17 07:20:34 +00:00
morluto 3cfa00a29c fix(ghidra): preserve valid Unicode language metadata
Validate XML bytes before shared syntax parsing so literal U+FFFD remains valid while malformed bytes and declaration mismatches fail at their producing boundary.

Validation: 92 focused checks, 3 selected fake Apktool boundary checks, 6 final encoding controls, check:fast and docs:check. Ghidra coverage uses tiny real files and descriptors, without a real engine or Windows qualification.

Refs #4712
2026-10-17 15:17:03 +08:00
morluto 623339a7ab refactor(xml): centralize strict document parsing
Own syntax validation and source-located DOM projection together. Preserve explicit consumer policies and remove duplicated warning-message filters from Apktool and SAZ.

Validation: 92 focused checks, 3 selected fake Apktool boundary checks, check:fast and docs:check. No real Apktool or Ghidra execution.

Refs #4694
2026-10-17 15:16:24 +08:00
Kshitiz Jain c0b3cd14b1 fix(javascript): exclude class member keys from lexical reads
Classify noncomputed class member keys and private names at the shared reference owner, avoiding false reads and closure captures. Preserve computed keys and initializer/body reads.

Validation: regression controls, fresh public CLI/MCP workflows and complete exact-head CI passed.

Fixes #3310.
2026-10-17 07:11:19 +00:00
siegfly 195cf18818 docs(native): distinguish analysis memory from source bytes
Document initialized Ghidra analysis-memory bytes, source mappings and incomplete coverage. Retain the portable test-owned alternate ELF for liveness cancellation.

Validation: fresh docs:check and complete exact-head CI passed. Historical real Ghidra checks remain explicitly attributed to their recorded revision/platform in the PR.

Refs #3075.
2026-10-17 07:11:10 +00:00
keeponlight e26bb49a4b fix(managed): avoid duplicate decode budget charging on cached metadata strings
Charge each immutable metadata heap string decode once; keep unique-string and per-occurrence projection accounting bounded.

Validation: focused capacity regressions, fresh real .NET DLL CLI/MCP workflows and complete exact-head CI passed. Before/after measurements and their limits are recorded in the PR.

Fixes #3449.
2026-10-17 07:10:58 +00:00
Sua Choi 326c073880 fix(apktool): preserve valid replacement characters in decoded XML (#4697) 2026-10-17 15:09:14 +08:00
Ysopwnable c6b62754b0 perf(kuna): reuse sorted string inventory for exact lookups
Reuse the validated string inventory's numeric address order for O(log n + matches) exact reads, preserving every encoding at the selected address without a second lookup index. Keep streaming validation, session cache lifetime, full listings, provenance and caller choices.

Validation: complete latest-head CI passed. Original real Kuna 1.833 Linux correctness and warm-lookup measurements remain pinned to their recorded feature revisions in the PR. The latest local 300 ms export-deadline observation and unchanged 31-test provider recheck are explicitly documented there; no assertion or timeout was weakened.

Refs #4695.
2026-10-17 07:04:16 +00:00
morluto 264799b0ff fix(kuna): retain workspace rollback failures
Compose failed rollback with the original target or read failure, bind it to the exact workspace lease, and retain the failed attempt while cleanup remains unresolved. Remove the silent release wrapper and duplicate exception wrapping; keep clean failures retryable and unknown causes opaque.

Refs: #4704
2026-10-17 14:56:01 +08:00
Ysopwnable 5b24399387 test(kuna): preserve caller and producer graph failure context
Align malformed call-graph retry assertions with caller-attributed errors while retaining the parser operation in diagnostics and the cause. Keep all malformed shapes, corrected-response retry, exact native-command count and owned-client cleanup coverage.

Validation: clean main reproduces 3 failed / 14 passed; corrected complete Kuna boundary selection passes 266 tests across 26 files; full check:ci and complete current-head CI passed. Greptile 5/5; human owner reviewed the diff/evidence and explicitly declared readiness. Codex review remains unavailable due repository usage limits. Test-only; no production/schema/authority change or real-engine qualification.

Fixes #4701.
2026-10-17 06:47:06 +00:00
morluto 5927937179 fix(kuna): preserve cancellation and cleanup ownership
Reuse shared startup ownership for query waits and preserve opaque cancellation reasons, captured command facts and typed primary failures. Bind retained command errors to exact process and workspace leases; share close attempts and update earlier caller and aggregate errors after cleanup retry.

Refs: #4700, #4702
2026-10-17 14:38:50 +08:00
morluto 988494abdb fix(kuna): own cancellable installation startup
Reuse the shared startup owner for independent caller waits, final departure cleanup, and session closure. Carry the producer signal through version probing and fingerprint finalization. Retry cleanly cancelled acquisition after settlement while retaining authored failures and exact descriptor cleanup dependencies.

Refs: #4698
2026-10-17 14:23:17 +08:00
Ysopwnable 3c9b5d6d02 test(ci): align fixtures with runtime and evidence contracts
Restore test fixture runtime prerequisites and align assertions with current Evidence and uncertainty contracts. Separate actual CLI publisher refusal from explicitly admitted Hopper socket/CLI fixtures, retaining exact wire identity and cleanup coverage. Validate retained EVM Evidence against exact unchanged source-carrier bytes.

Validation: 88 targeted local test passes across three completed selections, two platform skips, full check:ci; hosted Windows probes and complete current-head CI. No production authority, authentication or schema change. Controlled fixtures do not qualify actual disassemblers or workers.

Fixes #4690.
2026-10-17 06:20:23 +00:00
morluto 54c71313ae fix(kuna): attribute inventory failures to callers
Normalize shared installation and inventory failures at the session boundary, retaining producer operation metadata. Preserve exact cancellation reasons before admission and while waiting without adopting foreign cleanup authority.

Refs: #4689
2026-10-17 14:13:25 +08:00
morluto 6eb55be4e8 fix(kuna): preserve live cleanup in caller failures
Reuse canonical typed failure attribution instead of copying cleanup markers. Preserve captured facts, typed primary outcomes and opaque cancellation reasons; bind caller projections to the exact producer cleanup owner.

Refs: #4693
2026-10-17 14:13:16 +08:00
morluto 5717ad24aa fix(kuna): attribute installation failures to caller operations
Project the immutable shared installation failure through the existing authored-failure normalizer for each requesting operation. Preserve direct health identity, diagnostics, retryability and live cleanup dependencies.

Validation: ten baseline/candidate public CLI cases each; nineteen focused boundary checks plus four overlapping final cleanup controls, check:fast and docs:check passed. Concurrent callers share one acquisition failure without mutating its producer. No real Kuna, Windows, MCP-specific failure replay or performance measurement. Refs #4631.
2026-10-17 13:49:27 +08:00
morluto d09be9cfa2 docs(browser): clarify scenario cleanup after termination
Document retained profile data, missing-executable Playwright artifacts, platform cancellation differences and the absence of later reclamation. Keep recovery work tracked in #4659.

Confirmed installed Playwright 1.58.2 preparation order and one public CLI missing-executable reproduction in a private fixture; an empty artifacts directory remained and was removed only with the owned experiment root. Forced termination was not rerun. docs:check passed.
2026-10-17 13:49:20 +08:00
morluto 7086768a27 fix(adb): preserve display-scoped window focus declarations
Retain every focus declaration with its display header and identity, including explicit absence, unknown identities and duplicates. Derive singular summaries only for one unambiguous context; preserve scope and ambiguity through named CLI/MCP contracts.

Validation: original public CLI replay before/after, four failing baseline parser controls, 23 focused checks plus 11 overlapping provider/parser controls, converted MCP schemas, check:fast and docs:check. Bounded completed synthetic CLI warm reply tokens 282/513, elapsed 0.391/0.403s and CPU 0.622/0.622s; no efficiency claim. No live Android, vendor or large-output qualification. Refs #4660.
2026-10-17 13:40:09 +08:00
morluto be0b2d138c fix(adb): scope package uninstall requests to user zero
Send shell pm uninstall --user 0 through the shared quoted command boundary. Align tool guidance and Evidence limitations with the selected request scope while preserving device refusal diagnostics.

Validation: two provider controls failed before the fix and pass afterward; three public CLI/MCP scenarios capture exact arguments and equivalent success facts; nineteen command/SDK contract checks, check:fast and docs:check pass. No real device, multi-user data preservation, vendor/APEX behavior or performance measurement. Refs #4673.
2026-10-17 13:30:29 +08:00
morluto c360cfcd6d fix(browser): parse complete COOP and COEP structured items
Validate token items and their parameters before projecting exact declared policy tokens. Keep malformed and non-token values unknown; retain separate grammar for other policy headers.

Validation: real pinned Chrome public CLI baseline/candidate, 101 focused checks and 90 overlapping final checks, 834 upstream item-vector adaptations with two permitted refusals, check:fast and docs:check. Other browser/platform and policy-specific MCP delivery unverified; no performance measurement. Refs #4677.
2026-10-17 13:26:13 +08:00
morluto df2fc251e3 fix(browser): preserve repeated document Link fields
Parse CDP repeated field lines independently before applying URI and quoted-parameter delimiters. Preserve declaration order and derive agent hints from retained links.

Validation: real pinned Chrome public CLI baseline and candidate each captured three responses per fixture; repeated Document links/hints restored and comma/single/Fetch controls unchanged. Three source controls failed before the fix. Sixty-one focused checks plus fifteen overlapping final network checks, check:fast and docs:check passed. No performance measurement or other browser/platform claim. Refs #4679.
2026-10-17 13:21:50 +08:00
morluto 0ec4826633 fix(evm): preserve command owner cancellation reasons
Copy the exact opaque reason already recorded by OwnedCommand rather than discarding it at EVM failure conversion. Keep physical, timeout and incomplete cleanup outcomes distinct.

Validation: direct compiled probe and eight source controls; four reason controls failed before the fix. Twenty-one overlapping service/adapter checks, check:fast and docs:check passed. No real Linux x64 worker cancellation or public delivery exercised. Refs #4686.
2026-10-17 13:18:05 +08:00
morluto d694aef227 fix(evm): retain completed evidence through cleanup cancellation
Share validated interface Evidence assembly across success, cleanup failure and late cancellation. Preserve exact admission and completion abort reasons.

Validation: 26 service, MCP and host-refusal checks passed; check:fast passed. Thirty Linux x64 provider checks skipped on Darwin arm64, including expanded root-removal controls. Linux real-worker and public SIGINT reproduction remain unverified; no performance measurement. Refs #4683.
2026-10-17 13:16:40 +08:00
morluto 5853089885 fix(hopper): retain search failure context and cleanup ownership
Attribute regex inventory failures to the accepted search operation while retaining wire identity, typed outcomes, captured facts and live cleanup dependencies. Preserve exact cancellation reasons at inventory and progress completion.

Validated with focused source, socket-boundary and public CLI fixtures, existing MCP contract checks, check:fast and docs:check. Simulated Hopper only; real-engine behavior and performance costs remain unmeasured. Refs #4680.
2026-10-17 13:12:43 +08:00
morluto 4d980d0176 fix(javascript): preserve unresolved version relationships
Keep positive topology differences separate from unpaired neighbor identity. Preserve exact content pairing and source observations, report unresolved relationships without inventing incomplete inventory, and omit comparison edges when no substantive difference was observed.

Validated with 45 focused domain/CLI/MCP tests, nine expanded domain controls, check:fast, and docs:check. Clean baseline fails seven controls and passes two positive controls. Controlled static fixtures only; no target execution or large-bundle profiling.

Refs #4681
2026-10-17 12:51:04 +08:00
morluto 556504edcb fix(javascript): distinguish relocation from application changes
Keep location-only matched pairs unchanged under complete coverage while retaining both coordinates and location dimensions. Preserve incomplete coverage as unknown and retain definite content, property, and relationship changes.

Validated with focused domain, compiled CLI and MCP workflows, static checks and docs checks. A tiny public CLI bundle fixture reproduces the issue; the reported Bitwarden releases were not rerun.

Refs #4520
2026-10-17 12:36:21 +08:00
morluto 1e36e707b1 fix(ghidra): preserve launch outcomes through cancellation and rollback
Carry producer cancellation separately from physical launch errors and diagnostic causes. Retain exact opaque reasons, LaunchSupport warnings, and rollback owners; consume the authored outcome without reclassifying failed launches from a later deadline.

Validation: focused client/provider/rollback and helper/MCP tests, check:fast, docs:check. Controlled native process ownership and Node bridge only; real Ghidra/JVM and Windows/Linux fault behavior unverified.

Refs #4517
2026-10-17 12:27:08 +08:00
morluto e31aa72c9d fix(ghidra): retain executable header read and cleanup ownership
Read bounded stable prefixes through the installation file owner, preserve unknown header diagnostics, and propagate cancellation or incomplete cleanup. Share extent and identity validation with full artifact reads.

Validated with 269 focused tests, check:fast, and docs:check. Controlled Darwin file faults reproduce the original lost owner; real Ghidra and native Windows/Linux verification remain unperformed.

Refs #4676
2026-10-17 12:17:44 +08:00
morluto 5112fe07ae fix(ghidra): admit installation metadata through owned bounded reads
Reject special files without blocking, cap the properties payload at 1 MiB before allocation, and verify descriptor extent and the selected name. Preserve Latin-1 data, selected symlinks, physical filesystem facts, completed observations and exact failed-close ownership.

Reuse the stable bounded reader for profile artifacts and consolidate Ghidra file failure projection. Distinguish missing-at-open from disappearance or read failure, and attribute typed prerequisite failures at the consuming operation.

Validation: 384 focused tests across 14 files, 42 seed/extension controls, 32 final metadata/profile/CLI controls, check:fast and docs:check. Controlled macOS file/process fixtures; no real Ghidra/JVM or Windows/Linux lifecycle verification.

Bounded FIFO reproduction: baseline direct and public doctor required external cleanup around 2.2s; candidate returned independently around 0.4/0.57s, all fixture groups verified empty. Warm normal doctor baseline/candidate: elapsed 1.211/1.230s, CPU 1.239/1.247s, sampled family RSS 524/524MB, reply tokens 3116/3116. No performance improvement claimed; true cold and whole-agent costs unmeasured.

Refs #4672
2026-10-17 12:02:26 +08:00
morluto c72786f289 fix(ghidra): attribute returned profile cancellation at its owner
Apply the existing cancellation projection to returned Results as well as thrown failures. Preserve the exact owner-close reason and keep shutdown joining the active resolution before later admission.

Strengthen the held-path regression to require typed cancellation instead of success. The original assertion and the stronger attribution control failed on unchanged cc2e18a26; focused validation and check:fast pass after repair. Controlled private files and recording provider ports; no native Ghidra claim.

Refs #4674, #4561
2026-10-17 11:53:59 +08:00
morluto cc2e18a265 fix(ghidra): own Java prerequisite probes through final cleanup
Replace synchronous Java execution with bounded owned commands. Keep task-scoped readiness observations fresh across later tasks, defer target client prerequisites behind a closeable owner, and join doctor cancellation and cleanup.

Preserve completed physical failures across late cancellation, exact cleanup dependencies and partial observations. Admit target/profile inputs before probing and return typed factory failures instead of unavailable client wrappers.

Validation: 405 focused tests across 24 files, 2 explicit skips; 109 provider/doctor controls rerun after final admission refinement; check:fast and docs:check. Controlled macOS Java-shaped commands and public CLI/MCP workflows; no new JVM/Ghidra or Windows/Linux lifecycle verification.

Measured warm public doctor baseline/candidate: elapsed 0.447/1.223s, CPU 0.624/1.237s, sampled family RSS 379/529MB, reply tokens 3133/3133. Ownership adds cost; no performance improvement claimed. True cold, whole-agent and MCP costs unmeasured.

Refs #4561. Remaining installation file admission tracked in #4672.
2026-10-17 11:29:03 +08:00
morluto fb146bf40b fix(providers): own deferred client acquisition through final cleanup
Return a closeable owner before prerequisite work begins. Share creation across independent callers, join the final departure and late handoffs, retain exact failed resource owners, and gate operation dispatch again after acquisition.

Keep reuse admission separate from native health. Move shared provider ownership below application workflows and consolidate failure attribution; remove the superseded lazy client.

Validation: 34 focused owner, lazy, composite and session controls; check:fast. Compiled baseline/candidate recording-provider workflow reproduces then fences dispatch after close. No native provider claim.

Refs #4561, #4667
2026-10-17 11:25:23 +08:00
morluto 870dcb1fda test(binary): await and validate session status projections
Replace the cast from an asynchronous owner result with an awaited schema-validated projection. Give the producer a narrow typed router port and construct complete recording inputs.

Validation: all three controls failed on unchanged e567033a7 and pass after repair; check:fast passed. No provider runtime behavior claim.

Refs #4666
2026-10-17 11:23:00 +08:00
morluto e567033a71 fix(process): preserve completed observations and cleanup authority
Retain physical stream errors and late caller cancellation independently. Expose an exact failed Darwin preparation root for cleanup retry without closing the shared reader.

Validation: 24 owned-command tests, 6 cancellation controls, 2 targeted Darwin cleanup retry controls, and check:fast. Controlled macOS processes only; no new real JVM or Ghidra verification.

Refs #4561, #4638
2026-10-17 11:10:02 +08:00
morluto 150e996a86 refactor(providers)!: await owned readiness observations
Carry asynchronous readiness through provider selection, session status, CLI and MCP. Join observations during cancellation and shutdown, keep status readable during finalization, and retain mixed provider errors and exact cleanup dependencies through the shared inspection failure owner.

Validation: 255 focused tests across 18 files; 21 registry tests rerun with primary restoration assertion; check:fast; docs:check and 5 README conformance tests. Registered MCP and SDK schema checks use controlled providers; no real Ghidra/JVM verification.

This is a prerequisite for #4561. The synchronous Ghidra Java producer and its doctor/client ownership migration remain unfinished.

BREAKING CHANGE: binary_session candidate availability can be unknown during owner finalization. Consumers must handle this state; the internal session status API is now asynchronous.

Refs: #4561
2026-10-17 09:57:43 +08:00
morluto 4087048270 fix(process): retain owned command cancellation provenance
Preserve the owner-observed opaque caller reason through preparation, collection and cleanup failure wrapping without assigning cleanup authority or serializing the raw reason.

Validation: 68 focused process/deadline tests; check:fast; docs:check (5 conformance tests). Controlled local process fixtures only; no native provider verification.

Refs: #4638
2026-10-17 09:41:13 +08:00
morluto 71929ed37c fix(cutter): normalize command checkpoint cancellation
Produce typed cancellation at each command checkpoint while retaining the exact opaque reason as cause. Preserve independent typed physical failures without downstream equality guesses.

Validated 72 focused tests across nine files, static checks, docs and five conformance controls, including real client/service admission races and matching physical-error controls. No native Cutter engine or new public-transport reproduction claimed.

Refs: #4635
2026-10-17 09:24:43 +08:00
morluto 199396f61c fix(cutter): retain command attribution through descriptor cleanup
Pass the requesting operation into the descriptor failure owner. Discovery retains its own identity; command execution and completion confirmation retain cutter_command with exact cleanup dependencies.

Validated 61 focused tests across five files, including denied retry and successful native descriptor release. Controlled close faults; no native Cutter engine verified.

Refs: #4634
2026-10-17 09:22:54 +08:00
morluto 5c0472c571 fix(cutter): attribute status proof failures to command
Parse command status and validate session identity at one bridge boundary. Keep the preflight watermark requirement separate from exact completion receipt proof, and retain status as a compact auxiliary diagnostic.

Reuse authenticated protocol and descriptor fixtures. Validate 66 focused tests across eight files, static checks, docs and five conformance controls. CLI refusal controls use an authenticated loopback fixture; native Cutter was not verified.

Refs: #4607
2026-10-17 09:19:02 +08:00
morluto 8f65d7f256 fix(ida): attribute startup failures and retain cleanup ownership
Attribute shared startup failures separately for each accepted operation. Preserve the original stage as a compact diagnostic, with typed timeout budgets, limits, output, recovery and exact cleanup dependencies.

Link cancellation wrappers to the originating live cleanup owner, including release before and after wrapping. Remove the redundant typed-error branch.

Validated 130 focused tests, static checks, docs and five conformance controls; four source controls rerun after fixture type correction. Public CLI uses a loopback supervisor; no native IDA engine verified.

Refs: #4614, #4633
2026-10-17 09:12:57 +08:00
morluto 3e8236639a fix(errors): restore typed primary after confirmed cleanup
Retain the primary error through cleanup composition and resolve its presentation only after live ownership confirms release. Preserve historical diagnostics separately from producer facts.

Validate domain primary types, nested owners, denied retry and late observations; exercise public CLI and MCP projection plus firmware descriptor retry. Focused tests, source controls, static checks and docs pass. Native providers were not verified.

Refs: #4628
2026-10-17 09:04:09 +08:00
morluto 0a51b246c6 fix(firmware): preserve snapshot input failures and descriptor ownership
Reuse canonical artifact chunk reads across hash, snapshot, range and publication, leaving cleanup to the exact owner. Let the reader own cancellation, preserve same-read physical failures, and classify growth past the input allowance through the existing typed admission failure.

Validation: 46 focused tests across four files, docs checks and five conformance tests. Native file IO with controlled faults; no Linux firmware engines verified. Shared post-cleanup error projection remains tracked in #4628. Refs #4619, #4475
2026-10-17 08:53:56 +08:00
morluto 8451d28392 fix(artifacts): preserve input size refusals and recovery
Classify the WABT reader file-size boundary as a deterministic resource constraint with its input allowance. Give Go input-size refusals selected-input recovery rather than evidence-write advice. Preserve configuration and cleanup semantics.

Validation: 66 focused tests across four files including public CLI refusals, plus docs checks and five conformance tests. Sparse admission fixtures and version-only WABT stubs; no real WABT execution. Refs #4621, #4622
2026-10-17 08:45:15 +08:00
morluto 3d028dbc74 fix(adb): distinguish absent focus from missing declarations
Parse window focus into declaration records, normalize explicit null references once, and preserve original named lines in Evidence. Keep fallback based on declaration presence and retain unknown focus when both sections omit fields.

Validation: 86 focused tests including public CLI/MCP with advertised schemas, plus docs checks and five conformance tests. Fake ADB only; no physical device verified. Refs #4608
2026-10-17 08:38:33 +08:00
morluto bfb7ed0bd4 fix(javascript): retain assignment and nested-default destructuring
Share source identity and one iterative pattern walker across declarations and assignments. Traverse nested default targets without losing dependency paths, preserving existing scalar-default occurrence identities and conservative unresolved slots.

Validation: 405 focused tests across five files, including public CLI dependency Evidence. Refs #4609, #4604
2026-10-17 08:32:26 +08:00
morluto c3e42385e7 fix(javascript): derive loop cardinality from exact literal values
Use the canonical cooked literal value for for-in and for-of string cardinality, preserving possible assignments and prototype uncertainty across quoted and template spellings.

Validation: 473 focused tests across six files, including public CLI assignment Evidence and exported return shapes. Refs #4617
2026-10-17 08:27:39 +08:00
morluto 746a044e42 fix(filesystem): preserve opaque caller reasons through cleanup
Do not interpret exact caller cancellation reasons as descriptor ownership records. Preserve checkpoint-shaped reasons through successful close retry and supply cancellation context in direct signal-aware cleanup consumers.

Validation: 156 focused tests across 14 files passed; no native provider execution. Refs #4610
2026-10-17 08:25:01 +08:00
morluto 3bd9aa3484 test(filesystem): assert admission cancellation provenance
Keep the explicit checkpoint wrapper and verify its exact caller reason alongside the retained cleanup owner.

Refs #4610
2026-10-17 08:23:36 +08:00
morluto 0c7b6d834a fix(javascript): bound local getter-result expansion
Charge inner alias and branch work to the existing getter query budget before allocating queued states. Widen local suffixes during fallback so a single getter cannot reintroduce path permutations.

Validation: two new class/object controls fail on the baseline; 411 tests across eight affected files pass. Public CLI baseline/candidate controls preserve unrelated observations and expose intended partial resource coverage. Typecheck/lint, docs and formatting pass. Diagnostic costs retained; no speed or token reduction claim. This fixes a separate getter gap and does not resolve the getter-free Qodo input. Refs #3839.
2026-10-17 06:58:19 +08:00
d5be402fb3 fix(objc): isolate empty protocol selectors and category class names (#4549)
* fix(objc): isolate empty Objective-C protocol method selectors

A protocol method_list_t entry whose selector reached an empty string was
appended to objc_protocols and objc_protocol_records unchecked. The final
metadata schema then rejected the whole result, discarding every valid
class, protocol and category.

The protocol method decoder now skips only that entry and reports it as
a protocol failure. The protocol keeps its other method declarations, and
its record is marked partial with protocol_method_selector_empty.

Fixes #4540

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

* fix(objc): treat empty local category class names as unresolved

A category whose class reference pointed at a mapped local class with an
empty class_ro_t name took that empty string as class_name. The final
metadata schema then rejected the whole result, discarding every valid
class, protocol and category.

An empty local class name now fails the class reference the same way an
unmapped one does: the category is kept with class_name null,
class_source unresolved and a category_class_unresolved issue, and its
name, methods and protocols survive.

Fixes #4541

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-17 06:43:12 +08:00
VADLACHARYandmorluto 8267edc0ef fix(ghidra): resolve keg-only Homebrew openjdk on macOS (#4556) (#4557)
* fix(ghidra): resolve keg-only Homebrew openjdk on macOS (#4556)

Fixes an issue where the `ghidra-java` check fails when OpenJDK is installed via Homebrew on macOS (where it is keg-only and not added to `PATH` or resolved by `java_home`).

- **Problem:** When `options.javaHome` is undefined, the provider defaulted to a bare `"java"` command, causing valid Homebrew OpenJDK installations to be reported as missing dependencies.
- **Solution:** Replaced the single fallback with an absolute candidate list (`javaCandidates`) that probes standard Homebrew OpenJDK paths (`/opt/homebrew/opt/openjdk/...` and `/usr/local/opt/openjdk/...`) before falling back to `"java"`. All candidates are absolute, preserving existing constraints and current behavior.

* fix(ghidra): fix javaHome type narrowing in java candidate list (#4556)

Fixes a TypeScript compilation error (`TS2322` and `TS2345`) in `src/ghidra/GhidraInstallation.ts` where `options.javaHome` was inferred as `string | undefined` inside `path.join()`.

- **Problem:** The ternary operator structure prevented TypeScript from narrowing `options.javaHome` to `string` in the `path.join()` call when `options.javaHome` was defined, causing CI compile checks to fail.
- **Solution:** Refactored the `javaCandidates` assignment using an immediately invoked function expression (IIFE) with an explicit `if (options.javaHome === undefined)` check. This properly narrows `options.javaHome` to `string` in the non-undefined branch while keeping the same logic and Homebrew OpenJDK paths intact.

* fix(ghidra): order homebrew java candidates by host architecture (#4556)

Updates the Homebrew OpenJDK candidate ordering in `src/ghidra/GhidraInstallation.ts` to be architecture-aware, addressing review feedback regarding cross-architecture runs (e.g., x64 under Rosetta on Apple Silicon).

- **Problem:** Previously, `/opt/homebrew` was always checked before `/usr/local`. When running REA as an x64 process under Rosetta on Apple Silicon, this could incorrectly select an ARM64 JDK over an Intel/x64 one, triggering a `nativeDecompilerCheck` failure and missing the valid fallback.
- **Solution:** Dynamically orders `javaCandidates` based on the runtime `architecture`. On `arm64`, `/opt/homebrew` is prioritized; on `x64`, `/usr/local` is prioritized. Bare `"java"` remains first in both cases to preserve standard PATH resolution.

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-17 06:41:38 +08:00
360626434d fix(electron): delegate once dispatch to the active runtime (#4605)
* fix(electron): delegate once dispatch to the native runtime

Preserve the selected runtime callback dispatch and metadata semantics, recording actual entry or explicit observation gaps. Add paired cross-runtime controls and verify the real application hook source identity.

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

* refactor(electron): remove superseded once dispatch branches

The native once runtime owns callable-method selection. Both remaining observation-helper callers pass actual invocation frames, so remove the obsolete fifth argument and its unreachable call/apply dispatch paths.

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

* fix(electron): retain once gaps when registration bypasses on

Keep the unobserved synchronous registration scope with its owning channel and restore the previous scope after errors or reentry. Report the once limitation even when an application getter selects a native alias instead of patched on, while avoiding duplicate same-channel gaps.

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

* docs(electron): preserve native once verification cohorts

Record paired runtime controls, actual Joplin verification, and the unresolved startup, cleanup, and observation limits.

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

* fix(electron): retain once observation gaps on failed operations

Finalize unobserved registration scopes for both success and failure, preserving native errors and recording failed non-callable dispatch metadata.

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

* docs(electron): record failed once operation regressions

Preserve review findings, red/green controls, final runtime comparisons and real application limits without relabeling earlier cohorts.

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: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-17 06:38:13 +08:00
morluto d74e72406a fix(jeb): attribute transport timeouts to requested operations
Project canonical timeout and cancellation facts at the provider boundary while preserving causes, partial observations, live cleanup dependencies and provider-authored deadline guidance.

Validation: four real SDK/HTTP catalog and tool timeout controls fail before the fix. After rebasing onto the Ghidra timeout guidance change, 189 tests across ten files pass, including public CLI/MCP workflows and affected Ghidra tests. Typecheck/lint, docs, format and discovery pass. No native JEB engine or target verification. Refs #4597.
2026-10-17 06:31:16 +08:00
morluto a6e8f5269f fix(jeb): retain cleanup ownership and acquisition outcomes
Bind pending SDK resources to exact cleanup leases, coalesce disposal, and retry only unresolved children. Shared startup callers retain final cleanup while independent peers can cancel their waits.

Attribute SDK aborts by exact owned rejection identity, preserve physical failures under late cancellation, and retain completed Evidence at the application boundary.

Validation: 83 focused tests across eight files; typecheck/lint, docs check, formatting and discovery. Controlled real HTTP/SDK faults only; no native JEB engine or target verification. Refs #4603.
2026-10-17 06:30:02 +08:00
Syed Rishad 1b56237cae fix(ghidra): name the startup deadline setting when startup times out (#4590)
Fixes #4241
2026-10-17 06:10:15 +08:00
morluto 67384b23e5 fix(jeb): cache parsed metadata independently of optional version
Own the parsed engine facts separately from identity projections. Reuse successful unknown-version metadata, retain explicit refresh, and bind each execution identity to its own selected metadata under concurrent inspection.

Refs #4600 and #4597. Six affected files pass 58 tests; static, documentation and discovery checks passed. Real JEB and reconnect behavior remain unverified; prepared reconnect PR3754 stays separate pending cleanup ownership work.
2026-10-17 06:03:51 +08:00
morluto ece277b30b fix(jeb): retain requested operation during metadata probes
Pass the caller operation into the shared client-information producer and consume its parsed facts during inspection. Remove duplicate metadata parsing and the forwarding wrapper.

Refs #4597. Baseline real CLI/MCP controls failed for three requested operations; all 51 affected tests and static checks now pass. The endpoint is simulated; real JEB refusal behavior is unverified.
2026-10-17 05:54:18 +08:00
morluto cd9252bc63 test(filesystem): exercise digest cancellation and admission ownership
Cover ADB pull, package and bugreport physical failures and completed cancellation across successful and failed closes. Verify artifact retention callback timing and Pcap failed-admission owner recovery.

Refs #4598. Controlled real host files and simulated ADB output; native close faults and Android device behavior remain unverified.
2026-10-17 05:47:35 +08:00
morluto e65c3955f9 fix(filesystem): preserve digest failures through cleanup cancellation
Record cancellation at file checkpoints and before owner handoff. Keep physical hash failures, exact reasons, completed facts and failed-close owners distinct through artifact and provider projections and cleanup retries.

Refs #4598. Validated with 566 passing affected tests and 22 platform skips; final Kuna cleanup reason rerun passed 43 tests. No Android device or real JADX/Kuna execution claimed.
2026-10-17 05:47:19 +08:00
morluto 0d22474280 fix(adb): preserve version probe failures during admission
Classify producer-recorded timeout and capture overflow before configuration fallback, and pass typed failures through provider admission. Preserve exact version-probe cancellation reasons and parsed output facts. Clarify command lifecycle diagnostics.

Related: #3886, #4594. Controlled Node subprocess, provider/application and actual CLI regressions cover late abort ordering, active and preadmission cancellation, validated partial Evidence, physical failures, capture overflow and the unchanged ten-second deadline. The affected suite passed 218 tests across 14 files; static/docs/format/discovery checks passed. Native adb/device and Windows execution remain unverified.

Remaining related work: shared file-close cancellation provenance for digest consumers requires a separate producer and consumer migration.
2026-10-17 05:17:16 +08:00
morluto c4475d4a67 fix(adb): preserve command owner cancellation outcomes
Use the subprocess owner cancellation marker instead of the later signal state or an arbitrary ABORT_ERR code. Preserve the exact caller reason and captured output, and share parsed output projection with existence probes.

Related: #4594. Controlled process and source regressions fail on 9a95db681; the affected 14-file suite passes 218 tests. Actual Node subprocesses and simulated adb clients were exercised on macOS; no Android device or native Windows run.
2026-10-17 05:17:16 +08:00
709b5cbdc1 fix(electron): block synchronous shell effects during capture
Block showItemInFolder and writeShortcutLink using the shared active shell boundary, preserving undefined/false synchronous refusal contracts and shape-only timeline evidence.

Retain Nitin13-git implementation and add base-red/shared-contract coverage plus a reproducible unchanged Joplin welcome-attachment reveal. Local87cases, static and docs pass. The explicit test argv shim verifies the operation phase; standard startup and provider cleanup remain unsuccessful and are recorded without a full-workflow performance claim.

Fixes #4393

Co-authored-by: Nitin13-git <internshipsvc01@gmail.com>
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-16 21:03:12 +00:00
morluto 9a95db6819 test(uninstall): verify current recovery snapshots through the CLI
Plan and approve Cursor setup in an isolated home, edit an unrelated server, then uninstall and verify both the historical backup and exact pre-uninstall recovery bytes. Related: #4581, PR #4580.

The new real CLI control fails on f9ccacca2 with a missing recovery file and passes with the recovery owner fix. Verified on macOS; native Windows/Linux runs remain unverified.
2026-10-17 05:01:57 +08:00
morluto eba64e9386 fix(setup): retain skill recovery backup cleanup ownership
Route skill backup selection and verification through the shared recovery owner with the installer resource scope. Reuse verified snapshots without copying and remove duplicate selection and EEXIST handling.

Cover failed closes at first backup, recovery backup, and post-copy verification; block new descriptors while cleanup is pending and retain the exact owner through disposal retries. Related: #4592.

Validation: 158 focused tests and 511 adjacent setup tests passed; check:fast, docs:check and formatting passed. Close failures were injected on real file handles on macOS; native Windows/Linux faults were not reproduced.
2026-10-17 05:01:46 +08:00
Adolanium 967a8bc279 test(uninstall): cover backups that fail verification after copying
Add cases where the backup copy reports success but leaves the recovery
file missing or holding other bytes; uninstall must fail before writing.
Narrow the installation guide so it promises a backup path only for the
failures that report one.

(cherry picked from commit 70bff72403)
2026-10-17 04:57:06 +08:00
Adolanium c15c2062ad fix(uninstall): preserve the configuration as it was before uninstall edits it
(cherry picked from commit 2dfc2d0f7c)
2026-10-17 04:57:06 +08:00
morluto f9ccacca2c fix(ghidra): own configured profile artifact lifetimes
Keep bounded stable reads, format interpretation, snapshot writes and final descriptor cleanup under exact owners. Join complete provider profile work during shutdown and retain primary failures through late cancellation and cleanup retry.

Related: #4584, #4586
2026-10-17 04:50:13 +08:00
morluto 61e688070f fix(adb): refuse transfers when destination existence is unknown
Own ADB command failure facts once and interpret only the clean empty test false result as absence. Preserve unknown probe output and reuse parsed facts for package refusals.

Related: #4574
2026-10-17 04:30:50 +08:00
morluto f26973a529 fix(providers): own profile selection through finalization
Join pending profile producers on cancellation and shutdown. Preserve failed results before later signals, retain every automatic-selection cleanup owner, and move profile file ownership into the registry.

Related: #4569
2026-10-17 04:21:21 +08:00
morluto bf7fad390c fix(cleanup): project resources from unresolved owners
Retain historical cleanup facts while deriving live resource attribution from exact error dependencies and labelled leases. Unlabelled owners preserve uncertainty.

Related: #4578
2026-10-17 04:21:21 +08:00
morluto 50dbd875cf fix(process): preserve owning operation cancellation 2026-10-17 04:21:21 +08:00
bf3475867f fix(electron): preserve native IPC listener behavior (#4389)
Preserve original-function removal, native IPC dispatch and argument identity, with explicit metadata ownership and Evidence verification. Runtime cleanup limitations remain documented.

Fixes #4390.

Co-authored-by: Adolanium <94890352+Adolanium@users.noreply.github.com>
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-17 04:17:11 +08:00
morluto f22e8e7268 test(ghidra): align Java probe fixtures with raw output
Model spawnSync results as typed Buffer output, retain Unicode path assertions, and verify ENOBUFS remains runtime_unavailable with captured diagnostics.

Fixes four stale fixture failures observed in Linux portable and Windows curated CI at ae07d1517. Validation: 131 tests passed across five Java admission files; check:fast passed. Native Windows rerun remains CI verification.
2026-10-17 03:52:49 +08:00
morluto b0ddb58e27 fix(ghidra): own PSP inspection finalization
Retain exact file and directory cleanup owners through profile admission and launch reinspection. Preserve typed primary failures and exact cancellation; keep fingerprint framing and bounds stable.

Validation: 137 tests passed, 3 platform skips across six filesystem/provider/MCP files; check:fast passed. Controlled real-handle fixtures do not establish actual Allegrex execution or native Windows faults.

Refs: #4521, #4517. Shared profile evaluation completion remains tracked in #4569.
2026-10-17 03:52:33 +08:00
morluto ae07d15170 fix(ghidra): explain keg-only JDK admission remediation
Explain how to select an existing Homebrew JDK with JAVA_HOME when macOS
Java discovery cannot find a keg-only installation. Preserve caller-selected
runtime inputs and the Ghidra installation's declared Java bounds. Document
the installation paths and retain a regression showing only the requested
Java command is probed; no automatic JDK discovery or installation added.

Validation: 4 focused files, 122 passed; npm run check and docs:check passed.
Public CLI prerequisite fixtures ran on macOS; Homebrew keg-only guidance
is source/fixture verified against the primary formula documentation, not
a fresh Homebrew installation or a Windows runtime test.

Refs #4556
2026-10-17 03:34:11 +08:00
morluto c75cf827e7 fix(ghidra): retain typed Java prerequisite outcomes
Replace ambiguous absent observations with an owning Result producer for
Java prerequisite probes. Preserve missing-command, runtime-failure and
unresolved-property outcomes, process error codes, both bounded streams,
raw byte counts and partial properties through provider and doctor adapters.
Reject malformed numeric-prefix versions while retaining their source facts.
Migrate all injected hosts to the named probe contract and remove the old
parser/observation ownership from installation discovery.

Validation: 17 focused files, 350 passed; npm run check passed. Public doctor
fixtures distinguish absent/nonzero/malformed commands. Real installed JDK
and Ghidra readiness facts matched across 20 baseline/candidate CLI tasks.
Warm repeat raw replies6392tokens each, elapsed .448/.444s, CPU .662/.660s;
sampled family memory varied and reversed direction on repeat. No causal
performance improvement claim, true-cold or full-agent/fault-path measurement.

Synchronous probe deadline/cancellation/family cleanup remains #4561 and needs
coordinated discovery/admission supervision; no claim that it is fixed here.
No real failing JVM or Windows runtime verification performed.

Refs #4560, #4561
2026-10-17 03:33:16 +08:00
morluto 8116dc0651 fix(native)!: preserve unknown provider symbol addresses
Normalize empty addresses once at the symbol-inventory interpretation
boundary. Keep Swift and Objective-C records with null locations, preserve
the original empty value in evidence.reported_address, and retain partial
coverage instead of failing the entire metadata result. Nonempty provider
address syntax remains unchanged.

Validation: domain and production-registration MCP regressions failed before
the fix. Ten focused files passed 181 tests with 2 skipped, including SDK
converted JSON Schema/catalog validation and graph consumers. npm run check
and docs:check passed (5 documentation tests). Fixture provider only; no
native-provider malformed-symbol or Windows runtime reproduction claimed.

BREAKING CHANGE: Swift symbol address can now be null when the provider has
no location. Clients consuming swift_symbols must handle an unknown address.
The original provider value is retained in optional evidence.reported_address.

Refs #4542
2026-10-17 03:14:30 +08:00
morluto 060587b456 fix(cli): prefer native byte inspection for automatic dispatch metadata
Allow the composed one-shot dispatch workflow to open a covered Mach-O
without selecting a deep provider. The registry still owns selector
precedence; explicit request/environment choices remain binding, while
non-Mach-O and unsupported hosts retain deep symbol fallback. Requested
snapshots keep ordinary binding and profile semantics.

Share runtime composition and target admission with the native byte reader.
Keep target parsing, resource ownership and Evidence on the original session.

Validation: 13 focused files, 192 passed and 2 skipped; npm run check passed.
The six previously blocked native property CLI controls now pass. Ten measured
CLI workflows preserved exact metadata facts; warm raw reply token medians
1344/927, elapsed .475/.547s, CPU .691/.745s, sampled family RSS351.2/359.3MiB
baseline/candidate. Baseline used explicit Ghidra workaround; true cold and
full-agent costs unmeasured. No speed/memory improvement or Windows claim.

Refs #4555
2026-10-17 03:11:32 +08:00
morluto a70c42e03d fix(native): recover malformed Objective-C names at decode boundaries
Validate protocol and implementation selectors, ivar names and category
names before publishing records. Share validated local class identity
between class, superclass and category consumers, retaining unresolved
addresses and the decoder's failure reason.

Byte fixtures reproduced the reported validation failures. Final focused
4-file run: 118 passed; npm run check passed. Six existing CLI property
controls remain blocked before decoding by deep-provider auto-selection
ambiguity, as exposed by improved assertion diagnostics (#4555).
No Windows runtime or real malformed-application verification performed.

Refs #4540, #4541, #4533, #4534, #4115, #4555
2026-10-17 03:02:26 +08:00
morluto f31b231a92 fix(cli): isolate static native inspection from deep provider selection
Compose native utility sessions for the five existing static CLI questions.
Keep ordinary session routing for native runtime observations, and retain
caller-selected environments, target admission, Evidence and cleanup.

Validation: 17 focused files, 97 passed and 2 skipped; npm run check passed.
The three thin Mach-O CLI regressions now pass with macOS native utilities.
No Windows runtime verification or performance measurements performed.

Refs #4555
2026-10-17 02:56:35 +08:00
morluto 5eaef92e5b test(firmware): assert cleanup provenance through composed failures
Refs #4475.

Check public cleanup projection and retained primary diagnostics instead of a
superseded error subclass and flat diagnostic location. Require the caller's
retained error to resolve after the exact cleanup owner succeeds on retry.

Validation: 59 tests passed in the cached Linux audit container with Node
24.18.0 and npm 11.16.0, including both failing CI regressions. Producers are
controlled Node fixtures, not real unblob/binwalk engines. The temporary
container was removed through its recorded owner. Static checks passed.
2026-10-17 02:47:42 +08:00
morluto 0f61261e01 fix(native): retain owned preparation failures through command adapters
Refs #4522, #4526, #4555.

Retain producer-owned compiler output and cleanup markers through xcrun
resolution and native failure projection. Keep physical preparation failures
primary and preserve separate command deadline classification for interrupted
preparation. Omitted output facts stay unknown; client close does not resolve
foreign helper cleanup. Include CLI failure output in native regression reports.

Validation: 87 focused tests passed. Broader 140-file run passed 1483 tests,
skipped 109, and exposed three pre-existing deep-provider selection failures
tracked in #4555. Static checks passed. Compiler faults use controlled Node
fixtures; native Swift compiler faults and Windows execution were not verified.
Other command consumers still need preparation-fact propagation.
2026-10-17 02:47:42 +08:00
SAKURA 4838f44493 docs(i18n): explain translation contribution entry points
Document the translated README and website PO contribution entry points. Resolve README filenames through the existing locale inventory, preserving canonical Korean README links.

Validated on Linux with docs:check, five README consistency tests, all 20 inventory README paths, and current-head CI/automated reviews.
2026-10-17 01:49:55 +08:00
星闌 60bf102459 docs(browser): correct cache fingerprint completeness limits
Document complete decoded cache-body fingerprints, the existing CDP message bound, and incomplete coverage for missing/undecodable bodies.

Validated with docs:check, eight storage/cache tests, and current-head automated review/CI.
2026-10-17 01:27:45 +08:00
Arjun 6c06f406f6 fix(javascript): keep invalid V8 script events out of policy filtering
Preserve invalid V8 script events as unavailable protocol data instead of merging them into target-policy exclusions. Keep independent producer counts, original Evidence, and existing target authority.

Validated with 90 focused Linux tests, real Node Inspector CLI/MCP captures, complete static/architecture checks, and current-head automated review/CI.

Fixes #4376.
2026-10-17 01:16:20 +08:00
JNTMTMTM ab13a1c67a fix(javascript): include response carriers in endpoint traces
Include request and response endpoint carriers in the shared semantic query seed matcher so traces retain resolved response consumers, source identities, and incomplete coverage.

Validated with 44 domain tests, compiled CLI/MCP parity, full static/architecture checks, and current-head automated review/CI.

Fixes #4213.
2026-10-17 01:11:36 +08:00
morluto 9d940c2f28 fix(ghidra): preserve lifecycle ownership and failure provenance
Refs #4502, #4506, #4508, #4518, #4522, #4526.

Retain snapshot, endpoint, launch rollback and compiler cleanup owners. Join request finalization and cleanup cancellation. Preserve bounded compiler output and original byte counts through capture and Ghidra failures. Normalize execFile cancellation once at its producer.

Validation includes real macOS Ghidra TCP MCP success workflows, controlled Node compiler/process fault fixtures, focused regressions and static checks. Native Swift compiler faults, Windows execution and stdio transport were not verified. Broader startup primary classification in #4517 and native adapter propagation remain unfinished.
2026-10-17 00:44:02 +08:00
morluto 5f9ac2c98a fix(windows): retain primary snapshot and cancellation failures
Refs #4502, #4504.

Keep the original snapshot failure alongside disposal uncertainty and retain exact cancellation reasons at the TypeScript runtime boundary.

Native Windows execution and addon fault behavior were not verified on this macOS host; checked handle-release ownership in #4505 remains open.
2026-10-17 00:44:02 +08:00
morluto d292c445ee fix(apple): share dylib read ownership and cancellation facts
Refs #4495.

Normalize dylib file access once at the admitted descriptor boundary. Preserve full file identity, physical read outcomes, cancellation provenance and cleanup retry through artifact analysis.
2026-10-17 00:44:02 +08:00
morluto 2d163c12b1 fix(firmware): own file access through publication and cleanup retry
Refs #4475.

Share descriptor scopes across firmware inspection and publication. Preserve primary failures, exact cancellation reasons, and unresolved resources through finalization.
2026-10-17 00:44:02 +08:00
morluto 2b7b698db8 fix(cutter): retain descriptor owners and join discovery cancellation
Refs #4471, #3946.

Read only admitted regular descriptors without blocking on pipes. Retain failed closes, join discovery workers, and propagate cancellation through CLI and MCP. Preserve command and descriptor cleanup as independent owners.
2026-10-17 00:44:02 +08:00
morluto ce0d6790ed fix(browser): bind CDP cleanup failures to exact transport leases
Refs #4467.

Retain failed socket owners across translated failures and resolve aggregate cleanup only after every required transport closes. Preserve concurrently active peer sessions.
2026-10-17 00:44:02 +08:00
morluto effb71f59c fix(providers): preserve bounded artifact reads and cleanup owners
Refs #4373, #4455, #4457, #4458.

Use canonical artifact ownership in Go, Rust, SQLite and PE readers and their failure consumers. Admit PE read budgets before allocation and ignore zero-extent sections in interval ownership. Close Rust resources through CLI and MCP lifecycles.
2026-10-17 00:44:02 +08:00
morluto 542fa84957 fix(artifacts): retain descriptor ownership and read completion facts
Refs #4458, #4483.

Own admitted descriptors through final close, retain exact failed owners, and preserve physical read failures and cancellation reasons. Track stream coverage independently from delivered bytes and share the boundary across archive, Mach-O, hashing, and output readback consumers.
2026-10-17 00:44:02 +08:00
morluto 9abdad335c fix(setup): preserve configuration identity and inspection uncertainty
Refs #4378, #4384, #4432, #4496, #4501.

Keep inaccessible MSIX targets unknown, preserve literal AppData roots, and distinguish absent skill destinations from retained files. Isolate registration tests from host provider discovery.

Windows package behavior was exercised through filesystem fixtures; the installed Windows Claude application was not verified.
2026-10-17 00:44:02 +08:00
morluto 6d52814070 fix(kuna): retain fallback diagnostics for empty stderr
Refs #4374.

Preserve the command error when stderr contains no diagnostic text.
2026-10-17 00:44:02 +08:00
윤종근andmorluto 313a86a369 fix(i18n): correct Korean website translations (#3799)
Correct mistranslated technical terms and missing details across the Korean
website catalog. Improve sentence flow and spacing, and remove trailing
periods from Korean headings while keeping comparable overall text length.

Preserve English source keys, placeholders, comments, metadata, and obsolete
translation memory.

Refs #3798

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-17 00:31:01 +08:00
Abishek 8194695144 fix(reference): resolve extensionless imports to .mts and .cts files (#4223)
* fix(reference): resolve extensionless imports to .mts and .cts files

Include .mts and .cts extension probe slots in RESOLUTION_SUFFIXES so that relative imports and requires targeting ESM and CJS TypeScript files without an explicit extension resolve to internal relationships instead of remaining unresolved.

* test(reference): restore regression coverage for extensionless .mts and .cts imports

Restore tests covering extensionless imports, requires, and directory indexes to .mts and .cts files so that probe suffix omissions are caught by automated test regression.
2026-10-17 00:25:43 +08:00
Henglie 467f5572ac fix(native): report PE input byte budgets as file-size limits (#4290)
* fix(native): report PE input byte budgets as file-size limits

PeResourcesProvider translated every ArtifactReaderFailure with reason
"limit" into a memory resource constraint, so an image above the
caller's max_file_bytes was reported as a memory problem with memory
remediation advice. The bound that was exceeded is the input file's
byte length, which the caller raises with a flag.

Record the constrained bound on the failure the way ElectronFuseReader
already does, declare it at the reader's two input-byte refusals and at
the parser's metadata and entry budgets, and report that bound instead
of assuming memory. Readers that do not declare a boundary are
unchanged.

Fixes #4192

* fix(native): keep caller-selected PE budget refusals actionable

Address review findings on the budget classification:

- withCleanup now forwards the constrained bound, so a file that grows past
  its byte limit while its handle also fails to close is still reported as
  a file-size refusal rather than a memory one.
- Both caller-selected bounds carry their own remediation, pointing at
  max_file_bytes and max_entries instead of the file-size fallback's worker
  write-limit advice.
- AnalysisResourceConstraintError can declare that retrying cannot succeed.
  The entry budget is deterministic for the same file and limit, so it no
  longer inherits admission capacity's retryable behaviour.

The projection falls back to the previous resource-kind rule when a caller
does not declare retryability, so other capacity refusals are unchanged.

* fix(native): preserve budget retryability and name the schema ceiling

Address the remaining review findings on the budget classification:

- Failure redaction and the caller-copy path rebuilt resource constraint
  errors without forwarding retryable, so a deterministic capacity refusal
  projected as retryable again and automated callers could loop on it.
  Both constructors now forward the declared boolean.
- Both remediations recommended raising a bound the caller had already
  set to the schema maximum. The maxima are now named constants shared
  with the schema, and the remediation names the operation's hard ceiling
  when the caller is already at it.
2026-10-17 00:25:13 +08:00
ShuaixinHuang 5c0fbf47cb fix(setup): reject NUL bytes in registration environments (#4335) 2026-10-17 00:24:35 +08:00
Liu Jie 17488bd639 fix(javascript): preserve uncertainty in compound assignment results (#4342)
* fix(javascript): preserve uncertainty in compound assignment results

* fix(javascript): retain callable logical assignment alternatives

* fix(javascript): read prior module export assignment values

* fix: keep mutable module export assignment candidates conservative

* fix: preserve mixed receiver values and wrapped assignment targets

* fix: carry mutable export uncertainty through copied values
2026-10-17 00:24:09 +08:00
양예성 abbdb46427 fix(docs): correct Korean README formatting and website translations (#4344)
* Fix formatting in Korean README file

Corrected formatting in the README_ko.md file.

* Fix formatting of REA website link in README_ko.md

Update the REA website link format in Korean README.

* fix(docs): prevent misleading Korean website instructions

Correct the reviewed Korean catalog translations and repeated errors so
readers retain the English source's technical meaning and evidence.
Align the Touhou blog card and article bylines with the existing Korean
author/date format. Restore separators, identifiers, prerequisites,
license names and runtime status descriptions without rewriting source
messages or obsolete translation memory.

Constraint: Preserve msgid, msgctxt, named placeholders and recorded source facts
Confidence: high
Scope-risk: moderate
Tested: Strict Korean catalog validation; website checks and 18 regressions; npm run check; git diff --check; local blog and article browser checks
Not-tested: Every translated page across mobile widths and themes; production deployment
2026-10-17 00:23:48 +08:00
星闌andCodex 6d2eeeaed0 fix(javascript): resolve child ownership at receiver reads (#3819)
* fix(javascript): resolve child ownership at receiver reads

* fix(javascript): preserve deferred field child candidates

---------

Co-authored-by: Codex <codex@openai.com>
2026-10-17 00:22:26 +08:00
윤종근andKaoru0822 48c3d2d170 fix(website): use Noto Sans KR for Korean text (#3821)
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-17 00:22:09 +08:00
Hahaa909 5875fce666 fix(managed): verify Mach-O ABI-prefixed exports exactly (#3974) 2026-10-17 00:21:36 +08:00
Arjun 4cdf3b056f fix(evidence): classify ledger duplicate and missing review Evidence as caller input (#4452)
build_reconstruction_obligation_ledger reported a bundle with duplicate
record IDs, or a reviewed obligation citing Evidence absent from the
bundle, as unreadable_output and pointed callers at rea doctor. Both are
caller input mistakes.

Throw dedicated TypeError subclasses at those checks and map them to
workflow input errors, following the NonCanonicalEvidenceBundleError
approach from #3219 so unrelated TypeErrors stay protocol failures.

Fixes #4444
2026-10-17 00:19:52 +08:00
Kevin Joshua L 2bba39bc2b fix(domain): classify TypeScript parameter properties as declarations rather than identifier reads (#4465)
* fix(domain): classify TypeScript parameter properties as declarations rather than identifier reads

Recognize TSParameterProperty in isPatternOwnerDeclaration, isDeclarationIdentifier, and patternContains so parameter property definitions are not emitted as identifier reads, while retaining valid initializer reads. Fixes #4371

* test(domain): assert constructor body parameter property reads survive
2026-10-17 00:19:40 +08:00
ShuaixinHuang ffeba6aae0 test(adb): cover staged APK names and evidence identity (#4354)
test(adb): verify staged APK identity in reinstall CLI coverage
2026-10-17 00:18:01 +08:00
Kevin Joshua L 3d44176843 fix(native): reject empty Swift class vtable owner before slot insertion (#4469)
* fix(native): reject empty Swift class vtable owner before slot insertion

Throw an explicit RangeError within the descriptor recovery block when a Swift class vtable descriptor class name is empty. This prevents emitting malformed slots that fail final schema validation, allowing partial coverage and valid sibling descriptors to be preserved. Fixes #4369

* test(native): verify decoding continues for valid Swift vtable siblings after a malformed descriptor
2026-10-17 00:17:37 +08:00
Ace 90457af181 fix(contracts): complete canonical mobile graph inventory examples (#4026)
* fix(contracts): complete canonical mobile graph inventory examples

* test(contracts): place canonical graph CLI regression in acceptance

* refactor(contracts): reuse artifact identity owners in mobile examples
2026-10-17 00:15:58 +08:00
Swatantra Yadav e0e8b30b7f fix(process): retain selected freshness requirement in comparison Evidence parameters (#4065)
Preserve caller-selected max_capture_age_ms in compare_process_captures Evidence parameters so returned and session-retained records distinguish freshness-constrained comparisons and derive distinct semantic Evidence identities.

Add boundary MCP regression coverage asserting that max_capture_age_ms is omitted when unconstrained, retained in parameters and session evidence when specified, and differentiates comparison Evidence IDs.

Closes #4000
2026-10-17 00:15:27 +08:00
Yankı Küçük bef3fbbc6e fix(adb): keep a bare numeric wm density as the physical density (#4074)
Older Android builds print `wm density` as a bare number. The parser's
documentation named that form, but only the labeled `Physical density:`
and `Override density:` lines were matched, so a bare `420` left
`physical_density` null in successful display Evidence with no limitation
saying the declaration was unread (#3942).

The parser now reads the first bare numeric line as the physical density
when no labeled physical declaration is present; a labeled declaration
still wins so a mixed dump cannot downgrade the value. Override density
and physical size are unchanged.

Validation: parser unit tests for the labeled, bare, CRLF, mixed and
unrecognized forms, plus a provider-level regression through the
inspect_adb_display operation; two of them fail on main and pass here.
The issue's three CLI controls through `rea inspect-adb-display` against
a stub adb return physical density 420 for the bare form and keep the
labeled and override controls. Typecheck, lint and module boundaries pass.

Closes #3942
2026-10-17 00:15:05 +08:00
e0b989cc0a fix(ghidra): consume inspected configuration directory identity (#4077)
* fix(ghidra): consume inspected configuration directory identity

Carry the parsed release coordinate through installation inspection and provider prerequisites into the launcher. Remove duplicate property reads and basename guesses, preserve unresolved identity, and canonicalize directory versions without changing observed provider versions.

Keep verifier callers, lifecycle regressions, and installation guidance aligned. Extract the Ghidra-only follow-up from #2293 without changing CLI output or unrelated documentation.

* docs(ghidra): clarify startup failure side-effect boundary

Distinguish the launcher's directory and Java startup guard from the client's earlier private runtime allocation and target snapshot. Keep existing failure cleanup and runtime behavior unchanged.

---------

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 00:13:46 +08:00
abdalrhman 3eeada71fd fix(javascript): accept empty semantic property seeds (#4239) 2026-10-17 00:10:13 +08:00
5b08d04302 fix(package): parse tar listings without platform line endings (#4315)
Windows ships two tar implementations. `C:\Windows\System32\tar.exe` (bsdtar)
terminates every listed member with CRLF, so `verify-package-pack.mjs`
splitting the listing on a bare "\n" left a trailing "\r" on each non-final
member. `verifyPackedBridge` compares member identities exactly, so it reported
`package omitted package/bridge/hopper_bridge.py` although the tarball ships
that member (#4128).

`parseTarListing` normalizes line endings once, at the listing owner, and
preserves every member string beyond its line ending. `verifyPackagePack` is
the only tar listing producer in the package verifiers, so its consumers now
compare bare member names; the prefix checks in the same file keep their
behavior because they never matched a line ending.

Covered by `tests/conformance/package/packedTarListing.test.ts`: CRLF and LF
listings both yield exact bridge identities, the trailing line ending no longer
produces an empty member, member strings keep embedded spaces, and a regression
guard pins the previous parse's failure.

Real Windows 11 evidence (`package/bridge/hopper_bridge.py` in the listing):

| tar                                | listing | split("\n") | parseTarListing |
| ---------------------------------- | ------- | ----------- | --------------- |
| Git for Windows GNU tar            | LF      | true        | true            |
| C:\Windows\System32\tar.exe        | CRLF    | false       | true            |

Fixes #4128

Co-authored-by: Chow Kim <catch@126.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 00:09:59 +08:00
44c9ff7b75 fix(apple): preserve 128-bit keyed archive integers (#4324)
* fix(apple): preserve 128-bit keyed archive integers

* fix(browser): retain cancelled scenario steps

* fix(apple): guard wide plist integer decoding

* test(browser): retain cancellation error contract

---------

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 00:09:47 +08:00
Paritosh LahreandClaude Opus 5.5 f433786463 feat(android): make the JADX operation deadline configurable (#4466)
* feat(android): make the JADX operation deadline configurable

JADX operations used a fixed 120 s provider deadline and a separate fixed
120 s per-call bound in the session, so an APK whose cold load exceeds
120 s could not complete, and raising only the MCP client timeout could
not extend either. Add REA_JADX_OPERATION_TIMEOUT_MS, parsed once by the
JADX configuration owner (decimal milliseconds from 1 to 2147483647,
default 120000). The provider resolves it before acquiring anything and
uses it for the operation deadline and its timeout errors, and the session
uses it for every upstream call. Doctor reports the effective value, and
setup forwards an explicit value like the other JADX settings.

Fixes #3918

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

* test(android): isolate JADX fixtures from inherited shell settings

The shared JADX protocol fixture spread the developer's process.env, so an
exported REA_JADX_OPERATION_TIMEOUT_MS (or REA_JADX_HEAP_MIB) changed every
provider test that never selected one. Keep inheriting the host environment
for process launch, but take REA_JADX_* settings only from the fixture.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 00:06:32 +08:00
kukinghanandgreptile-apps[bot] 29b8769d2c fix(website): correct mistranslated zh-CN catalog entries (#3856)
* fix(website): correct mistranslated zh-CN catalog entries

The zh-CN gettext catalog had not been reviewed. Terminology was taken from
unrelated dictionary senses and several file names, version numbers and
observed byte values were translated as if they were prose, so rendered pages
showed content that did not match the English source or the recorded target
identity.

Rewrites 236 msgstr values across the catalog:

- Terminology now uses one term per concept: mask -> 掩码, assembly -> 汇编,
  helper -> 辅助函数, source -> 源码, native -> 原生, oracle -> 校验基准,
  authenticator -> 身份验证器, agent -> 编程助手, and flag stays untranslated.
- Literal values are preserved: MAIN.EXE, main/, Ghidra 12.1.x, 0x5b?,
  export.js, preload.js and npx rea-agents@latest setup are no longer
  translated.
- Word-salad sentences are rewritten in natural Chinese, including
  "剂将它们转化为方程" -> "编程助手把它们转化为方程".
- Step-marker separators damaged from "·" to "*" are restored.

No msgid or msgctxt changes, so translations extract is not required and no
entry becomes fuzzy or empty.

* Update website/locales/zh-CN/messages.po

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* Update website/locales/zh-CN/messages.po

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-10-17 00:04:58 +08:00
Ace 13d04a0f19 fix(mcp): declare Frida status session mutation (#3902)
* fix(mcp): declare Frida status session mutation

* style(test): format hook-normalized cancellation options
2026-10-16 23:59:29 +08:00
Afloat c012015ae8 fix(setup): deduplicate selected clients before planning (#3919)
Normalize client identities once in the shared workflow while preserving first-selected order. Keep planning, readiness, default skill selection and configuration application on the same unique list.

Cover duplicate selections in direct workflow tests and an isolated compiled CLI preview/apply/rerun journey. Refs morluto/rea#3847.
2026-10-16 23:59:15 +08:00
zhugehyukandClaude Opus 5.5 d62d3082c2 feat(ghidra): support raw big-endian m68k analysis (#4018)
* feat(ghidra): support raw big-endian m68k analysis

Admit `m68k` for headerless raw-binary targets. Admission requires a
2-byte aligned entry and a mapping within 0x0..0xffffffff, and Ghidra
imports with BinaryLoader using 68000:BE:32:default, the default compiler
specification, and the `ram` default address space.

`6502` and `m68k` now share one RAW_ONLY_ARCHITECTURES owner, so detected
targets, Evidence subjects, snapshots, and lifecycle output contracts
exclude both instead of repeating a 6502 literal.

Ghidra's default 68000 language decodes the 68040 superset; separate
68020/68030/CPU32/ColdFire variants, Amiga/Atari containers, and reset
vector discovery remain unsupported. Windows Ghidra P0 still admits raw
6502 only.

Verified with Ghidra 12.1.4 on macOS against an 8-byte self-built fixture
(nop; nop; moveq #1,d0; rts at 0x1000): load image verified in `ram`,
entry decompiles to `return 1`.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* test(ghidra): verify raw m68k analysis with real Ghidra

Add npm run verify:ghidra:m68k. The verifier loads a source-owned 22-byte
68000 fixture (annotated hex, no assembler) at 0x1000 and checks, through
the compiled CLI and stdio MCP server: the 68000:BE:32:default BinaryLoader
import, load-image parity, exact mapped bytes, both functions, the bsr.b
call reference, decompilation by address and file offset, rejection of a
misaligned entry and of a mapping past 0xffffffff without replacing the
active session, unchanged source bytes, and runtime cleanup.

Run with Ghidra 12.1.4 and JDK 21 on macOS arm64. Linux has not been
exercised; Windows Ghidra P0 does not admit m68k.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 23:57:34 +08:00
Burak Kalaycı 56738f3070 fix(i18n): correct Turkish website terminology (#4166)
* fix(i18n): correct Turkish website terminology

* fix(i18n): normalize coding agent terminology
2026-10-16 23:56:58 +08:00
duckhunt113andMark 2d95b85aa4 fix(managed): report stream-name errors at file offsets (#4418)
Co-authored-by: Mark <m-user@Marks-MacBook-Air.local>
2026-10-16 23:56:08 +08:00
Ysopwnable 9a82046b07 fix(cutter): attribute the host application version
Identify the Cutter host through Qt applicationVersion rather than the embedded Rizin module-version report. Preserve unknown host metadata without backend fallback and reuse the existing per-plugin lifetime observation.

Validated 13 Python protocol tests, 66 related CLI/MCP/provider tests, full static/docs gates, and official Linux Cutter 2.5.0 Qt5 public workflows. Native discovery and both Evidence version fields match the independently observed host version; source paths and cleanup remain preserved. The named-tool workflow serialization proxy drops from 8767 to 6912 o200k tokens; this is not model billing.

Fixes #4498.
2026-10-16 23:33:13 +08:00
Ysopwnable 2c1dee16df fix(cutter): read the active document from core configuration
Read file.path through CutterCore's scalar configuration API instead of invoking the debug-only currentlyOpenFile backup field. Preserve exact string paths and unknowns so existing discovery, generation admission and Evidence consumers receive the owning facts.

Validated 11 Python protocol tests, 66 relevant CLI/MCP/provider tests, full static/docs gates, and official Linux Cutter 2.5.0 Qt5 workflows with a Unicode/#? ELF path. Same-path reload identity remains partial and native document switching is unverified.

Fixes #4497.
2026-10-16 23:12:18 +08:00
jayinversandmaxazure 9e7e13fb71 fix(pe): ignore zero-extent sections in resource interval ownership (#4463)
Both resource-interval overlap predicates in PeResourceLayout used strict
endpoint inequalities without an extent or point ownership rule, so a
section with VirtualSize=0 and SizeOfRawData=0 inside a resource interval
was rejected as overlapping virtual sections, and the same empty section
inside a header-backed payload was rejected as ambiguous header mapping.
An empty payload at an RVA that starts a positive-extent section below
SizeOfHeaders silently resolved to the header offset even though that
section owns the point.

Normalize intersection once at the owning mapper: a section without
virtual extent owns no byte and never conflicts; an empty resource owns
exactly its start point, so a positive-extent section starting there
conflicts with header-backed ownership and keeps the existing
ambiguous-mapping failure. Positive-extent overlap rejection is
unchanged.

Fixes #4455
Fixes #4373

Co-authored-by: maxazure <maxazure@gmail.com>
2026-10-16 23:02:03 +08:00
Ysopwnable a6c6a76d37 fix(cutter): cancel discovery probes and queued work
Propagate CLI/MCP cancellation through shared Cutter discovery, stop queued probes, and drain admitted workers. Deliver terminal bridge results only after socket closure. Preserve confirmed command Evidence and caller reasons when cancellation arrives during finalization.

Validated 66 focused tests, full CI static/docs gates, actual CLI SIGINT, compiled stdio cancellation/recovery, and official Linux Cutter 2.5.0 Qt5 CLI/MCP ELF workflows. Native UI cancellation and descriptor filesystem cancellation remain unverified.

Fixes #3946.
2026-10-16 22:49:06 +08:00
180ba03bfd fix(electron): preserve cancellation across waits, state reads and cleanup
Fixes #4388. Refs #4027.

Observe cancellation during first-window waits and state reads; preserve cancellation and caller reasons before cleanup and retain partial observations when cleanup fails. Keep late cancellation and cleanup failure precedence.

Validation: 49 focused cases, static/docs checks, six controlled verifier-oracle cases and 39 current-head CI checks passed. Codex and Greptile found no remaining blocking issues. Real Joplin cancellation phases verified; complete Linux cleanup/first-signal CLI exit and packaged-runtime startup remain explicitly unverified/failed. Both verifiers now fail incomplete cleanup, including after main-process exit.

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: Adolanium <94890352+Adolanium@users.noreply.github.com>
2026-10-16 21:12:26 +08:00
Ysopwnable 1ff705473a fix(rizin): retain interrupted command frames (#4459)
Retain received complete and unfinished Rizin command bytes with bounded UTF-8 capture, truthful completion uncertainty and exact launch provenance. Keep captures independent of cancellation, protocol and cleanup precedence; wait for bounded output settlement on exit and preserve active bytes across stdin failures.

Validated353 focused tests (one existing skip), full CI static/docs checks, real official Linux Rizin0.9.1 interruption probes, paired completed public CLI/stdio MCP tasks, and focused mutation checks. Required CI passed; exact-head Codex review reported no findings and Greptile5/5. Fault cleanup uncertainty and partial-marker ambiguity remain explicit.

Fixes #4015. Follow-up to #4227.
2026-10-16 21:08:47 +08:00
Ysopwnableandhhang fe2a398dd9 fix(managed): retain descriptor ownership through hash cancellation
Fixes #4381. Follow-up to the Linux lifecycle audit in #4227.

Keep managed source hashing on bounded reads through its existing FileHandle owner. Preserve exact opaque cancellation reasons before filesystem or cleanup interpretation, retain genuine failed closes, block unsafe admission, and recover the same provider after verified cleanup.

Validated with 484 related tests, source/docs checks, seven real Linux provider cancellation/recovery scenarios using an unchanged official Microsoft .NET assembly, and paired actual CLI/stdio MCP Evidence workflows. Normal completed-task timing, CPU, memory and token proxies remained comparable.

Adopts xu91102/rea commit ebeec580c839f2f72144282a44f4e11ea5759843 with independent Linux verification and cancellation-reason hardening.

Co-authored-by: hhang <169027046+xu91102@users.noreply.github.com>
2026-10-16 18:49:10 +08:00
Ysopwnable c884864876 fix(filesystem): observe cancellation after file finalization
Fixes #4379.

Observe cancellation after file metadata, verified descriptor closure and recovered scope cleanup. Retain completed physical read outcomes and acquired-file digests under cancellation; preserve original failures and exact cleanup owners when close fails.

Bind cancellation markers to their verified owner so raw readers preserve nested callback outcomes and exact caller reasons. Reuse one ADB partial-result mapper for file, package and bugreport acquisition.

Validated with 570 related tests, source/docs checks, real Linux filesystem cancellation/cleanup/nesting probes and actual public CLI/MCP Evidence workflows.
2026-10-16 18:17:47 +08:00
b25d3517b6 fix(browser): report cancellation after cleanup and source-map abort (#2893)
* fix(browser): report cancellation after cleanup and source-map abort

An interrupt during CDP cleanup could still publish the captured page, and a source-map download abort was wrapped as a provider failure. Both now return AnalysisCancelledError for the requested operation.

* fix(setup): keep Devin configuration JSON-formatted

The empty DSH_HOME fix also changed Devin's declared format to "devin", which is not a configuration format and stops the typecheck. Devin still writes mcp_config.json.

* test(browser): keep cleanup cancellation coverage in its own suite

The added interrupt cases made the existing lifecycle suite exceed the function-length lint limit.

* test(ghidra): drop the duplicated path join import

The inventory MCP test imported join twice, which fails lint on current main.

* test(cli): type the retained skill directory pairs

noUncheckedIndexedAccess treats the unlabeled pair list as an open array, so the directory passed to join was string or undefined.

* fix(browser): preserve source-map cancellation results

* style(test): format the partial Frida load cancellation fixture

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

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: REA Maintenance <rea-maintenance@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-16 03:09:57 -07:00
Ysopwnable 781d5751ad fix(pwntools): preserve worker ownership across launcher exec
Fixes #4011.

Normalize exec-aware worker ownership at the shared pwntools decoder boundary. Preserve parent, process-group and run-token checks, explicit command pins, partial observations, and retryable cleanup.

Validated with real Linux pwntools/GDB/pwndbg CLI/MCP workflows, native-exec cancellation and foreign-token controls.
2026-10-16 16:47:20 +08:00
YsopwnableandKaoru0822 d72d856f58 feat(electron): resolve stable local constant window options
Resolve stable local constant BrowserWindow options with retained provenance and shared early-exit mutation queries. Preserve source ordering and unknown results for relevant mutations or escapes.

Includes real Joplin correctness/cost receipts and regression coverage.

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-16 14:52:33 +08:00
Ysopwnable 5a77acfbcd fix(browser): preserve cleanup failure diagnostics
Preserve Electron cleanup observer reasons and typed primary failures through the shared error projection. Keep ownership and partial Evidence semantics intact.

Fixes #4403. Refs #4227, #4282.
2026-10-16 14:51:25 +08:00
Ysopwnable 2c7583599b fix(javascript): preserve guarded factory fallthrough (#4341)
Preserve per-exit factory initialization and shared export facts.
Keep capture/caller boundaries, call-kind semantics and conditional
registration evidence consistent through CLI and MCP.

Fixes #3963
2026-10-16 13:10:49 +08:00
Kaoru0822andClaude Opus 5.5 8ab5024397 fix(kuna): stream string scans instead of capping them at 16 MiB (#4318)
* fix(kuna): stream string scans instead of capping them at 16 MiB

`list_strings` and `search_strings` buffered Kuna's whole string JSON and
refused anything above 16 MiB. Kuna writes about 290 bytes per row, so
every app above roughly 10 MB failed with a non-retryable memory error:
a 20 MB Ghostty binary produces 31 MB, Raycast 30 MB and an arm64
Firefox XUL 179 MB.

Parse the scan as Kuna writes it. `runOwnedCommand` accepts a stdout
sink, and `KunaStringInventoryReader` owns the interpretation: it
tokenizes synchronously with stream-json, keeps exact u64 address
tokens, validates each row before retaining it, skips members it does
not interpret, and admits retained rows against live heap headroom
rather than a fixed scan size. The first contradiction stops Kuna
instead of waiting for the rest; an incomplete document is judged only
after Kuna's own exit status. Diagnostics keep the 8 MiB Kuna budget,
now shared with the project export.

Refs #1957

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

* fix(kuna): admit string scan text as it streams and drop sinks with their command

Review found that packed string tokens were assembled whole before the
reader could discard them or check heap headroom, and that a stdout sink
could outlive a cancelled or timed-out command.

Move the token mechanics into `KunaJsonDocumentReader`: keys and numbers
(Kuna's own fields) stay packed, while strings, which carry target-derived
text, arrive in chunks. Ignored strings are dropped unassembled and kept
ones are admitted against live heap headroom as they grow. Kept text is
copied out of the decoded chunk it was sliced from, so retained rows no
longer pin whole chunks (XUL: 278 MB retained before, 156 MB after).
`KunaStringInventoryReader` keeps only the string-scan semantics.

`runOwnedCommand` now ties the stdout pipeline to the command's deadline
signal and aborts it when collection fails, destroying the sink.

Refs #1957

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

* fix(kuna): type the stream-json tokenizer where it is imported

The Reverse tools quality lane type-checks a partial project that reaches
KunaJsonStream without the separate streamJsonParser.d.ts augmentation,
so jsonParser had no declaration there. Declare the tokenizer's shape at
its only import site and remove the augmentation file, so every
compilation of the reader is self-contained. Split two helpers out of
the token dispatcher and simplify the stdout sink wait in
runOwnedCommand, which keeps the added complexity minimal.

Refs #1957

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

* fix(kuna): bound key and number assembly in streamed Kuna JSON

Review found that packed keys (and packed numbers) were assembled whole
by the tokenizer before the reader could discard or constrain them, so a
malformed scan with one enormous key could still exhaust the heap.

Stream every scalar. Keys are assembled only up to 64 code units, past
every field name the reader keeps, so a longer key is skipped unmatched;
kept numbers longer than that are rejected, and ignored numbers are
dropped unassembled. XUL's 611k-row scan parses in the same time.

Refs #1957

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

* fix(kuna): stream the call graph instead of capping it at 32 MiB (#4329)

* fix(kuna): stream the call graph instead of capping it at 32 MiB

Call relationships buffered `kuna decompile-graph` and refused output
above 32 MiB. A 53 MB Raycast binary produces a 157 MB graph (180k
functions, 773k edges), so every call query failed with a non-retryable
memory error after Kuna's minute-long whole-program walk.

`KunaCallGraphReader` validates the graph through the shared streaming
document reader. It retains only the facts queries use (126 MB for
Raycast) and checks edges against the complete node set once Kuna
exits. Unrequested bodies are refused before any of their text is
assembled, through new must-be-null row fields; address fields may be
null, as `forwardsTo` is. Malformed graphs are now `AnalysisOutputError`
like the string scan.

`KunaQueryWork.scan` owns the run-and-validate sequence both producers
shared: diagnostics budget, early rejection and retention of
deterministic overflow.

Refs #1957

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

* fix(kuna): index call-graph edges as they stream

Review found that the reader kept every parsed edge and built the edge
indexes only after streaming, outside any heap admission, so a large
graph could exhaust the heap instead of returning a memory constraint.

Index each edge when it arrives: duplicate pairs and callee orders are
checked per caller, call edges go straight into the caller/callee
indexes, and no edge array is kept, so the whole working set grows under
the per-row admission. Endpoints and order completeness are checked once
all nodes are known, and the validation-only indexes are released then.
Raycast's graph now peaks at 346 MB instead of 503 MB and retains 93 MB
instead of 126 MB, in the same time.

Also drop imports the shared scan made unused.

Refs #1957

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

* docs(kuna): state the graph's retained size after incremental indexing

Refs #1957

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

---------

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 21:53:59 -07:00
YsopwnableandKaoru0822 d9d085a15e feat(javascript): query retained application facts by exact path (#4265)
Expose exact-path, selected-property, paged reads of retained JavaScript application facts through the shared CLI/MCP analysis view. Preserve original identities, complete row Evidence and partial coverage; retain reproducible real-Joplin and actual-agent measurements, including negative cost results.

Fixes #4190. Part of #4027. Code mode remains deferred by #4091.

Integrate main after #4274. Validation: 114 distinct focused cases across documented invocations, 106 Python cases, full static/docs checks and 18 version-labelled real-application workflows with 9 complete CLI/MCP view parity controls. Preserve two local fixture deadline failures and the unchanged test passing on an available single CPU. Final-head CI passed all 36 jobs after one Windows timeout retry without source changes; both exact-head automated reviews are clear and all review threads resolved. Downloadable apps and raw large Evidence/transcripts are excluded.

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-16 12:46:53 +08:00
morluto 1b97b0f370 fix(lifecycle): preserve observations and cleanup ownership (#4274)
* fix(application): retain completed evidence through cancellation

Validate completed provider reports before classifying cancellation and preserve Evidence and captured diagnostics. Share MCP recording and delivery across custom observation handlers. Add source-owned completion and public SDK recording controls. Refs #3923, #3927, #4261.

* fix(wasm): retain final observations through cancellation

Keep completed Evidence after finalization and through CLI/MCP delivery. Preserve output and cleanup failure precedence. Refs #4254.

* fix(browser): retain final observations and forward cancellation

Classify final CDP detach and completed scenario cleanup at the provider boundary. Translate completed partials into canonical Evidence for MCP recording, and forward CLI cancellation to passive Electron tools. Refs #4255, #4256, #4257.

* fix(ghidra): isolate cancellation of shared startup waiters

Keep startup group-owned while each caller has an independent abortable wait. Await final-owner cleanup and preserve cleanup facts before retrying. Reuse the shared abortable promise helper. Refs #3983.

* fix(mcp): keep shutdown registrations until cleanup succeeds

Retain repeated event requests while unresolved owners need cleanup, and unregister only after success. Verify coalescing and failed-owner retry. Refs #4253.

* fix(adb): own transfer snapshots and stage screen publication

Bind transfer digests to the exact private input bytes and preserve caller APK names. Retain failed descriptors and private-root owners with typed cleanup evidence; publish captures exclusively after the cancellation cutoff. Refs #4251, #4252.

* fix(verification): report ADB cleanup failures

Always close after attempted verification and keep unexpected primary failures and cleanup details visible. Exercise the production script with controlled imports, including null and undefined throws. Refs #4260.

* fix(runner): stop owned Windows commands on cancellation

Use the retained ChildProcess handle for IPC cancellation and parent disconnection. Keep the SQLite lock until observed child close, and include native Windows regressions in the curated CI lane. Native Windows results remain pending. Refs #4258.

* fix(website): canonicalize link containment boundaries

Compare resolved targets with resolved repository and site roots. Accept directory aliases while preserving escape checks and keep fixtures within their temporary owner. Refs #4264.

* docs(testing): cover completion races and cleanup retry

Clarify validation-before-cancellation, independent startup waiter controls, producer and adapter coverage, and repeated shutdown events.

* fix(filesystem): retain file owners and completed reads after close failure

* fix(session): report retained snapshot staging in cleanup status

Fixes #4277. Include both snapshot and profile file-resource scopes in the owning status fact, preserving publication receipts and cleanup retry.

* docs(mcp): clarify recovery of raw partial observations

Document the observed SDK cancellation boundary and the open design decision in #4276 without promoting raw errors to Evidence.

* fix(kuna): own the complete installation inspection lifetime

* test(kuna): cover session cleanup of shared cache resources

* fix(hopper): isolate publisher operation resource admission

* fix(hopper): preserve launcher cleanup failure classification

* fix(adb): retain ordered cleanup and screen publication outcomes

* fix(runner): retain Windows job ownership through cleanup retries

* docs: clarify inspection admission and ordered cleanup regressions

* fix(ghidra): preserve startup cleanup and request deadlines

* fix(runner): supervise stdin errors and bootstrap native controls

* docs: require queued lifecycle controls to survive admission

* fix(cli): leave cleanup resolution with resource owners

* test(adb): verify owned install snapshots through CLI

* fix(errors): exclude resolved primary cleanup resources

* fix(adb): resolve publication cleanup after its handle closes

* fix(android): resolve JADX hash cleanup after owned close

* refactor(android): share hash failure cause options

* fix(kuna): gate commands on retained descriptor cleanup

* docs: cover cleanup admission and owner-confirmed recovery

* refactor(artifacts): bind cleanup errors to confirmed release

* fix(adb): resolve recovered cleanup at retained owners

* fix(kuna): resolve installation cleanup after owned close

* fix(kuna): gate cache reads on retained hash cleanup

* fix(setup): preserve publisher cleanup admission failures

* fix(android): resolve the reported retirement cleanup error

* test(runner): scope post-spawn fault control to POSIX

* test(adb): cover persistent snapshot owner retirement

* fix(kuna): preserve file outcomes with explicit cleanup ownership

* fix(artifacts): resolve errors through retained cleanup owners

* docs(lifecycle): carry cleanup ownership in typed outcomes

* fix(adb): bind staged screen errors to their cleanup lease

* fix(kuna): bind profile cleanup through a close capability

* fix(artifacts): retain descriptor resources in cleanup errors

* fix(pcap): bind existing errors to snapshot retirement

* fix(windows): load source owner through its file URL

* test(build): copy the command runner helper into fixtures

* fix(mcp): own shared observation providers in the transport

* fix(jadx): resolve every retained cleanup retry error

* fix(kuna): bind retry errors to every retained cleanup owner

* fix(adb): bind admission and shutdown errors to exact cleanup leases

* fix(windows): resolve command paths before native ownership

* test(publisher): assert exact descriptor recovery without release metadata

* docs(lifecycle): require every retry error to follow its owners

* fix(kuna): keep hash cleanup state stable across concurrent retries

* test(windows): exercise surviving descendants with scoped shutdown deadlines

* fix(uninstall): preserve primary read errors through cleanup failure

* test(publisher): assert cleanup Result and exact owner recovery

* test(windows): observe supervisor resources through IPC disconnect

* fix(browser): retain typed cancellation after interrupted actions

* test(publisher): assert ownership phases without retry-count coupling

* fix(lifecycle): preserve abort reasons through shared provider execution

* fix(jadx): preserve execution and late hash cancellation causes

* fix(adb): compose late cancellation with exact cleanup failures

* test(mcp): use the shared budget for session workflows
2026-10-16 11:48:59 +08:00
Ysopwnable 42fc6c53ae fix(javascript): preserve incoming callback target uncertainty (#4243)
Preserve incoming callback uncertainty and resolve proven preceding writes.
Share read-position and initializer identity facts with returned-reference
analysis; cache completed projected work with actual cycle dependencies.

Fixes #3880
2026-10-16 10:09:22 +08: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
4038 changed files with 789805 additions and 93443 deletions

No files matched your search

@@ -0,0 +1,113 @@
---
name: rea-analysis-optimization
description: Optimize REA providers and shared analysis workflows for agent context, latency, CPU, and memory while preserving correctness and Evidence. Use for analysis bottlenecks and cache or query design; CI/test-suite optimization belongs to rea-ci-tests-audit.
---
# REA analysis optimization
Find a concrete source of unnecessary work, change it at its owner, and verify
equivalent completed investigations. A useful outcome can also be a measured
decision to leave an implementation alone.
Follow the checkout's [performance priorities](../../../AGENTS.md#performance),
[tool design](../../../docs/tool-design.md), and
[testing guide](../../../docs/testing.md). This skill supplies an optimization
workflow, not additional permission to execute targets, publish, or merge.
Use the current contracts, provider prerequisites, and user authorization.
Read [the Kuna case study](references/kuna-case-study.md) when selecting a
reuse strategy or interpreting a benchmark. Its pinned observations illustrate
tradeoffs; they are not current-provider guarantees or universal speedups.
## Pin the question and the correctness oracle
Describe the analyst's completed task, including the facts and relationships
needed to answer it, requested detail, source access, and meaningful unknowns.
Choose a representative real artifact and a small or empty case. Record the
baseline revision, target digest, engine identity, options, host, and entrypoint.
Use a separate checkout for changes without disturbing other work.
Establish an oracle independent of the changed implementation: source-built
fixture expectations, independently mapped file bytes, or a native producer
paired with checks of its interpretation. Comparing two wrappers around the
same faulty decoder does not establish correctness. Preserve address widths,
same-address alternatives, unresolved endpoints, coverage, and source linkage.
An empty result proves absence only within the actually examined scope.
## Locate the earliest unnecessary work
Trace one public CLI or MCP task through shared application code to its producer.
Measure relevant phases: admission and identity checks, native analysis,
decoding, lookup, projection, serialization, Evidence retention, and cleanup.
Include catalog discovery and extra calls when they affect the completed task.
Use the evidence to choose the smallest structural correction:
| Observed cost | Candidate correction | Condition to establish |
| --------------------------------- | ---------------------------------------------------------- | ------------------------------------------------------------------------------------ |
| Repeated scans of validated facts | Reuse existing order or lookup structure | The owner establishes ordering and identity; every matching alternative survives |
| Repeated equivalent engine work | Share a validated result or in-flight producer | Inputs, freshness, lifetime, and per-caller outcomes remain explicit |
| Full reports for selected facts | Push selection or projection to the earliest capable owner | Required facts and evidence remain reachable; examined coverage is reported |
| Large temporary transport buffers | Decode incrementally or use bounded source reads | Validate the whole admitted document; retained facts and copies still consume memory |
| Many repeated homogeneous reads | Share admission, decoding, or batch work | Preserve item identity, order where required, and individual failures |
Do not introduce an index, generic cache, batch endpoint, or orchestration layer
without identifying the work it removes and its maintenance/resource cost.
Projection after materializing a full report may reduce output while leaving
analysis time and peak memory unchanged. Fix missing or incorrect facts before
benchmarking an alternative composition against a better-covered workflow.
## Keep one interpretation and a defined reuse lifetime
Let the producer boundary validate and normalize facts once. Consumers query
that representation through the shared CLI/MCP implementation; adapters should
not rebuild relationships or independently reinterpret raw reports. Keep lookup
identity separate from display formatting. Reuse contracts when their intent
matches, honoring requested detail and complete Evidence access.
For every cache or shared producer, state its key, owner, invalidation, retained
size, and lifetime. Account for result-affecting target bytes, engine contents,
analysis options, architecture/load mapping, and parser/schema versions where
they affect reuse. Validate live state before calling a cached observation
current. Cache a refusal only when it is deterministic for that validated key;
malformed, partial, transient, or cancelled attempts need explicit retry policy.
One cancelled waiter must not invalidate a successful peer. Preserve producer
failure context and each caller's cancellation reason. Retain ownership and
capacity charges until shared work and cleanup settle; preserve completed
observations when cleanup fails. Exercise invalidation, concurrent consumers,
last-waiter cancellation, close, and cleanup retry when those paths change.
Bound the process family and retained state, not just a parent heap or worker
count. Account for decoding and serialization expansion before allocation.
Choose CPU, memory, concurrency, and disk limits from the host and workload;
stop before OOM and verify owned cleanup. A guard-stopped task is incomplete.
Do not replace a transport buffer cap with an unbounded retained inventory or
hide partial results behind a success status.
## Compare completed tasks and limit the claim
Run the affected real public workflow before and after with equivalent inputs,
results, coverage, and Evidence semantics. Include small inputs so setup costs
and regressions are visible. Distinguish fresh sessions, warm runtime caches,
and OS file caches. Alternate or repeat runs when variance affects the decision.
Choose meaningful boundary regressions for cases the actual engine cannot
reliably reproduce; a fake engine does not qualify a real provider.
Report task outcome, relevant phase timings, total elapsed and CPU time, peak
process-family memory and its accounting method, resource stops, and cleanup.
Check the payload exit status as well as the profiler's. Keep failed attempts
and identify unmeasured costs; a passing retry does not explain a failure.
For token claims, count schemas/discovery, arguments, replies, retries, and
continuations needed to finish the task. Label tokenizer estimates separately
from actual model usage and record the client/catalog behavior. Smaller JSON
alone establishes neither fewer model tokens nor better investigation quality.
Do not claim a complex-software advantage without completed representative
investigations and comparable correctness/coverage.
Finish with the bottleneck, owning change, preserved semantics, executed
checks, before/after measurements, and remaining limits. Keep any integration
record current with its tested revision and distinguish merged production work
from experiment-only code. Stop when the demonstrated bottleneck is resolved
or further benefit remains speculative.
@@ -0,0 +1,4 @@
interface:
display_name: "REA analysis optimization"
short_description: "Optimize analysis with preserved Evidence and measured costs"
default_prompt: "Use $rea-analysis-optimization to find and verify a concrete analysis bottleneck while preserving correctness and Evidence."
@@ -0,0 +1,112 @@
# Kuna: reuse before adding machinery
Read this reference for concrete examples of the skill's decisions. The records
below concern static Linux analysis with caller-supplied Kuna. They do not
establish parity with another provider or a general advantage for Code Mode,
atomic tools, or any particular orchestration design.
## Exact-address lookup: a measured, narrow improvement
[PR #4695](https://github.com/morluto/rea/pull/4695) reused the validated string
inventory's numeric address order. Previously, each `list_strings(address)`
filtered all cached facts. The owner now performs a binary lower-bound lookup
and returns every encoding at that address: O(log n + matches), without a
second retained lookup index.
The owning implementation is
[`KunaStringInventory.ts`](../../../../src/kuna/KunaStringInventory.ts), consumed
by [`KunaStringQueries.ts`](../../../../src/kuna/KunaStringQueries.ts). Inspect
their current versions before adapting the example. Numeric u64 addresses,
same-address encodings, duplicate rejection, source digests, caller selection,
and the existing session lifetime were part of the contract. Full listings and
literal/regex searches kept their semantics. An attempted removal of the
duplicate-detection set was dropped: no stable memory benefit was established,
and it delayed malformed-input rejection.
### Recorded workload and results
The benchmark used unmodified Kuna 1.833 and LLVM 16 on Linux x86-64, with
602,544 string facts. Baseline was `be0b2d138`; the final feature was
`5f564962da0e1b17f00a68a74a312539f0209a01`, subsequently integrated in
`c6b62754b095a134d6d67877508fa34311a2eda2`. These are historical observations,
not a benchmark of every subsequent main revision.
The same production stdio MCP task discovered the catalog, opened the binary,
made one cold selected read and 28 warm exact reads, checked retained Evidence,
and closed the session. Five selected addresses and two absent selectors were
read four times. A native filtered scan selected the strings; an independent
ELF load-header mapping checked the source bytes and terminators. Returned
facts, source linkage, target digests, and cleanup were checked.
Three alternating fresh sessions per implementation used one CPU, a 4 GiB
process-family hard limit, a 3 GiB early stop, and no swap. OS file caches were
warm. Whole-harness costs include the independent native oracle, hashing,
catalog/client work, and correctness checks.
| Median | Before | After |
| ---------------------------------------- | ---------: | ---------: |
| 28 warm exact MCP reads | 495.2 ms | 90.8 ms |
| Cold selected MCP read | 11.30 s | 11.01 s |
| Whole harness elapsed | 28.24 s | 27.43 s |
| Whole process-family CPU | 27.71 s | 26.89 s |
| Kernel family peak, including file cache | 2237.6 MiB | 2240.6 MiB |
Warm totals ranged from 491.7–544.3 ms before to 84.8–99.5 ms after: about
5.5 times faster at this phase. Cold/whole-task timing and memory overlapped;
no stable improvement in those costs was established. One small SQLite pair
(7,985 facts) measured 20 warm reads at 88.6 → 80.5 ms and the whole harness at
13.68 → 13.94 s. The large-inventory lookup benefit does not generalize to a
whole-task speedup on small inputs.
Catalog hashes, call counts, and normalized facts were unchanged. A
`cl100k_base` estimate over one pass of raw catalog JSON, arguments, and replies
was about 846,000 tokens in both lanes, with about 802,562 from the catalog.
This was transport serialization, not rendered model context or actual usage;
client discovery, prompts, replay, and protocol framing were unmeasured.
**No token reduction or improved agent investigation outcome was established.**
Pinned target SHA-256:
`f62d254b7f2bf42df8c8b07d46ee3bb4c2cafeca436b2e6bc6ccbe4581f58f40`.
Pinned engine SHA-256:
`d858918b0766435cd96b999a77f2d5a31a0cc4f07256421c883b5b9dc4dd58e4`.
All six normalized-result sequence digests were
`4d2f90269da8568984090b20274163c4a7c2a0538aab63530bd2fe44840a0bba`.
Selected byte agreement does not prove native discovery completeness; missing
strings and relationships can remain unknown.
## Other transferable changes and their limits
- [Cached string inventory and search, #4095](https://github.com/morluto/rea/pull/4095):
share validated facts across queries in a defined session. Audit key inputs
and lifetime before extending reuse across targets or engine revisions.
- [Shared single-function work, #4045](https://github.com/morluto/rea/pull/4045)
and [repeat-request reuse, #4169](https://github.com/morluto/rea/pull/4169):
avoid duplicate native work while retaining per-caller cancellation and
cleanup ownership. A query count alone cannot reveal duplicate subprocesses.
- [Streamed strings, #4318](https://github.com/morluto/rea/pull/4318)
and [streamed call graph, #4329](https://github.com/morluto/rea/pull/4329):
remove fixed aggregate transport-buffer failures through incremental parsing.
Streaming transport does not bound every retained object or final reply.
These links establish changes; no numerical speedup is asserted here.
Typed reference facts, native string-query projection, and shared native byte
reads remained experimental at the 2026-10-17 checkpoint
`65f2f9a22bc892f61d2b00f9a4611124f57d4bff` on
`experiment/atomic-codemode-20261016`. Some experiments used a patched native
engine. Check current integration and bring-your-own engine capabilities before
proposing them; a working experiment is not a stock-provider guarantee.
## Applying the reasoning elsewhere
For another provider or format, inspect its actual representation and native
capabilities before borrowing the implementation. Existing numeric ordering
may support range reads; an on-disk native query may avoid materializing an
inventory; an immutable parse may support several views. Measure the relevant
alternative rather than requiring every provider to use Kuna's cached array.
For live observations, immutable artifact caching rules are insufficient:
validate the process/session identity and freshness required by the caller.
For compact replies, preserve access to source Evidence and report examined
coverage; projection must not silently turn an unresolved edge into no edge.
For batching, test mixed successes and failures and measure the entire task,
including discovery, extra calls, native work, and serialization.
@@ -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: "42"
version: "45"
---
# REA
@@ -30,6 +30,17 @@ unavailable, read [connection and recovery](references/connection-and-recovery.m
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.
If the host times out without a REA result, do not repeat the unchanged call or
assume work stopped. Read the recovery guide's deadline distinction before retrying.
If the MCP connection drops, retry that call once only when it is safe to
repeat or known not to have started. If it may already have changed the
target, or it fails again, tell the user and ask them to restart the MCP
client. If REA could not start its connection, retry once, then tell the user
and ask them to restart the MCP client. Do not retry a startup failure that
returns the same unchanged result. Run `rea doctor` when REA could not start
or tools stay missing after that restart. For `cleanup_incomplete`, a retryable
capacity limit, or `configuration_invalid`, follow the recovery guide's ladder
before asking for a restart. Read that guide before a second repair attempt.
1. Diagnose without changing REA or client configuration. `npx` may download
the package and write to npm's cache. For Codex, run
@@ -89,9 +100,19 @@ explicit path or endpoint and do not need it.
- ASAR or extracted JavaScript/Electron tree:
`analyze_javascript_application`.
- Archive/package member inventory (ZIP/APK/IPA/MSIX/AppX or DMG):
`open_binary` with the supplied local path; use `inspect_artifact` when its
graph and findings help answer the question.
- Archive/package member inventory (ZIP/APK/IPA/MSIX/AppX, DMG, or a PyInstaller
executable): `open_binary` with the supplied local path; use `inspect_artifact`
when its graph and findings help answer the question. A PyInstaller CArchive
is the application layer; the bootloader is only the native prefix. Start with
`detail: "summary"` for large inventories, then use retained inventory views
for selected member fields/pages. A complete small inventory can cost less
than extra calls and schemas. Runtime options are metadata, not files.
Compact delivery still inventories and hashes members. See the
[Python packaged applications guide](https://github.com/morluto/rea/blob/main/docs/python-packaged-applications.md).
- Apple app plist, Interface Builder, asset catalog, or keyed archive resources:
`open_bundle_resources`, then the selected resource tool. This does not
require `CFBundleExecutable` or a deep provider. Resource sessions have no
executable digest or binary snapshot; use `open_binary` for native code.
- Android APK code, classes, methods, or incoming references:
`inspect_android_package`, then focused Android tools when advertised.
Archive member inventory still uses the archive route above.
@@ -114,7 +135,10 @@ explicit path or endpoint and do not need it.
connect Everywhere's separate MCP server for live traffic.
- Retained HAR/SAZ/native mitmproxy/aioquic qlog capture: `inspect_web_network_capture`. Select
original record ordinals when useful; inspect producer fields and byte/number
sidecars without fetching recorded URLs or claiming live attribution. See
sidecars without fetching recorded URLs or claiming live attribution. HAR/SAZ/qlog
old-generation heap is `REA_CAPTURE_DECODER_OLD_GENERATION_MIB` (default 192 MiB,
floor 8, maximum 16384), set where REA starts. Host memory and `NODE_OPTIONS` do not
raise it. See
[historical captures](https://github.com/morluto/rea/blob/main/docs/web-network-captures.md) for the exact upstream
profile and credential exclusions. Qlog support starts with aioquic 1.5.0
JSON 0.3; stream coverage and endpoint loss declarations do not recover bodies
@@ -154,8 +178,18 @@ explicit path or endpoint and do not need it.
Select JavaScript glue and candidate paths explicitly and preserve URL/path
ambiguity. No implicit fetch or target execution; WAT is not original source.
See the [WASM guide](https://github.com/morluto/rea/blob/main/docs/wasm-artifacts.md).
- Explicit local Android APK (or ZIP/AAB) signing: `inspect_apk_signature`.
Prefer its summary or selected signer/digest/lineage page; retain complete
Evidence and use `inspect_analysis_view` kind `apk-signature` for follow-ups.
Reports which schemes are present (v1 JAR, v2, v3, v3.1), each signer's
certificate SHA-256 fingerprint (v1 included, from its PKCS#7 block), v3 key-rotation lineage, source stamp, and
whether each signed digest still matches the bytes (self-consistency). This
is identity and integrity, not a trust or authenticity verdict.
See the [APK signature guide](https://github.com/morluto/rea/blob/main/docs/apk-signature.md).
- Explicit local EVM bytecode carrier: `inspect_evm_interface` with caller-selected
`raw` or `hex` encoding. Preserve carrier/decoded digests and treat selectors,
`raw` or `hex` encoding. The owned worker deadline is `timeout_ms` (default
30000 ms, range 1..2147483647, next call). It does not raise the fixed byte,
address-space, or CPU allowances. Preserve carrier/decoded digests and treat selectors,
argument strings and mutability as inferred candidates; this performs no chain
lookup or target execution. For the complete inferred interface without hex or
duplicate stdout, discover support and select `detail: "summary"`; complete
@@ -169,14 +203,69 @@ 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 CUDA/HIP code objects: `inspect_gpu_code_objects`; inventory targets
first, then decode selected payloads and query retained GPU Evidence.
- Bundled GGUF or safetensors model declarations: `inspect_model_artifact`
with the explicit local file. Default header hashing leaves full-file identity
unknown; `hash: "full"` additionally reads all weights for SHA-256. MCP returns
retained summaries; optional `view` selects one page in the initial call. Use
`inspect_analysis_view` kind `model-artifact` for
batched metadata keys, tensor names or type totals, selected fields and pages.
Preserve header-scoped indices and array preview omissions; do not infer model
behavior from declared architecture or tensor types. See the
[model file guide](https://github.com/morluto/rea/blob/main/docs/model-artifacts.md).
- Unity IL2CPP names, member owners and string literals from `global-metadata.dat`:
`inspect_il2cpp_metadata` with the explicit local file; MCP defaults to compact
counts. Query sections, images, types, methods, fields, properties, events,
parameters or string_literals through `inspect_analysis_view`
with the returned `parent_evidence_id`, using exact filters, batch indices and
selected fields. Parameters accept `method_indices`, avoiding a scan of all
parameter pages. Missing collections in historical Evidence remain unknown.
Versions 24, 27, 29, 31 and 39; no Unity, IL2CPP
binary or disassembler is run. Unrecognized magic (possibly encrypted) and
other versions are unsupported. Optional matching `binary_path` enables static native addresses and type references; query `method_addresses` with indices or addresses/states, preserving every shared-body method and both artifact identities. Without a linked binary native counts remain unknown. Byte-pattern fallback is inferred; no runtime behavior or same-build pairing is proved. See the
[IL2CPP metadata guide](https://github.com/morluto/rea/blob/main/docs/il2cpp-metadata.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.
For selected crates, use `view: {collection: "packages", names: [...],
fields: ["name", "version", "source"]}` on the first call; explicitly include
`dependencies` for direct index references. Use `detail: "summary"` for counts
and ranges, or retained `inspect_analysis_view` Rust package/edge pages for
subsequent filters. Keep the complete parent Evidence accessible; CLI compact
delivery requires `--evidence_output`.
See the
[Rust binary guide](https://github.com/morluto/rea/blob/main/docs/rust-binaries.md).
- PE Authenticode certificate table: `inspect_pe_certificates` with the explicit
local PE32/PE32+ file. This built-in target-free inspection requires no
Windows SDK. Preserve WIN_CERTIFICATE ranges, types, payload SHA-256, and
PKCS#7 signer/X.509 identity fields, including `subject_key_identifier_hex`
when the signer uses that form. A missing security directory is observed
absence of that table, not proof the image is unsigned in every catalog sense.
Signatures are not verified. See the
[PE certificate guide](https://github.com/morluto/rea/blob/main/docs/pe-certificates.md).
- ELF note tables: `inspect_elf_notes` with the explicit local ELF32/ELF64
file. This built-in target-free inspection requires no disassembler. Preserve
PT_NOTE and SHT_NOTE ranges, owners, types, descriptor SHA-256, and GNU
build-id or ABI-tag fields. A missing table is observed absence of those
declarations, not proof the image never carried notes. See the
[ELF notes guide](https://github.com/morluto/rea/blob/main/docs/elf-notes.md).
- CPython 3.7–3.13 `.pyc` header, nested marshal code objects and bytecode
identity: `inspect_python_pyc` with the explicit local file. This built-in
target-free inspection requires no Python installation. Preserve magic,
PEP 552 hash or mtime/size, nested names, and bytecode SHA-256. Bytecode is
not decompiled, and a decoded header does not prove the matching `.py` is
present. See the
[CPython pyc guide](https://github.com/morluto/rea/blob/main/docs/python-pyc.md).
- Erlang BEAM IFF container: `inspect_erlang_beam` with the explicit local
`.beam` file. This built-in target-free inspection requires no Erlang
installation. Preserve chunk names, atom order, export/import/lambda tables,
and the Code header. Missing optional tables stay null. Bytecode is not
executed. See the
[Erlang BEAM guide](https://github.com/morluto/rea/blob/main/docs/erlang-beam.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
@@ -199,6 +288,8 @@ explicit path or endpoint and do not need it.
- PE resource type/name/language trees and static icon group references:
`inspect_pe_resources` with an explicit path. This portable operation requires
no active target or engine; opaque payloads retain file ranges and SHA-256.
- PE export directories and ELF dynamic-symbol exports: `inspect_native_exports`
with an explicit path; portable, no active target or engine.
- Native executable, library, or analysis database: `open_binary`, then
use focused analysis tools directly; call `binary_overview` when metadata or
inventory context is useful and available from the selected provider.
@@ -217,7 +308,12 @@ 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; it has no cross-references, callers or CFG. A cold first
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
@@ -242,6 +338,12 @@ Provider candidate facts describe possible providers, while top-level session
capabilities describe the current route and `analysis_provider_binding` names the
selected provider. Do not treat those scopes as interchangeable.
For a Ghidra startup timeout on a large native target, follow
[deadline recovery](references/native-and-artifacts.md#ghidra-startup-deadline-recovery)
before abandoning deep analysis or repeating the same cold import. The default
330 seconds is configurable; changing the server's environment requires a new
REA process.
## Work summary-first
Start with the default result and use its inline Evidence and graph context.
@@ -24,7 +24,10 @@ matching bundled native controls and uses an owned Job Object with bidirectional
stdio. A full JDK, including `jdk.compiler`, is required on every host; Windows
`JAVA_HOME` selects `bin\java.exe`. Consult the Android guide for actual host
verification coverage. Calls use one worker and provider deadlines; optional
`REA_JADX_HEAP_MIB` selects the JVM heap. The APK is never launched,
`REA_JADX_HEAP_MIB` selects the JVM heap and `REA_JADX_OPERATION_TIMEOUT_MS`
the per-operation deadline (integer milliseconds from 1 to 2147483647, default
120000; a change applies on the next process start; the MCP client's tool
timeout must also allow it). The APK is never launched,
and no emulator is required.
Manifest entries and decompiled text are static observations, not execution or
@@ -11,14 +11,15 @@ main and an older release expose the same arguments.
## Choose the remedy from the observed failure
| Observation | Meaning and next step |
| --------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| No REA tools in the active session | Inspect registration with `rea doctor --client codex --json`. A skill installation alone does not register MCP. |
| Missing, malformed or stale registration | Prepare a scoped setup plan; apply only authorized changes with backups. |
| Aligned registration, but no connected tools | Restart/reconnect the client. Doctor checks registration; it cannot establish the current agent's connection. Inspect the client's launch error if reconnection fails. |
| `MCP tool call requires approval, but approval policy is never` | Codex rejected the call before REA executed it. Do not install an engine or repeat setup to solve this client-policy decision. See the approval section below. |
| REA returns `provider_unavailable` or an unavailable tool entry | Read that operation's provider diagnostics or `binary_session.result.tool_availability`. Expand each group's tool names and inherit the shared client requirements unless overridden. Use the operation's availability, reason, and remediation; a complete tool catalog does not imply every operation is available. |
| `rea` is missing in a child shell, or Node is incompatible | Inspect the actual shell PATH and launcher/runtime. A prefix installation may be absent from a login shell's PATH; this does not establish a broken MCP registration. |
| Observation | Meaning and next step |
| --------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| No REA tools in the active session | Inspect registration with `rea doctor --client codex --json`. A skill installation alone does not register MCP. |
| Missing, malformed or stale registration | Prepare a scoped setup plan; apply only authorized changes with backups. |
| Aligned registration, but no connected tools | Restart/reconnect the client. Doctor checks registration; it cannot establish the current agent's connection. Inspect the client's launch error if reconnection fails. |
| MCP connection dropped or REA could not start | If the connection drops, retry that call once only when it is safe to repeat or known not to have started. If it may already have changed the target, or the same failure returns, tell the user REA's connection failed and ask them to restart the MCP client. If REA could not start its connection, retry once, then tell the user and ask them to restart the MCP client. Do not retry a startup failure that returns the same unchanged result. Run `rea doctor` when REA could not start or tools stay missing after restart. Doctor cannot restore a live connection by itself. |
| `MCP tool call requires approval, but approval policy is never` | Codex rejected the call before REA executed it. Do not install an engine or repeat setup to solve this client-policy decision. See the approval section below. |
| REA returns `provider_unavailable` or an unavailable tool entry | Read that operation's provider diagnostics or `binary_session.result.tool_availability`. Expand each group's tool names and inherit the shared client requirements unless overridden. Use the operation's availability, reason, and remediation; a complete tool catalog does not imply every operation is available. |
| `rea` is missing in a child shell, or Node is incompatible | Inspect the actual shell PATH and launcher/runtime. A prefix installation may be absent from a login shell's PATH; this does not establish a broken MCP registration. |
If the client cannot launch its shell/sandbox helper or read the installed skill,
inspect its launch diagnostic. For example, Codex can refuse helper aliases when
@@ -34,6 +35,83 @@ answers part of the question, and name the unavailable evidence separately.
Do not substitute another target. Label observations from ordinary shell tools
as such; only actual REA output supplies REA Evidence.
## Distinguish client and server deadlines
A host message such as `timed out awaiting tools/call` with no REA result means
the client's deadline expired before a result arrived. It does not establish
that REA finished, canceled successfully, or released all resources. Do not
repeat the unchanged analysis. Inspect available session/provider state and
retained Evidence only when useful; an idle `binary_session` is not proof that
every target-free operation stopped. Never invent a missing Evidence reference.
Allow the host tool deadline to cover execution, cancellation/cleanup, and
response delivery. In Codex, the setting is
`mcp_servers.<server-id>.tool_timeout_sec` in **seconds**, not
`startup_timeout_sec`. Select a value for the actual operation budget and
headroom, not a universal timeout. See the
[official MCP configuration reference](https://learn.chatgpt.com/docs/extend/mcp?surface=cli).
Apply configuration changes only within the authorized scope, preserving
unrelated entries and backups; reconnect/restart as required by the host.
When configuration is not editable, report the setting needed or use an
authorized CLI fallback. Do not silently broaden permissions or switch targets.
A returned REA `provider_timeout` instead means the **server/provider** reached
its deadline. Follow its `remediation.action`, reuse accessible partial Evidence,
and resolve reported incomplete cleanup before retrying. Change a request
field or a `REA_*` setting only when the returned remediation explicitly
recommends that change. Restart REA when that text says the value is read at
startup. A mention that a control does not raise the deadline is not a
recommendation to change it. Do not invent a setting the remediation does not
name. Raising only the client deadline does not change
this server budget. For static JavaScript,
`analysis_timeout_ms` (CLI `rea analyze-javascript-application <path>
--analysis-timeout-ms <milliseconds>`) controls each source's
startup, analysis and transfer deadline in **milliseconds**. Increase that
budget or reduce semantic work / select exact inventoried `source_paths` when
the task permits. Omit `source_paths` for all sources; `[]` is invalid.
`detail: "summary"` reduces delivery, not analysis work. A per-source budget
does not bound the elapsed time for an entire multi-source application.
Partial-Evidence inspection can avoid recomputation; an unavailable reader is
not itself a reason to block a corrected static-analysis retry.
The shorthand `rea analyze <path>` does not expose this timeout flag; use the
explicit JavaScript command when changing that budget.
For invalid arguments, use the connected tool's current input schema, or the
installed CLI's command help. Refresh the catalog if stale; do not guess fields
from an older skill or return the entire catalog to repair one field. Correct
all reported fields before retrying.
## Recovery ladder
Follow the returned remediation. Change a request field or a `REA_*` setting
only when that text explicitly recommends the change. Restart REA when it says
the value is read at startup. A mention that a control does not raise a limit
is not a recommendation to change it. Do not invent a setting. Host memory and
`NODE_OPTIONS` do not raise a REA admission unless that remediation says so.
- MCP connection dropped: retry that call once only when it is safe to repeat
or known not to have started. If it may already have changed the target, or
it fails again, tell the user REA's connection failed and ask them to restart
the MCP client. Do not run `rea doctor` for the drop itself.
- REA could not start its connection: retry once, then tell the user and ask
them to restart the MCP client. Run `rea doctor` when REA could not start or
tools stay missing after that restart. Do not retry a startup failure that
returns the same unchanged result.
- `cleanup_incomplete`, or retained cleanup owners in the error details: retry
that exact cleanup owner. Do not start new work that needs those resources
until that owner confirms release. Repeating the original call is not that
retry.
- Capacity, when the result is `resource_constraint` and `retryable` is true:
wait for the active admission to finish, then retry within the reported
limits. Do not restart REA to free the slot.
- `configuration_invalid`: correct the named setting in the environment that
starts REA (the shell or the MCP registration), then restart REA. Run
`rea doctor` to see the `runtime-configuration:` check. Re-run `rea setup`
for that client when the registration must carry the new setting, then
reconnect.
- `invalid_request`: correct the reported fields from the current schema. Do
not repeat the unchanged call.
## Repair registration when needed
For Codex:
@@ -24,6 +24,17 @@ retain its complete source and receive that view immediately. Without that
advertised choice, keep the existing complete call. CLI inspection returns
portable complete Evidence; save it before projecting a selected inline view.
For native procedure/name/string inventories, select `detail: "summary"` when
the connected tool advertises it and the task needs counts or selected rows.
Use its parent Evidence ID with
`view: {kind: "native-inventory", offset: 0, limit: 32, fields: ["value"]}`.
Search tools retain the existing pattern/mode/case/document selection. Omit
`fields` for complete records; `record.address` and `source_index` remain on
selected rows. Selecting `value` keeps its string/decoding qualifications.
Parent Evidence ID plus source index preserves same-address
alternatives. Follow `coverage.next_offset` only as far as the question needs;
exhausting retained rows does not establish complete program discovery.
Use `record_unknown` to track unresolved questions in the user's investigation
and name the authority or environment still required. Supply supporting and
contradicting evidence IDs. It updates the session registry directly.
@@ -69,3 +80,27 @@ 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.
Use `inspect_analysis_view` with `view: {kind: "environment-manifest"}` on the
same retained HAR Evidence to project the primary origin's request lines into
named tools with path templates, parameters, observed query names, value-free
body shapes and record pointers for an agent environment. Opt into
`template_rules` only when recorded identifiers need them; every template,
parameter and merged sibling is an inference, no header, cookie or body value is
copied, and the three stated unknowns stay unresolved.
Use `view: {kind: "environment-state"}` with the manifest's `template_rules` to
extract the data those reads observed instead: entity types and identity
fields, entities with recorded values, documents, and per-tool render plans
checked against the recordings. The state comes only from reads before the
first related write; types, identity and list membership are inferences. The
view also gives each state-changing tool a write plan, served only when a later
recorded read confirms its effect and none contradicts it.
`rea environment-serve manifest.json --evidence capture.json` derives that
state at startup, answers read tools from it, serves the write tools whose plan
is served, each applying its effect to that state, and serves `data/reset`,
`data/add` and `data/get` on `/agentenv`; other write tools are not served, and
each plan states why in its `reason`. It replays the capture instead with
`--replay-only` or when the state is unavailable (not derivable, or a catalog
over the discovery budget), and its stopped document's `state` says which
applied.
@@ -18,9 +18,15 @@ only selected files. The result and summary record `source_selection`; files
outside scope and their relationships remain unknown, so partial application
coverage does not mean the selected source failed. Use repeated `--source-path`
on either static application CLI route. Omit the selection when the question
requires complete application analysis. `detail: "summary"` reduces delivery
requires complete application analysis; `source_paths: []` is invalid, not an
unfiltered request. `detail: "summary"` reduces delivery
and does not by itself reduce analysis work.
ASAR header/index admission defaults to 16 MiB / 16777216 bytes;
`REA_ASAR_MAX_HEADER_BYTES` (at most 256 MiB, set where REA starts) changes it.
`source_paths`, `detail`, and `max_heap_mb` do not bypass or raise it. Or
analyze an extracted directory.
Exact `#alias` specifiers resolve through the importer's
enclosing package `imports` map to package-local files; nested `node_modules`
packages use their own map. Wildcard, recursive, and external-package import
@@ -35,7 +41,9 @@ the complete application Evidence inline when it fits.
When the connected schema advertises `max_heap_mb` and `analysis_timeout_ms`,
these select the isolated source analyzer's old-space heap and per-source
deadline. Defaults are 1024 MiB and 300000 ms. They do not change the MCP
process heap. A Bun-hosted REA does not enforce the heap choice; on Windows it
process heap or the client's tool deadline. For a host timeout without REA
output, use the [deadline recovery guide](connection-and-recovery.md#distinguish-client-and-server-deadlines)
instead of raising the server budget. A Bun-hosted REA does not enforce the heap choice; on Windows it
instead limits the analyzer's committed memory to that choice plus a 512 MiB
runtime reserve.
Resource-limited analyses preserve completed static facts and
@@ -51,6 +59,20 @@ in the same session. A returned `error.details.partial_observation` reference
can be supplied directly to `inspect_analysis_view`. If the SDK cancels locally, retrieve its recorded ID from
the Evidence bundle before inspecting the partial analysis.
## Inventory route candidates without returning the application graph
When the advertised schema supports `view`, use `detail: "summary"` with
`view: {kind: "javascript-routes", collection: "routes", fields: ["path", "confidence"], limit: 20}`.
The same view works with `analyze_web_bundle` on already selected captured sources.
The complete source Evidence is retained; use its `parent_evidence_id` with
`inspect_analysis_view` for `summary`, `routes`, `occurrences`, `sources`, or
`unknowns`. Filter and batch IDs before requesting more fields, and follow
`coverage.next_offset`. CLI selections require `--evidence_output` to keep the
complete record. Static patterns, inferred deployment bases, heuristic system
classification, and unavailable sources never establish route reachability or
the complete application route denominator. See the
[route inventory guide](https://github.com/morluto/rea/blob/main/docs/javascript-route-inventory.md).
## Read a module without repeating analysis
Use the actual returned parent ID, on the same connection, as `source.evidence_id`.
@@ -94,6 +116,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
@@ -165,7 +217,8 @@ accept complete inline Evidence or
`{"kind":"retained-evidence","evidence_id":"ev_<64 lowercase hex characters>"}`
for their application input (`application`, or `left`/`right`). `inspect_analysis_view`
uses the same retained-reference form in `source`, or portable inline Evidence,
to project a summary, module page, or one module with its recorded observations. This notation is
to project a summary, module page, or one module with its recorded observations.
This notation is
a template: replace it with the actual returned ID. Versions before 4.1.0 accept only full
inline Evidence. Use the exact ID
returned by the producer on the same MCP connection. Resolution does not run
@@ -173,3 +226,15 @@ analysis or select a provider; findings remain inline. Native observation
arrays still take full Evidence. `close_binary` clears retained references;
export a bundle before closing, or supply portable inline Evidence in another
connection. A missing reference includes its ID and recovery guidance.
For exact file/native metadata or processing gaps, use
`view: {kind: "javascript-facts", where: {paths: ["package.json"]}, property_keys: ["name", "operation", "analysis_status"], limit: 32}`.
Rows retain canonical node/observation identity and original Evidence. Paths
match Evidence artifact-path/source-range locations; missing properties remain
omitted and source coverage stays separate from page exhaustion. Batch paths and
select only needed property keys; complete Evidence remains accessible by export.
If names are unknown, opt into `include_available_property_keys: true`; it lists
the original keys per returned observation without unselected values. Keep each
row's node/observation identity paired when composing facts.
These rows select application-graph observations; semantic relationships and
semantic unknown records remain in the complete parent Evidence and trace tools.
@@ -1,8 +1,52 @@
# Native, managed, and packaged artifacts
For release comparisons, export `list_procedures` Evidence from each target before
closing it. Close clears session records: import both bundles after collecting the
two targets, or supply portable inline Evidence. `match_native_functions` pairs unique exact names and, when supplied,
complete observed body bytes. Reuse optional same-context `list_names`,
`procedure_info`, `read_bytes` and existing `analyze_function` Evidence; avoid
decompiling every function merely to match it. Its summary retains a parent for
`inspect_analysis_view` `function-matches` collections `matches` and `functions`.
Select columns, batch IDs and page individual facts; aliases remain unresolved.
Unpaired observations do not prove additions/removals. Existing matched dossier
pairs include inputs for `compare_functions`; otherwise analyze just chosen pairs.
## Apple app resources
Use `open_bundle_resources` with the selected app path, then `inspect_plist`,
`decode_interface_builder`, `inspect_asset_catalog`, or `inspect_keyed_archive`.
The resource session reads the app without resolving `CFBundleExecutable` or
starting a deep provider. Preserve `resource_root` provenance, per-resource
hashes where reported, and null executable identity; a directory path is not
an executable digest. Export retained Evidence before closing or switching
roots. Binary analysis snapshots do not apply. Use `open_binary` to switch to
executable analysis, and `close_binary` to release either session kind.
The four matching CLI commands accept the app path directly. For an archive
inside the app, use `inspect-keyed-archive APP_PATH RELATIVE_ARCHIVE_PATH`.
Host requirements for plist and compiled asset/Interface Builder tooling still
apply; a resource-only session does not establish provider availability.
## Desktop installation directories
When an installation directory is supplied and its runtime is unknown, use
`inspect_desktop_application` with its absolute path. Default MCP summary
delivery retains complete Evidence. Read `files`, `omissions`, `candidates`, `components` or
`next_probes` with `inspect_analysis_view` kind `desktop-application`, selecting
exact paths, batched file/omission IDs or families and only needed fields and pages.
Summary `probe_issues` retains counts and bounded previews. Select files with
`has_issues: true` and fields `path`, `read_status`, `issues` to retrieve exact
failures directly; preview text is display data, while `file_id` selects full facts. Preserve source
coverage, read-range identity and unknowns; directory snapshot identity is not
a digest of every file's contents. Component path markers do not establish
runtime presence. For compact CLI output, use
`inspect-desktop-application PATH --detail summary --evidence_output BUNDLE`.
See [desktop installations](https://github.com/morluto/rea/blob/main/docs/desktop-applications.md).
## Go build metadata
Use `inspect_go_binary` for an explicit local ELF, PE or thin Mach-O when the
Use `inspect_go_binary` for an explicit local ELF, PE or Mach-O (universal
files need `architecture`) when the
question concerns the embedded compiler version, module/dependency versions,
local replacements or build settings. No `open_binary`, Go installation or
target execution is required. The CLI equivalent is `inspect-go-binary PATH`.
@@ -11,9 +55,38 @@ 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.
## Model files
Use `inspect_model_artifact` (`inspect-model-artifact PATH`) for local GGUF v2/v3
and safetensors declarations without a model runtime. Header-only mode does not
read weights or establish full-file identity. `hash: "full"` explicitly streams
all bytes. Retained `model-artifact` views page `metadata`, `tensors` and derived
`types`, with exact batch filters and selected fields. Arrays retain typed counts
and eight-element previews; omitted values, unknown types and runtime behavior
stay explicit. See [model files](https://github.com/morluto/rea/blob/main/docs/model-artifacts.md).
## IL2CPP metadata
Use `inspect_il2cpp_metadata` for an explicit local Unity `global-metadata.dat`
(versions 24, 27, 29, 31 and 39) when the question concerns assembly images,
names, member owners, string literals, namespaces or index ranges. No `open_binary`, Unity installation or target
execution is required. The CLI equivalent is `inspect-il2cpp-metadata PATH`.
MCP defaults to counts with complete retained Evidence. Query `sections`,
`images`, `types`, `methods`, `fields`, `properties`, `events`, `parameters` or
`string_literals` through `inspect_analysis_view`; use exact names,
batch indices and selected fields. Row indices are scoped by artifact SHA-256
and collection; page offsets count filtered matches. Parameters support batch
`method_indices` to read one method's parameters directly. Historical reports
without member collections retain unknown counts rather than empty inventories. Preserve
artifact SHA-256 and index widths. Unrecognized magic may mean encryption and
is unsupported, not decrypted. Linking names to `GameAssembly` or
`UnityFramework` code addresses remains a separate native-analysis question.
See [IL2CPP metadata](https://github.com/morluto/rea/blob/main/docs/il2cpp-metadata.md).
## Rust dependency metadata
Use `inspect_rust_binary` for an explicit local ELF, PE, or thin Mach-O when
Use `inspect_rust_binary` for an explicit local ELF, PE, or Mach-O (universal
files need `architecture`) 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`.
@@ -23,6 +96,51 @@ 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).
## PE Authenticode certificate tables
Use `inspect_pe_certificates` for an explicit local PE32/PE32+ image when the
question concerns the Authenticode certificate table, WIN_CERTIFICATE types, or
embedded PKCS#7 signer and X.509 identity fields. No `open_binary`, Windows SDK,
or target execution is required. The CLI equivalent is
`inspect-pe-certificates PATH`. Preserve artifact SHA-256, table file ranges,
payload hashes, original validity-time spellings, and
`subject_key_identifier_hex` when the signer uses that form. A missing
`IMAGE_DIRECTORY_ENTRY_SECURITY` directory is observed absence of that table,
not proof the image is unsigned in every catalog or overlay sense. Signatures,
chains, timestamps, and Authenticode digest coverage remain unobserved. See
[PE Authenticode certificate tables](https://github.com/morluto/rea/blob/main/docs/pe-certificates.md).
## ELF note tables
Use `inspect_elf_notes` for an explicit local ELF32/ELF64 image when the
question concerns PT_NOTE/SHT_NOTE owners, GNU build-id, or GNU ABI-tag
fields. No `open_binary` or target execution is required. The CLI equivalent
is `inspect-elf-notes PATH`. Preserve artifact SHA-256, table file ranges,
descriptor hashes, and original owner spellings. A missing table is observed
absence of those declarations, not proof the image never carried notes in
another container. See
[ELF note tables](https://github.com/morluto/rea/blob/main/docs/elf-notes.md).
## CPython pyc files
Use `inspect_python_pyc` for an explicit local CPython 3.7–3.13 `.pyc` when the
question concerns the header magic, PEP 552 source hash or timestamp, nested
code-object names, or bytecode identity. No `open_binary`, Python installation,
or target execution is required. The CLI equivalent is `inspect-python-pyc PATH`.
Preserve artifact SHA-256, the 16-byte header fields, nested code names, and
bytecode SHA-256. Bytecode is not decompiled. A decoded header does not prove
the matching `.py` is present. See [CPython pyc inspection](https://github.com/morluto/rea/blob/main/docs/python-pyc.md).
## Erlang BEAM files
Use `inspect_erlang_beam` for an explicit local `FOR1`/`BEAM` file when the
question concerns IFF chunks, atom names, exports, imports, lambdas, or the
Code header. No `open_binary`, Erlang installation, or bytecode execution is
required. The CLI equivalent is `inspect-erlang-beam PATH`. Preserve artifact
SHA-256, chunk file ranges, 1-based atom indices, and original atom spellings.
Missing optional tables stay null. See
[Erlang BEAM files](https://github.com/morluto/rea/blob/main/docs/erlang-beam.md).
## SQLite snapshots
Use `inspect_sqlite_database` with an explicit local database snapshot for its
@@ -77,6 +195,30 @@ and tool profile. Text offsets count UTF-16 code units; follow `next_offset`.
The complete source remains retained after an oversized MCP reply. No target
execution or network acquisition is implied, and WAT is decoded representation.
## Offline APK signatures
Use `inspect_apk_signature` for a selected local APK/ZIP/AAB. Default MCP summaries
retain complete Evidence. Select `view.collection: signers/digests/lineage`,
with scheme or ID batches, fields and paging; use `inspect_analysis_view` kind
`apk-signature` for retained reads after deletion. IDs retain artifact/scheme
scope and signer links; `fields: []` keeps identity links. Empty pages preserve
partial decoding reasons and unknown authenticity. CLI compact delivery needs
`--evidence_output` to preserve the complete parent.
It reads the APK
Signing Block (v2/v3/v3.1) and v1 `META-INF` signing without executing anything,
and reports each signer's certificate facts (for v1, the certificate the PKCS#7
block's SignerInfo names), the declared v3 rotation lineage,
the declared source-stamp certificate and, for SHA-256/SHA-512 digests, whether
the declared content digest matches the current bytes. That match is not a
signature verification: anyone who changes the bytes can recompute the digest.
`posture` summarises v1-only signing (which cannot tell a v1-only APK from one
whose v2/v3 block was removed), observed signer agreement across schemes,
whether the v1 signer equals the block-scheme signer (`false` is notable),
whether every differing signer appears in a declared (unverified) rotation
lineage, and the strongest scheme.
It never claims authenticity: no signature or lineage is cryptographically
verified.
## Native targets
After `open_binary`, use focused search, procedure, or function tools directly.
@@ -124,6 +266,85 @@ operation's full ordered list. Candidate capabilities describe possible
providers. Top-level session capabilities describe the current route, and
`analysis_provider_binding` identifies the selected provider.
## Ghidra startup deadline recovery
`provider_timeout` during the first Ghidra query can cover import, auto-analysis,
bridge startup, and readiness. The default 330,000 ms is configurable through
`REA_GHIDRA_STARTUP_TIMEOUT_MS`; a smaller query still needs the same cold import.
Preserve useful Evidence before closing the session or restarting REA. Check
`close_binary` and resolve reported cleanup failures. Set a larger startup budget
in the environment that launches REA, then restart REA and reconnect the MCP
client before reopening the same target. Closing and reopening a binary in the
existing server keeps its original configuration and starts import again.
Environment changes in an agent's shell do not update an already running MCP
server. If its launch configuration cannot be changed, report that constraint
and the exact retry setting instead of claiming a fixed technical limit.
The client's tool-call deadline must allow startup plus the requested query;
server settings and progress notifications cannot override it. CLI analysis
avoids the MCP client deadline, but retains the provider deadline. For example:
```sh
REA_GHIDRA_STARTUP_TIMEOUT_MS=1800000 rea analyze /absolute/path/to/app.exe --provider ghidra --json
```
Thirty minutes is an example budget, not a completion guarantee. Keep host CPU
and memory controls in place. Disable specific expensive analyzers only when
their missing coverage is acceptable and retain the resulting limitations.
Exported C and embedded resources remain useful independent evidence, but a
timeout does not establish complete analysis or absence of code. See the
[deadline and client guide](https://github.com/morluto/rea/blob/main/docs/mcp-contracts.md#ghidra-first-query-deadlines-and-recovery).
## Bridge request ceiling
Hopper and Cutter share `REA_BRIDGE_MAX_REQUEST_BYTES`. Unset, both sides use
16777216 bytes. The payload budget excludes the terminating LF. A decimal
integer from 7464 to 536870887 raises or lowers that ceiling. The floor fits
the largest bounded symbol-name command. A tight Cutter status still returns
its session id and up to 64 completion receipts. Its path is included when it
fits, and is unknown when it does not, so an older descriptor path is not
paired with the new generation. The maximum is the encoded payload, one byte under Node's string ceiling so
the framing LF still fits. Cutter measures, signs, and writes a fitting
response in bounded chunks. The bridge searches only the newly read bytes
for the framing LF. The client unescapes each socket chunk into one response
string before parsing it, so this ceiling does not keep the paged wire frame
and another concatenated copy. Invalid values are configuration errors and do not fall back to 16777216. Set the variable in the environment that starts REA, restart REA,
and start Hopper or Cutter from that same environment. An out-of-band bridge
without the setting stays at 16777216 bytes. The client reports that mismatch
during discovery only after the hello proves a live authenticated bridge, and
does not send a status request or the command. A stale descriptor is ignored.
Discovery fails if any such live ceiling differs, even when another live
session matches. A command response discarded for that ceiling stays an
unknown execution with `output_truncated`. A hello
that never sends its authenticated frame does not hold the accept loop.
At most 8 connection threads run; a further accept is closed, and a failure
to start a thread does not stop the listener. `completed_connection` is the
longest prefix of connections that have terminated. Dense scalar replies are
encoded in bounded batches.
The 25000 ms Cutter request deadline and the 256-candidate discovery budget
still have no runtime override.
## Static memory and original file bytes
For Ghidra, `read_bytes` reads initialized analysis memory, including loader
fixups; it does not read a running process. Check `returned_bytes` and `complete`
before decoding a table. An incomplete read is missing coverage, not a zero-filled
tail. Use `list_segments` to inspect the selected block's initialization and address-space
facts, and retain unavailable facts as unknown.
Use `address_to_file_offset` to anchor one observed address to original file
bytes. A mapping for the first address does not establish a contiguous mapping
for an entire table, and loader fixups can make analysis bytes differ from source
bytes. Keep source ranges and analysis-memory observations separate.
A pointer table can refer to writable slots populated by a constructor rather
than directly to immutable resources. An uninitialized block or an observed zero
does not establish the slot's later runtime value. Follow `xrefs` and the relevant
writer's assembly or pseudocode; distinguish recorded assignments from inferred
execution order. Do not replace unreadable bytes with zeros or conclude that a
resource is absent without the necessary initialization evidence.
## DOS MZ with Ghidra
For an admitted DOS MZ target, use `inspect_native_load_image` to check measured
@@ -133,10 +354,8 @@ 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
use inclusive ends and can contain gaps. Do not treat the enclosing span as code.
Complete function body ranges use inclusive ends and can contain gaps. Do not
treat the enclosing span as code.
For packed targets, retain the original and separately derived artifact identities;
decompiling an unpacking stub does not recover the unpacked program.
@@ -226,8 +445,8 @@ every executable in the bundle. It also lists missing or weak loads and earlier
`architecture`. System paths stay undetermined because the dyld shared cache
provides them.
Use `extract_artifact` when materialized files are needed. It extracts the active
target and accepts optional `integrity_policy`; see the [MCP contracts](https://github.com/morluto/rea/blob/main/docs/mcp-contracts.md).
Use `extract_artifact` when materialized files from a supported format are needed.
It extracts the active target and accepts optional `integrity_policy`; see the [MCP contracts](https://github.com/morluto/rea/blob/main/docs/mcp-contracts.md).
It materializes all regular files into a fresh temporary directory chosen by
REA. Symlinks and encrypted entries are inventory facts, not extractable files.
The result reports the materialized directory; callers do not choose the
@@ -236,23 +455,30 @@ destination. No separate permission grant is needed.
Native DMG traversal automatically uses a read-only mount on supported macOS
hosts, with an owned temporary mount directory and cleanup. It needs no approval
flag. Unsupported hosts or mount failures remain explicit limitations; do not
claim child inventory when only root identity is available. Extraction is a
separate filesystem-writing operation.
claim child inventory when only root identity is available. DMG extraction is
unavailable through `extract_artifact`. To access DMG files on macOS, create a
read-only mount with your own tools, select files from that mount for analysis,
and detach the mount you created when finished. REA's inspection mount is
temporary, not a retained export of the image's contents.
For headerless DOS COM, explicitly open with `format: "dos-com"` (CLI
`--target-format dos-com`). Inspect the load image before trusting function
analysis: the entry is `0x10100`, file offset zero, with imposed real-mode segment
context. PSP/stack/device state remains unmodeled. See [DOS guide](https://github.com/morluto/rea/blob/main/docs/ghidra-dos.md).
Use `inspect_byte_entropy` to triage high- and low-entropy ranges before extraction.
For other headerless images (embedded firmware without ELF/Mach-O/PE/MZ
headers), open with `format: "raw-binary"`, `architecture` (`x86`, `x86_64`,
`arm`, `arm64`, or `6502`), and `entry_point` (`0x` hex). File offset 0 is mapped at
`arm`, `arm64`, `6502`, or `m68k`), 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. `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).
`BinaryLoader` with a generic language: big-endian for `m68k`, little-endian
otherwise. Big-endian ARM, Thumb-only, 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. `m68k` requires an even entry and decodes the 68040
superset of the 68000 family; Amiga/Atari containers and vector-table entry
discovery 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
@@ -270,8 +496,37 @@ 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).
## Offline PE and ELF export tables
Use `inspect_native_exports` with an absolute `path`, or
`rea inspect-native-exports ./libexample.so --json`, to read a PE export
directory or ELF dynamic symbol table on any host without opening a native
target. Ordinal-only, forwarded and hidden-version exports stay distinct;
partial or unsupported coverage means the inventory is incomplete, not that
exports are absent. Use `inspect_macho` for Mach-O. See
[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`.
## Godot PCK
For a standalone PCK, `open_binary` then `inspect_artifact` with
`detail: "summary"` retains the complete static inventory. Use the returned
`parent_evidence_id` with `inspect_analysis_view` inventory occurrences filtered
by exact `logical_paths` or `occurrence_ids`, and join `artifact_id` to nodes
filtered by `artifact_ids`. Select only needed fields; identity links remain.
Prefer `inspect-artifact --detail summary --evidence_output PATH` for CLI tasks;
the same CLI discovers embedded packs via their EOF trailer without opening a
native analysis provider. Normal native opening remains unchanged.
Distinguish declared MD5 from observed SHA-256 and graph/page completeness from
`source_coverage`. Encrypted directories and sparse bundles have unknown entry
coverage; encrypted members have unknown content identity. An empty filtered
page cannot prove absence in unknown coverage. Retained views are historical,
not fresh filesystem checks. No decryption, Godot resource decoding, GDScript
decompilation or target execution is provided. See
[Godot PCK](https://github.com/morluto/rea/blob/main/docs/godot-pck.md).
@@ -69,6 +69,12 @@ with `inspect_analysis_view` and `view: {kind: "capture", detail: "preview", off
Select complete record pages only for needed headers, metadata or payloads; views
read the historical parent without repeating capture decoding.
For selected protobuf/gRPC bodies, keep raw bytes as the source and use
`inspect_analysis_view` kind `protobuf`, collection `bodies`, `frames` or
`fields`. Batch exact record ordinals and field numbers/IDs, and select
`schema_value` or `text` only when needed. Supply a local descriptor set and
message type for schema interpretation; compressed payloads remain unknown.
Inspect native `.saz` snapshots with `inspect_web_network_capture` and
`format: "saz"`; encrypted archives are unsupported. For live
traffic analysis, connect Everywhere's separate MCP server and use its
+13 -3
View File
@@ -1,5 +1,10 @@
name: Cache npm package downloads
description: Restore npm's download cache and save it only from trusted default-branch events.
description: Restore npm's download cache. Trusted default-branch jobs that opt in may save it.
inputs:
save:
description: Save the shared download cache when this event is allowed to write it.
required: false
default: "false"
outputs:
can-save:
description: Whether this run may update the shared npm download cache.
@@ -23,20 +28,25 @@ runs:
can_save=true
fi
cache_key="npm-download-${RUNNER_OS}-${runtime_arch}-${LOCKFILES_HASH}"
# Same platform only. A lockfile change can reuse tarballs from the previous key.
restore_key="npm-download-${RUNNER_OS}-${runtime_arch}-"
{
printf 'can-save=%s\n' "${can_save}"
printf 'path=%s\n' "${cache_path}"
printf 'key=%s\n' "${cache_key}"
printf 'restore-key=%s\n' "${restore_key}"
} >> "${GITHUB_OUTPUT}"
- name: Restore and save npm package downloads
if: steps.context.outputs.can-save == 'true'
if: steps.context.outputs.can-save == 'true' && inputs.save == 'true'
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: ${{ steps.context.outputs.path }}
key: ${{ steps.context.outputs.key }}
restore-keys: ${{ steps.context.outputs.restore-key }}
- name: Restore npm package downloads without saving
if: steps.context.outputs.can-save != 'true'
if: steps.context.outputs.can-save != 'true' || inputs.save != 'true'
uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: ${{ steps.context.outputs.path }}
key: ${{ steps.context.outputs.key }}
restore-keys: ${{ steps.context.outputs.restore-key }}
@@ -24,19 +24,7 @@ runs:
BUILD_CACHE_PREFIX: rea-portable-build-v2-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('.npmrc', '.nvmrc', 'package-lock.json') }}
run: |
npm run --silent test:prepare -- --dry=json |
node --input-type=module -e '
import { createHash } from "node:crypto";
import { readFileSync } from "node:fs";
const { tasks } = JSON.parse(readFileSync(0, "utf8"));
const hashes = tasks.map(({ taskId, hash }) => {
if (typeof hash !== "string")
throw new Error(`Build task ${taskId} must have an eager Turbo hash`);
return `${taskId}:${hash}`;
}).sort();
const hash = createHash("sha256").update(hashes.join("\n")).digest("hex");
console.log(`key=${process.env.BUILD_CACHE_PREFIX}-${hash}`);
console.log(`prefix=${process.env.BUILD_CACHE_PREFIX}-`);
' >> "$GITHUB_OUTPUT"
node scripts/ci/portable-build-cache.mjs key >> "$GITHUB_OUTPUT"
- name: Restore portable build outputs
if: inputs.build_cache_artifact == '' && inputs.save != 'true'
uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
+4 -1
View File
@@ -17,7 +17,10 @@ describe the use case and observable outcome. -->
## Change and scope
<!-- Explain the chosen approach, why it fits REA's architecture, and what is
intentionally not included. -->
intentionally not included. If adding complexity for a speculative or vanishingly
unlikely race or security edge case, record the concrete failure mode, likelihood,
complexity cost, and the user's buy-in obtained before implementation. Do not
silently expand scope for theoretical robustness. -->
Follow the [contribution scope](https://github.com/morluto/rea/blob/main/CONTRIBUTING.md#contribution-scope).
Unresolved scope, authority, side effects, or required verification block
+64
View File
@@ -0,0 +1,64 @@
name: Cache retention
on:
workflow_run:
workflows: ["CI"]
types: [completed]
# Qualification calls ci.yml with actions: read. A nested workflow cannot raise
# that token, so cache deletion stays in this workflow.
permissions:
contents: read
actions: write
concurrency:
group: cache-retention-${{ github.event.workflow_run.head_sha }}
cancel-in-progress: false
jobs:
retain:
name: Retain Turbo snapshots and npm download caches
# Restrict the triggering-source checkout to same-repository default-branch
# CI events. The helper runs from this trusted workflow revision; triggering
# source is used only for cache identity and is never executed. Static and
# build save snapshots before later lanes finish, so failed, timed-out, or
# cancelled runs still retain those snapshots.
if: >-
(github.event.workflow_run.conclusion == 'success' ||
github.event.workflow_run.conclusion == 'failure' ||
github.event.workflow_run.conclusion == 'timed_out' ||
github.event.workflow_run.conclusion == 'cancelled') &&
github.event.workflow_run.head_repository.full_name == github.repository &&
github.event.workflow_run.head_branch == github.event.repository.default_branch &&
(github.event.workflow_run.event == 'push' ||
github.event.workflow_run.event == 'schedule' ||
github.event.workflow_run.event == 'workflow_dispatch')
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
with:
ref: ${{ github.workflow_sha }}
path: trusted-retention
persist-credentials: false
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
with:
ref: ${{ github.event.workflow_run.head_sha }}
path: triggering-source
persist-credentials: false
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: trusted-retention/.nvmrc
- name: Retain current Turbo snapshots and npm download caches
env:
GH_TOKEN: ${{ github.token }}
CI_REPOSITORY: ${{ github.repository }}
CACHE_PREFIX: rea-static-v1-${{ runner.os }}-${{ runner.arch }}-
CACHE_KEEP_KEY: rea-static-v1-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('triggering-source/.nvmrc', 'triggering-source/.npmrc', 'triggering-source/package-lock.json') }}-${{ github.event.workflow_run.head_sha }}
CACHE_DEEP_PREFIX: rea-static-deep-v1-${{ runner.os }}-${{ runner.arch }}-
CACHE_DEEP_KEEP_KEY: rea-static-deep-v1-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('triggering-source/.nvmrc', 'triggering-source/.npmrc', 'triggering-source/package-lock.json') }}-${{ github.event.workflow_run.head_sha }}
CACHE_KNIP_PREFIX: knip-meta-v1-${{ runner.os }}-
CACHE_KNIP_KEEP_KEY: knip-meta-v1-${{ runner.os }}-${{ hashFiles('triggering-source/knip.json', 'triggering-source/tsconfig.json', 'triggering-source/package.json', 'triggering-source/package-lock.json') }}-${{ github.event.workflow_run.head_sha }}
CACHE_PORTABLE_PREFIX: rea-portable-build-v2-${{ runner.os }}-${{ runner.arch }}-
DEDUPE_NPM_DOWNLOADS: "true"
run: node trusted-retention/scripts/ci/cache-retention.mjs
+211 -19
View File
@@ -15,6 +15,15 @@ on:
description: Exact qualification source; must match the caller revision.
required: true
type: string
remote_cache_write:
description: Trusted release qualification may publish the remote cache.
required: false
type: boolean
default: false
secrets:
TURBO_TOKEN:
description: Remote cache token. Only trusted release qualification passes it.
required: false
permissions:
contents: read
@@ -29,6 +38,15 @@ concurrency:
env:
HUSKY: "0"
# Set Actions secret TURBO_TOKEN and variable TURBO_TEAM. OIDC can mint
# TURBO_TOKEN via vercel/setup-turborepo-remote-cache-action; do not commit a token.
# A called workflow sees the caller event, so workflow_call is not an event
# name here. Release qualification sets remote_cache_write and passes the
# token. Open pull requests get an empty token and the local cache only.
# Remote reads need a credential, and pull requests do not receive one.
TURBO_TOKEN: "${{ (inputs.remote_cache_write == true || (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'))) && secrets.TURBO_TOKEN || '' }}"
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
TURBO_CACHE: "${{ (inputs.remote_cache_write == true || (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'))) && 'local:rw,remote:rw' || 'local:rw' }}"
jobs:
changes:
@@ -54,12 +72,15 @@ jobs:
browser: ${{ steps.plan.outputs.browser }}
binary_layout: ${{ steps.plan.outputs.binary_layout }}
evm: ${{ steps.plan.outputs.evm }}
apk_signature: ${{ steps.plan.outputs.apk_signature }}
javascript_recovery: ${{ steps.plan.outputs.javascript_recovery }}
recorded_crash: ${{ steps.plan.outputs.recorded_crash }}
web_network: ${{ steps.plan.outputs.web_network }}
web_runtime: ${{ steps.plan.outputs.web_runtime }}
web_source_map: ${{ steps.plan.outputs.web_source_map }}
agentenv: ${{ steps.plan.outputs.agentenv }}
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 }}
@@ -126,6 +147,9 @@ jobs:
- name: Cache npm package downloads
id: npm-cache
uses: ./.github/actions/npm-cache
with:
# Linux x64 writer. Parallel Linux jobs restore this key.
save: "true"
- run: npm ci
- name: Restore static task cache
if: needs.changes.outputs.code == 'true'
@@ -139,19 +163,20 @@ jobs:
- 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 }}
CI_STATIC_CONTEXT_SCRIPT: check:ci:fast:tasks
run: npm run ci:static-context
- if: needs.changes.outputs.code == 'true'
run: npm run check:ci
- if: needs.changes.outputs.code == 'true'
run: npm run verify:test-discovery
- name: Check LLDB producer retention and launch contracts
if: needs.changes.outputs.code == 'true'
run: npm run test:python
run: npm run check:ci:fast
- if: needs.changes.outputs.code != 'true'
run: npm run format:check
- name: Save validated static task cache
@@ -173,6 +198,72 @@ jobs:
if-no-files-found: ignore
retention-days: 7
static-deep:
name: Deep static checks
needs: changes
# Full qualification only. Provider lanes wait on fast static, not this job.
if: needs.changes.outputs.tier == 'full' && needs.changes.outputs.code == 'true'
runs-on: ubuntu-latest
timeout-minutes: 15
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: Restore deep static task cache
id: static-cache
uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: .turbo/cache/
key: rea-static-deep-v1-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('.nvmrc', '.npmrc', 'package-lock.json') }}-${{ github.sha }}
restore-keys: rea-static-deep-v1-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('.nvmrc', '.npmrc', 'package-lock.json') }}-
- name: Restore Knip analysis cache
id: knip-cache
uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: node_modules/.cache/knip
# Each trusted commit publishes a new entry. Restore still matches the
# configuration prefix from the previous successful full run.
key: knip-meta-v1-${{ runner.os }}-${{ hashFiles('knip.json', 'tsconfig.json', 'package.json', 'package-lock.json') }}-${{ github.sha }}
restore-keys: knip-meta-v1-${{ runner.os }}-${{ hashFiles('knip.json', 'tsconfig.json', 'package.json', 'package-lock.json') }}-
- name: Record deep static task source and execution identity
continue-on-error: true
env:
CI_PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }}
CI_STATIC_CONTEXT_SCRIPT: check:ci:deep:tasks
run: npm run ci:static-context
- name: Run deep static checks
run: npm run check:ci:deep
- name: Save validated deep static task cache
if: >-
success() && 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: Save Knip analysis cache
if: >-
success() && steps.npm-cache.outputs.can-save == 'true' &&
steps.knip-cache.outputs.cache-hit != 'true'
uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: node_modules/.cache/knip
key: ${{ steps.knip-cache.outputs.cache-primary-key }}
- name: Upload complete deep static diagnostics
if: always()
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: static-deep-check-reports
path: .cache/static-checks/
include-hidden-files: true
if-no-files-found: ignore
retention-days: 7
test-source:
name: Source tests
needs: changes
@@ -212,9 +303,11 @@ jobs:
# 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:
# 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"]') }}
# Fast feedback uses one source group and eight built groups so the
# slowest built shard job stays inside a ten-minute execution budget.
# Classification, build, and report merge sit outside that job.
# Full qualification retains six complete shards with coverage.
shard: ${{ fromJSON(needs.changes.outputs.tier == 'fast' && '["1/8","2/8","3/8","4/8","5/8","6/8","7/8","8/8"]' || '["1/6","2/6","3/6","4/6","5/6","6/6"]') }}
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
@@ -292,11 +385,13 @@ jobs:
needs.changes.outputs.browser == 'true' ||
needs.changes.outputs.binary_layout == 'true' ||
needs.changes.outputs.evm == 'true' ||
needs.changes.outputs.apk_signature == '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'
needs.changes.outputs.web_source_map == 'true' ||
needs.changes.outputs.agentenv == 'true'
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
@@ -316,7 +411,9 @@ jobs:
with:
save: ${{ github.event_name != 'pull_request' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }}
- run: npm run test:prepare
- name: Find available trusted full-suite timing history
# Fast and full shards measure different file costs. Download only the
# matching history so one tier cannot pack the other with its weights.
- name: Find available trusted timing history
id: history
continue-on-error: true
env:
@@ -324,13 +421,14 @@ jobs:
CI_REPOSITORY: ${{ github.repository }}
CI_REPOSITORY_ID: ${{ github.repository_id }}
CI_DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
CI_TIMING_TIER: ${{ needs.changes.outputs.tier }}
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@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: test-timings
name: ${{ steps.history.outputs.artifact-name }}
run-id: ${{ steps.history.outputs.run-id }}
github-token: ${{ github.token }}
path: .cache/ci-test-history
@@ -351,17 +449,23 @@ 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.
- name: Stage current portable build task cache
shell: bash
run: |
# A new Turbo dry run can evict the entries this build just used.
node scripts/ci/portable-build-cache.mjs snapshot < .cache/ci-build-plan.json
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: rea-build-cache
path: .turbo/cache/
path: .cache/ci-build-cache/
include-hidden-files: true
if-no-files-found: error
retention-days: 7
sqlite-runtime:
# Preserve all selected coverage, but admit expensive host/provider work
# only after static validation. Portable tests still provide early feedback.
# Admit expensive host/provider work after fast static validation
# (typecheck, lint, format). Deep static does not gate these lanes.
# Portable tests still provide early feedback.
needs: [changes, static, build]
if: needs.changes.outputs.sqlite == 'true'
uses: ./.github/workflows/sqlite-runtime.yml
@@ -456,11 +560,22 @@ jobs:
include-hidden-files: true
if-no-files-found: error
retention-days: 30
# Default-branch runs only. Pull-request artifacts share this name and
# the lookup reads one page, so publishing them would hide main.
- name: Publish successful timing history
if: needs.changes.outputs.code == 'true' && needs.changes.outputs.tier == 'full' && success() && needs.test-shard.result == 'success'
if: >-
success() &&
needs.changes.outputs.code == 'true' &&
needs.test-shard.result == 'success' &&
(
needs.changes.outputs.tier == 'full' ||
(needs.changes.outputs.tier == 'fast' && needs.test-source.result == 'success')
) &&
github.ref == format('refs/heads/{0}', github.event.repository.default_branch) &&
(github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch')
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: test-timings
name: test-timings-${{ needs.changes.outputs.tier }}
path: .cache/ci-test-history/test-results.json
include-hidden-files: true
if-no-files-found: error
@@ -564,6 +679,9 @@ jobs:
- name: Cache npm package downloads
id: npm-cache
uses: ./.github/actions/npm-cache
with:
# ubuntu-latest is the Linux x64 writer above. Other hosts save their own key.
save: ${{ matrix.os != 'ubuntu-latest' }}
- name: Verify native runtime identity
env:
EXPECTED_CI_PLATFORM: ${{ matrix.platform }}
@@ -642,9 +760,11 @@ jobs:
tests/boundary/cli/nativePlistPrototypeKeyCli.test.ts
tests/boundary/cli/nativePlistSelectionFailureCli.test.ts
tests/boundary/cli/nativeDemangleCli.test.ts
tests/boundary/cli/nativeDispatchPropertyAttributes.test.ts
tests/boundary/mcp/nativeDemangleMcp.test.ts
tests/boundary/providers/native/nativeSignatureTargetBinding.test.ts
tests/boundary/cli/nativeSignatureCli.test.ts
tests/boundary/cli/nativeMachoArm64_32Cli.test.ts
tests/boundary/mcp/nativeSignatureTargetBindingMcp.test.ts
src/process/capture/FilesystemSnapshot.test.ts
tests/process-global/filesystemSnapshotFallbackCancellation.test.ts
@@ -699,17 +819,24 @@ jobs:
- name: Cache npm package downloads
id: npm-cache
uses: ./.github/actions/npm-cache
with:
save: "true"
- run: npm ci
- name: Typecheck with the official Windows compiler
env:
GOMAXPROCS: "2"
GOMEMLIMIT: "3GiB"
run: npm run typecheck -- --checkers 2
- 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@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: windows-native-controls
path: native/windows/build/
- run: npm run verify:windows-native:artifact
- run: npm run build:cached
- name: Record named Windows host capabilities
run: node scripts/windows-capabilities.mjs | Out-File -Encoding utf8NoBOM windows-capabilities.json
- name: Validate Windows host capability report
@@ -730,17 +857,26 @@ jobs:
npx vitest run --coverage.enabled=false
src/process/capture/FilesystemSnapshot.test.ts
tests/process-global/filesystemSnapshotFallbackCancellation.test.ts
tests/boundary/filesystem/typescriptCompiler.test.ts
tests/boundary/filesystem/binaryTarget.test.ts
src/contracts/sessionPathInputs.test.ts
tests/boundary/filesystem/directAnalysisSnapshotReplay.test.ts
tests/boundary/mcp/rawBinaryMcp.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/filesystem/clientConfigurationEnvironment.test.ts
tests/boundary/filesystem/clientConfigurationLayeredEnvironment.test.ts
tests/acceptance/setup/registrationRuntimeEnvironmentCli.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/packedBridgeVerification.test.ts
tests/boundary/process/windowsOutputCapture.test.ts
tests/acceptance/webModuleCli.test.ts
tests/acceptance/webSourceLocationCli.test.ts
@@ -763,6 +899,8 @@ jobs:
src/process/ProcessOwnership.validation.test.ts
src/application/Update.test.ts
tests/process-global/updateMaintenance.test.ts
tests/process-global/runnerCancellation.test.ts
tests/process-global/windowsOwnedCommand.test.ts
tests/conformance/providers/ghidra/realGhidraWindowsWorkflow.test.ts
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
@@ -795,7 +933,7 @@ jobs:
tests/boundary/filesystem/javascriptArtifactContentIdentity.test.ts
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: 22.19.0
node-version: 22.23.4
- name: Verify native path boundaries on Node 22
run: >-
npx vitest run --coverage.enabled=false
@@ -837,67 +975,117 @@ jobs:
skills/reverse-engineer-anything
if-no-files-found: error
# Provider lanes wait for fast static and the portable build, not deep static.
browser:
needs: [changes, static, build]
if: needs.changes.outputs.browser == 'true'
secrets:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
uses: ./.github/workflows/real-browser.yml
with:
build_cache_artifact: rea-build-cache
remote_cache_write: ${{ inputs.remote_cache_write == true || (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch')) }}
binary_layout:
needs: [changes, static, build]
if: needs.changes.outputs.binary_layout == 'true'
secrets:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
uses: ./.github/workflows/real-binary-layout.yml
with:
build_cache_artifact: rea-build-cache
remote_cache_write: ${{ inputs.remote_cache_write == true || (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch')) }}
evm:
needs: [changes, static, build]
if: needs.changes.outputs.evm == 'true'
secrets:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
uses: ./.github/workflows/real-evm-interface.yml
with:
build_cache_artifact: rea-build-cache
remote_cache_write: ${{ inputs.remote_cache_write == true || (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch')) }}
apk_signature:
needs: [changes, static, build]
if: needs.changes.outputs.apk_signature == 'true'
secrets:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
uses: ./.github/workflows/real-apk-signature.yml
with:
build_cache_artifact: rea-build-cache
remote_cache_write: ${{ inputs.remote_cache_write == true || (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch')) }}
javascript_recovery:
needs: [changes, static, build]
if: needs.changes.outputs.javascript_recovery == 'true'
secrets:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
uses: ./.github/workflows/real-javascript-recovery.yml
with:
build_cache_artifact: rea-build-cache
remote_cache_write: ${{ inputs.remote_cache_write == true || (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch')) }}
recorded_crash:
needs: [changes, static, build]
if: needs.changes.outputs.recorded_crash == 'true'
secrets:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
uses: ./.github/workflows/real-recorded-crash.yml
with:
build_cache_artifact: rea-build-cache
remote_cache_write: ${{ inputs.remote_cache_write == true || (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch')) }}
web_network:
needs: [changes, static, build]
if: needs.changes.outputs.web_network == 'true'
secrets:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
uses: ./.github/workflows/real-web-network-captures.yml
with:
build_cache_artifact: rea-build-cache
remote_cache_write: ${{ inputs.remote_cache_write == true || (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch')) }}
web_runtime:
needs: [changes, static, build]
if: needs.changes.outputs.web_runtime == 'true'
secrets:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
uses: ./.github/workflows/real-web-runtime.yml
with:
build_cache_artifact: rea-build-cache
remote_cache_write: ${{ inputs.remote_cache_write == true || (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch')) }}
web_source_map:
needs: [changes, static, build]
if: needs.changes.outputs.web_source_map == 'true'
secrets:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
uses: ./.github/workflows/real-web-source-map.yml
with:
build_cache_artifact: rea-build-cache
remote_cache_write: ${{ inputs.remote_cache_write == true || (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch')) }}
agentenv:
needs: [changes, static, build]
if: needs.changes.outputs.agentenv == 'true'
secrets:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
uses: ./.github/workflows/real-agentenv.yml
with:
build_cache_artifact: rea-build-cache
remote_cache_write: ${{ inputs.remote_cache_write == true || (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch')) }}
nativeaot:
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, static]
if: needs.changes.outputs.website == 'true'
@@ -911,6 +1099,7 @@ jobs:
[
changes,
static,
static-deep,
build,
reverse-tools-quality,
test-shard,
@@ -927,12 +1116,15 @@ jobs:
browser,
binary_layout,
evm,
apk_signature,
javascript_recovery,
recorded_crash,
web_network,
web_runtime,
web_source_map,
agentenv,
nativeaot,
ghidra_bridge,
website,
]
runs-on: ubuntu-24.04
@@ -0,0 +1,64 @@
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"
# The bridge probe imports only source-owned ARM/Thumb bytes and never
# executes target code. All SDK/project state stays in runner storage.
- name: Acquire checksum-verified Ghidra SDK 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" -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
- name: Inspect real ARM and Thumb instruction context through the bridge
shell: bash
run: |
java_home_variable="JAVA_HOME_${GHIDRA_JAVA_MAJOR}_X64"
export JAVA_HOME="${!java_home_variable}"
probe_root="${RUNNER_TEMP}/rea-arm-context"
mkdir -p "${probe_root}/project"
printf '\036\377\057\341\160\107\000\000' > "${probe_root}/arm-thumb.bin"
timeout 180s "${GHIDRA_INSTALL_DIR}/support/analyzeHeadless" "${probe_root}/project" ArmContext \
-import "${probe_root}/arm-thumb.bin" -loader BinaryLoader -processor ARM:LE:32:v7 \
-noanalysis -readOnly -deleteProject \
-scriptPath "${GITHUB_WORKSPACE}/tests/conformance/ghidra;${GITHUB_WORKSPACE}/bridge/ghidra" \
-postScript ReaArmContextProbe.java "${GITHUB_WORKSPACE}/bridge/ghidra" \
> "${probe_root}/output.log" 2>&1 || { cat "${probe_root}/output.log"; exit 1; }
cat "${probe_root}/output.log"
grep -F 'REA_ARM_CONTEXT_OK arm=0 thumb=1 unavailable=null (injected provider value)' "${probe_root}/output.log"
+5
View File
@@ -14,6 +14,11 @@ concurrency:
env:
HUSKY: "0"
# Writes only on the default branch for push, schedule, manual dispatch,
# and workflow_call. Pull requests get an empty token and remote read-only mode.
TURBO_TOKEN: "${{ (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_call')) && secrets.TURBO_TOKEN || '' }}"
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
TURBO_CACHE: "${{ (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_call')) && 'local:rw,remote:rw' || 'local:rw,remote:r' }}"
jobs:
mutation:
+98
View File
@@ -0,0 +1,98 @@
name: Real agentenv environment bundle
on:
workflow_dispatch:
workflow_call:
inputs:
build_cache_artifact:
description: Portable build cache artifact from the calling CI run.
required: false
type: string
default: ""
remote_cache_write:
description: Trusted release qualification may publish the remote cache.
required: false
type: boolean
default: false
secrets:
TURBO_TOKEN:
description: Remote cache token. Only trusted callers pass it.
required: false
permissions:
contents: read
env:
HUSKY: "0"
# A called workflow sees the caller event, so workflow_call is not an event
# name here. Writes when remote_cache_write is set, or on the default branch
# for push, schedule, and manual dispatch. Open pull requests stay empty-token
# and use the local cache only.
TURBO_TOKEN: "${{ (inputs.remote_cache_write == true || (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'))) && secrets.TURBO_TOKEN || '' }}"
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
TURBO_CACHE: "${{ (inputs.remote_cache_write == true || (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'))) && 'local:rw,remote:rw' || 'local:rw' }}"
NODE_OPTIONS: --max-old-space-size=768
PIP_DISABLE_PIP_VERSION_CHECK: "1"
jobs:
real-agentenv:
name: Export and run an environment bundle with the agentenv runner
runs-on: ubuntu-24.04
timeout-minutes: 30
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
- uses: ./.github/actions/portable-build-cache
with:
build_cache_artifact: ${{ inputs.build_cache_artifact }}
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
with:
python-version: "3.12"
# Public ECR caps anonymous pulls by data per source IP and hosted runners share IPs, so the
# framework's image builds can fail here for hours. mirror.gcr.io serves the same Docker
# official images under the same digests, so each base image the pinned framework builds from
# is pulled there and named as its Dockerfile names it, as the framework's own CI does.
# Digest-pinned names are set in containerd, whose images Docker reads only on the containerd
# image store; switching stores restarts dockerd, so it happens before anything uses Docker.
- name: Use the containerd image store
run: |
[ -s /etc/docker/daemon.json ] || echo '{}' | sudo tee /etc/docker/daemon.json >/dev/null
jq '.features["containerd-snapshotter"] = true' /etc/docker/daemon.json > "$RUNNER_TEMP/daemon.json"
sudo mv "$RUNNER_TEMP/daemon.json" /etc/docker/daemon.json
sudo systemctl restart docker
docker info --format '{{json .DriverStatus}}' | grep -q io.containerd.snapshotter
- name: Acquire the pinned agentenv runner in isolated runner storage and check host Docker
shell: bash
run: |
runner_root="${RUNNER_TEMP}/rea-agentenv-runner"
python -m venv "${runner_root}"
"${runner_root}/bin/python" -m pip install --require-virtualenv --no-cache-dir 'agentenv-framework==0.9.1334'
"${runner_root}/bin/agent-env" --version
docker version --format '{{.Server.Version}}'
echo "REA_AGENT_ENV_COMMAND=${runner_root}/bin/agent-env" >> "${GITHUB_ENV}"
echo "REA_DOCKER_COMMAND=$(command -v docker)" >> "${GITHUB_ENV}"
- name: Pull the pinned framework's public ECR base images from mirror.gcr.io
shell: bash
run: |
package="$("${REA_AGENT_ENV_COMMAND%/agent-env}/python" -c 'import agent_env, pathlib; print(pathlib.Path(agent_env.__file__).parent)')"
grep -rhoE '^FROM +public\.ecr\.aws/docker/library/[^ ]+' --include='Dockerfile*' "${package}" | awk '{print $2}' | sort -u |
while read -r image; do
name=${image#public.ecr.aws/docker/library/}
source=mirror.gcr.io/library/$name
case $name in *@*) source=mirror.gcr.io/library/${name%%[:@]*}@${name#*@} ;; esac
for attempt in 1 2 3; do
docker pull --quiet "$source" && break
if [ "$attempt" = 3 ]; then echo "::warning::couldn't pull $source; $image's build will pull it"; continue 2; fi
sleep $((attempt * 10))
done
case ${name%@*} in *:*) docker tag "$source" "${image%@*}" ;; esac
case $image in *@*) sudo ctr --namespace moby images tag "$source" "$image" >/dev/null ;; esac
done
- name: Export a bundle from the source-owned capture and run it with the agentenv runner
run: npm run verify:agentenv:environment
+97
View File
@@ -0,0 +1,97 @@
name: Real APK signature inspection
on:
workflow_call:
inputs:
build_cache_artifact:
description: Portable build cache artifact from the calling CI run.
required: false
type: string
default: ""
remote_cache_write:
description: Trusted release qualification may publish the remote cache.
required: false
type: boolean
default: false
secrets:
TURBO_TOKEN:
description: Remote cache token. Only trusted callers pass it.
required: false
workflow_dispatch:
permissions:
contents: read
env:
HUSKY: "0"
# A called workflow sees the caller event, so workflow_call is not an event
# name here. Writes when remote_cache_write is set, or on the default branch
# for push, schedule, and manual dispatch. Open pull requests stay empty-token
# and use the local cache only.
TURBO_TOKEN: "${{ (inputs.remote_cache_write == true || (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'))) && secrets.TURBO_TOKEN || '' }}"
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
TURBO_CACHE: "${{ (inputs.remote_cache_write == true || (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'))) && 'local:rw,remote:rw' || 'local:rw' }}"
NODE_OPTIONS: "--max-old-space-size=768"
RAYON_NUM_THREADS: "1"
GOMAXPROCS: "2"
UV_THREADPOOL_SIZE: "2"
JAVA_TOOL_OPTIONS: "-Xmx512m -XX:ActiveProcessorCount=2"
jobs:
real-apk-signature:
runs-on: ubuntu-latest
timeout-minutes: 12
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
- uses: ./.github/actions/portable-build-cache
with:
build_cache_artifact: ${{ inputs.build_cache_artifact }}
- name: Prepare pinned SDK oracle without installing host software
shell: bash
run: |
python3 - <<'PYTHON'
import hashlib, os, urllib.request, zipfile
from pathlib import Path
root = Path(os.environ["RUNNER_TEMP"]) / "rea-apk-signature-sdk"
packages = [
("build-tools_r36.1_linux.zip", "a7b5889e4a79fcf3b0976bef40d401f4240fb1eed891d9d91169da1111e11d78", 64195204, "build-tools/36.1.0", {"apksigner", "aapt2", "lib/apksigner.jar", "source.properties"}),
("platform-36_r02.zip", "37607369a28c5b640b3a7998868d45898ebcb777565a0e85f9acf36f29631d2e", 65878410, "platforms/android-36", {"android.jar", "source.properties"}),
]
for name, expected, expected_size, directory, selected in packages:
archive = root / name
archive.parent.mkdir(parents=True, exist_ok=True)
digest = hashlib.sha256()
total = 0
with urllib.request.urlopen("https://dl.google.com/android/repository/" + name, timeout=60) as response, archive.open("wb") as output:
while chunk := response.read(1024 * 1024):
total += len(chunk)
if total > expected_size:
raise RuntimeError("SDK archive exceeds the pinned package size budget")
digest.update(chunk)
output.write(chunk)
if total != expected_size or digest.hexdigest() != expected:
raise RuntimeError("SDK oracle archive digest mismatch")
extracted = set()
with zipfile.ZipFile(archive) as source:
for entry in source.infolist():
relative = entry.filename.partition("/")[2]
if relative not in selected:
continue
target = root / directory / relative
target.parent.mkdir(parents=True, exist_ok=True)
target.write_bytes(source.read(entry))
target.chmod((entry.external_attr >> 16) & 0o777 or 0o644)
extracted.add(relative)
if extracted != selected:
raise RuntimeError("Pinned SDK package is missing required oracle files")
with Path(os.environ["GITHUB_ENV"]).open("a") as environment:
environment.write("ANDROID_HOME=" + str(root) + "\n")
PYTHON
- run: npm run verify:apk-signature -- --require-tools
+16
View File
@@ -14,6 +14,15 @@ on:
required: false
type: string
default: ""
remote_cache_write:
description: Trusted release qualification may publish the remote cache.
required: false
type: boolean
default: false
secrets:
TURBO_TOKEN:
description: Remote cache token. Only trusted callers pass it.
required: false
permissions:
contents: read
@@ -21,6 +30,13 @@ permissions:
env:
NODE_OPTIONS: --max-old-space-size=768
HUSKY: "0"
# A called workflow sees the caller event, so workflow_call is not an event
# name here. Writes when remote_cache_write is set, or on the default branch
# for push, schedule, and manual dispatch. Open pull requests stay empty-token
# and use the local cache only.
TURBO_TOKEN: "${{ (inputs.remote_cache_write == true || (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'))) && secrets.TURBO_TOKEN || '' }}"
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
TURBO_CACHE: "${{ (inputs.remote_cache_write == true || (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'))) && 'local:rw,remote:rw' || 'local:rw' }}"
REA_VERIFY_LARGE_ELF_MCP: ${{ inputs.large_mcp && '1' || '0' }}
jobs:
+16
View File
@@ -8,10 +8,26 @@ on:
required: false
type: string
default: ""
remote_cache_write:
description: Trusted release qualification may publish the remote cache.
required: false
type: boolean
default: false
secrets:
TURBO_TOKEN:
description: Remote cache token. Only trusted callers pass it.
required: false
workflow_dispatch:
env:
HUSKY: "0"
# A called workflow sees the caller event, so workflow_call is not an event
# name here. Writes when remote_cache_write is set, or on the default branch
# for push, schedule, and manual dispatch. Open pull requests stay empty-token
# and use the local cache only.
TURBO_TOKEN: "${{ (inputs.remote_cache_write == true || (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'))) && secrets.TURBO_TOKEN || '' }}"
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
TURBO_CACHE: "${{ (inputs.remote_cache_write == true || (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'))) && 'local:rw,remote:rw' || 'local:rw' }}"
permissions:
contents: read
+11 -3
View File
@@ -8,6 +8,14 @@ on:
permissions:
contents: read
env:
HUSKY: "0"
# Writes only on the default branch for push, schedule, manual dispatch,
# and workflow_call. Pull requests get an empty token and remote read-only mode.
TURBO_TOKEN: "${{ (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_call')) && secrets.TURBO_TOKEN || '' }}"
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
TURBO_CACHE: "${{ (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_call')) && 'local:rw,remote:rw' || 'local:rw,remote:r' }}"
jobs:
windows-native-artifact:
name: Build Windows bridge security boundary
@@ -22,7 +30,7 @@ jobs:
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@v4
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: cutter-windows-native
path: native/windows/build/
@@ -53,7 +61,7 @@ jobs:
uses: ./.github/actions/npm-cache
- run: npm ci
- if: matrix.platform == 'Windows'
uses: actions/download-artifact@v4
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: cutter-windows-native
path: native/windows/build/
@@ -67,7 +75,7 @@ jobs:
env:
EXPECTED_PLATFORM: ${{ matrix.platform }}
run: npm run verify:cutter
- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
if: always()
with:
name: cutter-upstream-${{ matrix.platform }}
+16
View File
@@ -9,6 +9,15 @@ on:
required: false
type: string
default: ""
remote_cache_write:
description: Trusted release qualification may publish the remote cache.
required: false
type: boolean
default: false
secrets:
TURBO_TOKEN:
description: Remote cache token. Only trusted callers pass it.
required: false
permissions:
contents: read
@@ -16,6 +25,13 @@ permissions:
env:
NODE_OPTIONS: --max-old-space-size=768
HUSKY: "0"
# A called workflow sees the caller event, so workflow_call is not an event
# name here. Writes when remote_cache_write is set, or on the default branch
# for push, schedule, and manual dispatch. Open pull requests stay empty-token
# and use the local cache only.
TURBO_TOKEN: "${{ (inputs.remote_cache_write == true || (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'))) && secrets.TURBO_TOKEN || '' }}"
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
TURBO_CACHE: "${{ (inputs.remote_cache_write == true || (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'))) && 'local:rw,remote:rw' || 'local:rw' }}"
jobs:
real-evm-interface:
@@ -6,6 +6,11 @@ on:
env:
HUSKY: "0"
# Writes only on the default branch for push, schedule, manual dispatch,
# and workflow_call. Pull requests get an empty token and remote read-only mode.
TURBO_TOKEN: "${{ (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_call')) && secrets.TURBO_TOKEN || '' }}"
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
TURBO_CACHE: "${{ (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_call')) && 'local:rw,remote:rw' || 'local:rw,remote:r' }}"
permissions:
contents: read
+5 -12
View File
@@ -13,20 +13,8 @@ on:
- "src/composition/go.ts"
- "src/cli/goCommands.ts"
- "src/server/registerGoTools.ts"
- "src/server/createServer.ts"
- "src/cli.ts"
- "src/cliCommandNames.ts"
- "src/contracts/toolContracts.ts"
- "src/contracts/toolEffects.ts"
- "src/artifacts/readStableArtifact*.ts"
- "src/artifacts/ArtifactReader.ts"
- "src/process/PrivateRuntimeRoot.ts"
- "scripts/verify-go-binary.mjs"
- "scripts/lib/mcp-verifier-results.mjs"
- "scripts/rea.mjs"
- "package.json"
- "package-lock.json"
- ".nvmrc"
permissions:
contents: read
@@ -38,6 +26,11 @@ concurrency:
env:
NODE_OPTIONS: --max-old-space-size=768
HUSKY: "0"
# Writes only on the default branch for push, schedule, manual dispatch,
# and workflow_call. Pull requests get an empty token and remote read-only mode.
TURBO_TOKEN: "${{ (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_call')) && secrets.TURBO_TOKEN || '' }}"
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
TURBO_CACHE: "${{ (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_call')) && 'local:rw,remote:rw' || 'local:rw,remote:r' }}"
jobs:
real-go-binary:
+5
View File
@@ -5,6 +5,11 @@ on:
env:
HUSKY: "0"
# Writes only on the default branch for push, schedule, manual dispatch,
# and workflow_call. Pull requests get an empty token and remote read-only mode.
TURBO_TOKEN: "${{ (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_call')) && secrets.TURBO_TOKEN || '' }}"
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
TURBO_CACHE: "${{ (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_call')) && 'local:rw,remote:rw' || 'local:rw,remote:r' }}"
permissions:
contents: read
+5
View File
@@ -5,6 +5,11 @@ on:
env:
HUSKY: "0"
# Writes only on the default branch for push, schedule, manual dispatch,
# and workflow_call. Pull requests get an empty token and remote read-only mode.
TURBO_TOKEN: "${{ (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_call')) && secrets.TURBO_TOKEN || '' }}"
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
TURBO_CACHE: "${{ (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_call')) && 'local:rw,remote:rw' || 'local:rw,remote:r' }}"
permissions:
contents: read
@@ -8,6 +8,15 @@ on:
required: false
type: string
default: ""
remote_cache_write:
description: Trusted release qualification may publish the remote cache.
required: false
type: boolean
default: false
secrets:
TURBO_TOKEN:
description: Remote cache token. Only trusted callers pass it.
required: false
workflow_dispatch:
permissions:
@@ -15,6 +24,13 @@ permissions:
env:
HUSKY: "0"
# A called workflow sees the caller event, so workflow_call is not an event
# name here. Writes when remote_cache_write is set, or on the default branch
# for push, schedule, and manual dispatch. Open pull requests stay empty-token
# and use the local cache only.
TURBO_TOKEN: "${{ (inputs.remote_cache_write == true || (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'))) && secrets.TURBO_TOKEN || '' }}"
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
TURBO_CACHE: "${{ (inputs.remote_cache_write == true || (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'))) && 'local:rw,remote:rw' || 'local:rw' }}"
NODE_OPTIONS: "--max-old-space-size=768"
RAYON_NUM_THREADS: "1"
+8 -12
View File
@@ -20,21 +20,17 @@ on:
- "src/composition/captures.ts"
- "src/cli/captureCommands.ts"
- "src/server/registerCaptureTools.ts"
- "src/server/createServer.ts"
- "src/server/sessionAvailabilityPolicy.ts"
- "src/application/CapabilityInventory.ts"
- "src/contracts/toolContracts.ts"
- "src/contracts/toolEffects.ts"
- "src/cli.ts"
- "src/cliCommandNames.ts"
- "src/process/**"
- "src/artifacts/readStableArtifact.ts"
- "scripts/verify-packet-capture.mjs"
- ".github/workflows/real-packet-capture.yml"
- "package*.json"
- ".nvmrc"
permissions:
contents: read
env:
HUSKY: "0"
# Writes only on the default branch for push, schedule, manual dispatch,
# and workflow_call. Pull requests get an empty token and remote read-only mode.
TURBO_TOKEN: "${{ (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_call')) && secrets.TURBO_TOKEN || '' }}"
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
TURBO_CACHE: "${{ (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_call')) && 'local:rw,remote:rw' || 'local:rw,remote:r' }}"
concurrency:
group: real-packet-capture-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
@@ -53,7 +49,7 @@ jobs:
- 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
- run: npm run test:focused -- tests/boundary/captures/fieldDiscovery.test.ts src/process/LinuxResourceLimits.test.ts tests/boundary/captures/captureProvider.test.ts tests/boundary/captures/captureDescriptorCleanup.test.ts tests/boundary/captures/resourceLimits.test.ts tests/boundary/captures/packetOutput.test.ts tests/boundary/mcp/packetCaptureMcp.test.ts tests/boundary/cli/captureCleanup.test.ts
- run: npm run test:focused -- tests/boundary/captures/fieldDiscovery.test.ts src/process/LinuxResourceLimits.test.ts tests/boundary/captures/captureProvider.test.ts tests/boundary/captures/captureDescriptorCleanup.test.ts tests/boundary/pcap/pcapProvider.test.ts tests/boundary/pcap/pcapProviderCrossPlatform.test.ts tests/boundary/captures/resourceLimits.test.ts tests/boundary/captures/packetOutput.test.ts tests/boundary/mcp/packetCaptureMcp.test.ts tests/boundary/cli/captureCleanup.test.ts
- run: node scripts/verify-packet-capture.mjs
env:
REA_TSHARK_COMMAND: /usr/bin/tshark
+28 -5
View File
@@ -9,6 +9,15 @@ on:
required: false
type: string
default: ""
remote_cache_write:
description: Trusted release qualification may publish the remote cache.
required: false
type: boolean
default: false
secrets:
TURBO_TOKEN:
description: Remote cache token. Only trusted callers pass it.
required: false
permissions:
contents: read
@@ -16,6 +25,13 @@ permissions:
env:
NODE_OPTIONS: --max-old-space-size=768
HUSKY: "0"
# A called workflow sees the caller event, so workflow_call is not an event
# name here. Writes when remote_cache_write is set, or on the default branch
# for push, schedule, and manual dispatch. Open pull requests stay empty-token
# and use the local cache only.
TURBO_TOKEN: "${{ (inputs.remote_cache_write == true || (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'))) && secrets.TURBO_TOKEN || '' }}"
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
TURBO_CACHE: "${{ (inputs.remote_cache_write == true || (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'))) && 'local:rw,remote:rw' || 'local:rw' }}"
UV_CONCURRENT_BUILDS: "1"
UV_CONCURRENT_INSTALLS: "1"
UV_PYTHON_DOWNLOADS: never
@@ -24,7 +40,19 @@ jobs:
real-recorded-crash:
runs-on: ubuntu-24.04
timeout-minutes: 15
strategy:
fail-fast: false
matrix:
image: ["ubuntu:24.04", "debian:trixie-slim"]
container: ${{ matrix.image }}
steps:
- name: Install fixture and debugger prerequisites in the disposable container
shell: bash
run: |
apt-get update -qq
apt-get install --yes --no-install-recommends \
ca-certificates git python3-venv gcc libc6-dev strace gdb
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
@@ -36,11 +64,6 @@ jobs:
- 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: |
sudo apt-get update -qq
sudo apt-get install --yes --no-install-recommends gdb
- name: Acquire exact unchanged upstreams in isolated runner storage
shell: bash
run: |
+7 -11
View File
@@ -13,21 +13,17 @@ on:
- "src/composition/signals.ts"
- "src/cli/signalCommands.ts"
- "src/server/registerSignalTools.ts"
- "src/server/createServer.ts"
- "src/server/sessionAvailabilityPolicy.ts"
- "src/application/CapabilityInventory.ts"
- "src/contracts/toolContracts.ts"
- "src/contracts/toolEffects.ts"
- "src/cli.ts"
- "src/cliCommandNames.ts"
- "src/process/**"
- "src/artifacts/readStableArtifact.ts"
- "scripts/verify-signal-recording.mjs"
- ".github/workflows/real-signal-recording.yml"
- "package*.json"
- ".nvmrc"
permissions:
contents: read
env:
HUSKY: "0"
# Writes only on the default branch for push, schedule, manual dispatch,
# and workflow_call. Pull requests get an empty token and remote read-only mode.
TURBO_TOKEN: "${{ (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_call')) && secrets.TURBO_TOKEN || '' }}"
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
TURBO_CACHE: "${{ (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_call')) && 'local:rw,remote:rw' || 'local:rw,remote:r' }}"
concurrency:
group: real-signal-recording-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
+5
View File
@@ -12,6 +12,11 @@ permissions:
env:
HUSKY: "0"
# Writes only on the default branch for push, schedule, manual dispatch,
# and workflow_call. Pull requests get an empty token and remote read-only mode.
TURBO_TOKEN: "${{ (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_call')) && secrets.TURBO_TOKEN || '' }}"
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
TURBO_CACHE: "${{ (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_call')) && 'local:rw,remote:rw' || 'local:rw,remote:r' }}"
jobs:
real-termux:
@@ -9,12 +9,28 @@ on:
required: false
type: string
default: ""
remote_cache_write:
description: Trusted release qualification may publish the remote cache.
required: false
type: boolean
default: false
secrets:
TURBO_TOKEN:
description: Remote cache token. Only trusted callers pass it.
required: false
permissions:
contents: read
env:
HUSKY: "0"
# A called workflow sees the caller event, so workflow_call is not an event
# name here. Writes when remote_cache_write is set, or on the default branch
# for push, schedule, and manual dispatch. Open pull requests stay empty-token
# and use the local cache only.
TURBO_TOKEN: "${{ (inputs.remote_cache_write == true || (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'))) && secrets.TURBO_TOKEN || '' }}"
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
TURBO_CACHE: "${{ (inputs.remote_cache_write == true || (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'))) && 'local:rw,remote:rw' || 'local:rw' }}"
NODE_OPTIONS: --max-old-space-size=768
jobs:
+16
View File
@@ -7,11 +7,27 @@ on:
required: false
type: string
default: ""
remote_cache_write:
description: Trusted release qualification may publish the remote cache.
required: false
type: boolean
default: false
secrets:
TURBO_TOKEN:
description: Remote cache token. Only trusted callers pass it.
required: false
workflow_dispatch:
permissions:
contents: read
env:
HUSKY: "0"
# A called workflow sees the caller event, so workflow_call is not an event
# name here. Writes when remote_cache_write is set, or on the default branch
# for push, schedule, and manual dispatch. Open pull requests stay empty-token
# and use the local cache only.
TURBO_TOKEN: "${{ (inputs.remote_cache_write == true || (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'))) && secrets.TURBO_TOKEN || '' }}"
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
TURBO_CACHE: "${{ (inputs.remote_cache_write == true || (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'))) && 'local:rw,remote:rw' || 'local:rw' }}"
jobs:
real-web-runtime:
runs-on: ubuntu-latest
+16
View File
@@ -7,11 +7,27 @@ on:
required: false
type: string
default: ""
remote_cache_write:
description: Trusted release qualification may publish the remote cache.
required: false
type: boolean
default: false
secrets:
TURBO_TOKEN:
description: Remote cache token. Only trusted callers pass it.
required: false
workflow_dispatch:
permissions:
contents: read
env:
HUSKY: "0"
# A called workflow sees the caller event, so workflow_call is not an event
# name here. Writes when remote_cache_write is set, or on the default branch
# for push, schedule, and manual dispatch. Open pull requests stay empty-token
# and use the local cache only.
TURBO_TOKEN: "${{ (inputs.remote_cache_write == true || (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'))) && secrets.TURBO_TOKEN || '' }}"
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
TURBO_CACHE: "${{ (inputs.remote_cache_write == true || (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'))) && 'local:rw,remote:rw' || 'local:rw' }}"
jobs:
real-web-source-map:
runs-on: ubuntu-latest
+56 -23
View File
@@ -21,6 +21,12 @@ on:
env:
HUSKY: "0"
# Remote cache stays local when either value is empty.
# Writes only on the default branch for push, schedule, manual dispatch,
# and workflow_call. Pull requests get an empty token and remote read-only mode.
TURBO_TOKEN: "${{ (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_call')) && secrets.TURBO_TOKEN || '' }}"
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
TURBO_CACHE: "${{ (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_call')) && 'local:rw,remote:rw' || 'local:rw,remote:r' }}"
concurrency:
group: release-${{ inputs.release_branch || github.event.pull_request.merge_commit_sha || github.ref_name }}
@@ -301,11 +307,17 @@ jobs:
contents: read
actions: read
uses: ./.github/workflows/ci.yml
secrets:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
with:
source_sha: ${{ needs.merged-release.outputs.sha || needs.release-please.outputs.sha }}
remote_cache_write: true
rehearsal:
name: Rehearse qualified release on a clean runner
env:
TURBO_TOKEN: ""
TURBO_CACHE: local:rw
needs: [merged-release, release-please, qualification]
if: >-
always() && !cancelled() && needs.qualification.result == 'success'
@@ -339,6 +351,9 @@ jobs:
always() && !cancelled() &&
needs.qualification.result == 'success' && needs.rehearsal.result == 'success'
runs-on: ubuntu-latest
env:
TURBO_TOKEN: ""
TURBO_CACHE: local:rw
permissions:
contents: read
id-token: write
@@ -347,41 +362,56 @@ jobs:
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
registry-url: https://registry.npmjs.org
- name: Download the original qualified package
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: verified-release-package
path: .cache/verified-package/
- name: Bind publication to qualified intent
id: package
run: node scripts/ci/prepare-npm-publication.mjs
- name: Publish
if: steps.package.outputs.published == 'false'
env:
NPM_RELEASE_TAG: ${{ steps.package.outputs.tag }}
run: >-
npm publish .cache/verified-package/package.tgz --ignore-scripts
--registry=https://registry.npmjs.org --access=public --tag="$NPM_RELEASE_TAG"
verify-npm:
name: Verify published npm package
needs: [merged-release, release-please, publish]
if: >-
always() && !cancelled() && needs.publish.result == 'success'
runs-on: ubuntu-latest
env:
TURBO_TOKEN: ""
TURBO_CACHE: local:rw
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: 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: |
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
env:
VERIFIED_PACKAGE_VERSION: ${{ steps.package.outputs.version }}
@@ -389,9 +419,12 @@ jobs:
publish-mcp:
name: Publish to MCP Registry
needs: [merged-release, release-please, publish]
env:
TURBO_TOKEN: ""
TURBO_CACHE: local:rw
needs: [merged-release, release-please, verify-npm]
if: >-
always() && !cancelled() && needs.publish.result == 'success' &&
always() && !cancelled() && needs.verify-npm.result == 'success' &&
((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'))
runs-on: ubuntu-latest
+1 -1
View File
@@ -20,7 +20,7 @@ jobs:
# Exercise unavailable-runtime diagnostics, the first supported release,
# and new patches on both supported lines without hand-editing versions.
# The repository-pinned Node 24 version also runs the complete test suite.
node: ["22.x", "24.15.0", "24.x", "26.x"]
node: ["22.x", "24.22.1", "24.x", "26.x"]
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
+1 -1
View File
@@ -1 +1 @@
24.18.0
24.22.1
+1 -1
View File
@@ -1,3 +1,3 @@
{
".": "6.5.0"
".": "6.7.0"
}
+7 -1
View File
@@ -20,6 +20,10 @@ 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.
Before adding complexity for a speculative or vanishingly unlikely race or security edge case, explain the concrete failure mode, likelihood, and complexity cost to the user and get their buy-in. Do not silently expand scope for theoretical robustness.
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.
@@ -57,7 +61,7 @@ do not establish fewer tokens or faster analysis.
## Development
Use [.nvmrc](.nvmrc) and `package.json#packageManager` for the pinned toolchain. Cursor Cloud may place older Node.js first on `PATH`; environment setup installs the pinned toolchain under `/usr/local/bin`.
Use [.nvmrc](.nvmrc) and `package.json#packageManager` for the pinned toolchain. Cursor Cloud may place older Node.js first on `PATH`; environment setup installs the pinned toolchain under `/usr/local/bin`. Prefix `PATH="/usr/local/bin:$PATH"` before `node` and `npm`.
- `npm ci`: install locked dependencies.
- `npm run build:cached`: build the runtime and packaged skill.
@@ -68,6 +72,8 @@ Use [.nvmrc](.nvmrc) and `package.json#packageManager` for the pinned toolchain.
Use relevant checks for the change. The complete local gate, `npm run check:pr`, is optional for broad changes or CI diagnosis. See [CONTRIBUTING.md](CONTRIBUTING.md) for development, generated-file ownership, and release conventions.
Coordinate expensive checks across agents. If a test unexpectedly consumes heavy CPU, stop its owned process family and profile a bounded representative case before retrying. Inspect production work, fixture generation, subprocesses, and repeated setup; preserve regression coverage when optimizing. Follow the [test resource guidance](docs/testing.md#developer-commands).
`docs/public/product-catalog.json`, `docs/verification/managed-conformance-*.json`, and `skills/` are ignored outputs. Edit source contracts and `.agents/skills/reverse-engineer-anything/`, then regenerate as needed. `.cache/mcp-tool-catalog.json` is test metadata; runtime builds and source checking do not consume it. Never commit binaries, provider project documents, credentials, `dist/`, `node_modules/`, or local planning artifacts.
## Tool and Test Changes
+88
View File
@@ -1,5 +1,93 @@
# Changelog
## [6.7.0](https://github.com/morluto/rea/compare/rea-agents-6.6.0...rea-agents-6.7.0) (2026-10-18)
### Highlights
- Client configuration edits follow the duplicate-member semantics used by MCP clients.
### Changes
- Includes the [6.6.0 changes and migration notes](https://github.com/morluto/rea/releases/tag/rea-agents-6.6.0). That version was tagged but was not published to npm.
### Fixes
- **Client configuration:** Setup edits the last repeated JSON/JSONC member, matching the value clients actually read. Setup and uninstall refuse a removal that would reactivate a shadowed duplicate, preserving the original configuration. ([#4957](https://github.com/morluto/rea/pull/4957), [morluto/rea#4952](https://github.com/morluto/rea/issues/4952)). Thanks @kaoru0822-kitauji, @xu91102.
Full contribution record: [4 commits and 1 merged PR](https://github.com/morluto/rea/blob/rea-agents-6.7.0/docs/releases/6.7.0.contributions.json).
## [6.6.0](https://github.com/morluto/rea/compare/rea-agents-6.5.0...rea-agents-6.6.0) (2026-10-18)
### Highlights
- Add Kuna as a caller-supplied static-analysis provider.
- Query selected JavaScript facts and multiple APK locale tables without repeating the underlying analysis.
- Extend Ghidra analysis to Windows ARM PE targets and explicit raw m68k images.
- Preserve completed Evidence, cancellation reasons and retryable cleanup across provider and CLI finalization.
### Changes
- **Kuna analysis:** Use a caller-installed Kuna for procedure listings, pseudocode, assembly, call relationships, exact-address xrefs and cached string searches. Project exports retain source spans and digests; repeated requests reuse completed work. Recorded real-provider coverage uses Kuna 1.833 on Linux; Windows, PE and universal Mach-O are unsupported. See [the provider guide](docs/kuna-provider.md). ([#3832](https://github.com/morluto/rea/pull/3832), [#3854](https://github.com/morluto/rea/pull/3854), [#4071](https://github.com/morluto/rea/pull/4071), [#4095](https://github.com/morluto/rea/pull/4095)). Thanks @gensokyo-dev, @kaoru0822-kitauji, @N0zoM1z0.
- **Retained JavaScript facts:** Query exact application paths, selected properties and filtered module references through `inspect_analysis_view`, with paging and complete row Evidence. These views reuse completed analysis and preserve source selection and unknown coverage. ([#4265](https://github.com/morluto/rea/pull/4265), [morluto/rea#4190](https://github.com/morluto/rea/issues/4190), [#4097](https://github.com/morluto/rea/pull/4097), [morluto/rea#4092](https://github.com/morluto/rea/issues/4092)). Thanks @kaoru0822-kitauji, @N0zoM1z0.
- **APK locale tables:** Request several resource locales in one decode with repeatable CLI `--locales` or MCP `locales`. Each table reports its status and coverage; existing single-locale fields remain available. ([#4804](https://github.com/morluto/rea/pull/4804), [morluto/rea#4777](https://github.com/morluto/rea/issues/4777)). Thanks @abhay-2108, @N0zoM1z0.
- **Windows ARM binaries:** Ghidra admits ARMNT/Thumb-2 and ARM64 PE applications and DLLs on the Windows x64 host lane. Recorded Ghidra 12.1.4 verification uses `WindowsResourceReference` disabled and raised startup budgets; default-analyzer completion, ARM64EC/ARM64X and Windows-on-Arm hosts remain unverified or unsupported. See [the exact scope](docs/windows-ghidra-p0.md#native-authority-and-target-identity). ([#3965](https://github.com/morluto/rea/pull/3965), [morluto/rea#2186](https://github.com/morluto/rea/issues/2186)). Thanks @N0zoM1z0, @WestLabsAI.
- **Raw m68k images:** Analyze explicitly selected big-endian m68k raw binaries using Ghidra’s fixed 68000 profile and a caller-supplied aligned entry. Real CLI/MCP verification used Ghidra 12.1.4 on macOS arm64; Linux is unverified and Windows P0 does not admit m68k. See [mapping requirements](docs/raw-binary.md). ([#4018](https://github.com/morluto/rea/pull/4018)). Thanks @icedac.
- **JADX deadlines:** Set `REA_JADX_OPERATION_TIMEOUT_MS` to extend APK loading and upstream operation deadlines together. Doctor reports the effective value and setup forwards an explicit selection. ([#4466](https://github.com/morluto/rea/pull/4466), [morluto/rea#3918](https://github.com/morluto/rea/issues/3918)). Thanks @jaychou813, @SandstromPL.
- **Electron window options:** Resolve stable local constant `BrowserWindow` options while retaining source provenance. Relevant writes and escapes keep the result unknown. ([#4400](https://github.com/morluto/rea/pull/4400), [morluto/rea#4366](https://github.com/morluto/rea/issues/4366)). Thanks @kaoru0822-kitauji, @N0zoM1z0.
- **Language editions:** Add the Greek website edition and Malayalam documentation, with Korean typography and translation corrections across the site. ([#3520](https://github.com/morluto/rea/pull/3520), [#4792](https://github.com/morluto/rea/pull/4792), [#3821](https://github.com/morluto/rea/pull/3821), [morluto/rea#3820](https://github.com/morluto/rea/issues/3820), [#3799](https://github.com/morluto/rea/pull/3799), [morluto/rea#3798](https://github.com/morluto/rea/issues/3798), [#4344](https://github.com/morluto/rea/pull/4344), [#3856](https://github.com/morluto/rea/pull/3856), [#4166](https://github.com/morluto/rea/pull/4166)). Thanks @gensokyo-dev, @hi2242, @kalayciburak, @kaoru0822-kitauji, @kraken-503, @kukinghan, @N0zoM1z0, @yeseong0412.
### Fixes
- **Cancellation and cleanup:** Keep completed observations, exact cancellation reasons and the original physical failure through finalization. Failed cleanup retains its process or file owner for retry; recovered resources disappear from pending-cleanup reports without replacing the original error. ([#4238](https://github.com/morluto/rea/pull/4238), [morluto/rea#4232](https://github.com/morluto/rea/issues/4232), [#4274](https://github.com/morluto/rea/pull/4274), [morluto/rea#3923](https://github.com/morluto/rea/issues/3923), [3e82366](https://github.com/morluto/rea/commit/3e8236639abffa39deb5d5e939ec8baf6aaf3ca3), [16924e2](https://github.com/morluto/rea/commit/16924e275e939d0543ba01493324959a9e1d731c), [bbfe6ed](https://github.com/morluto/rea/commit/bbfe6edd60290f9bfbec68c8a0bcf605d8bbf5b3), [1f23e97](https://github.com/morluto/rea/commit/1f23e977b50301e91f02a4a17c9f8d48bb7b60f3), [08fbeb8](https://github.com/morluto/rea/commit/08fbeb8bdab27118b6c57039aef7c0fa8b6463bc)). Thanks @coygeek.
- **Provider startup:** Join prerequisite probes and in-flight readiness work before shutdown, preserve typed timeout and JVM-memory failures, and retain startup diagnostics through cleanup. Ghidra recognizes keg-only Homebrew JDKs and names the setting responsible for a startup timeout. ([376eeca](https://github.com/morluto/rea/commit/376eeca770820b31f7f8d4c5fcc397315621643e), [ceeff5a](https://github.com/morluto/rea/commit/ceeff5a41788e47e5aee96a4baf191a6ce7fde4b), [3ad1032](https://github.com/morluto/rea/commit/3ad1032b11edea2dcb054e78f951cc43159ae83d), [964decd](https://github.com/morluto/rea/commit/964decd989acfa84c4bfc3295362c4c46877fc20), [3fa972a](https://github.com/morluto/rea/commit/3fa972af7c7cb6cf014ffc426bdf7130dc6f2fd2), [#4557](https://github.com/morluto/rea/pull/4557), [morluto/rea#4556](https://github.com/morluto/rea/issues/4556), [#4590](https://github.com/morluto/rea/pull/4590), [morluto/rea#4241](https://github.com/morluto/rea/issues/4241)). Thanks @DanielCvz1, @J0UH, @Rinkia, @RishTheFish21, @vadlachary2002.
- **Client configuration:** Preserve the selected launcher, destination file, caller timeout choices and unresolved configuration evidence. Setup/uninstall recovery stays bound to the exact published file, keeps current verified backups, and transfers failed cleanup ownership for retry. ([d0253c9](https://github.com/morluto/rea/commit/d0253c99d65afbbd12732a2fdaf711640574e17f), [7e4eed9](https://github.com/morluto/rea/commit/7e4eed9dd48485b6a7100300aa98401811c2deb9), [2fb5e20](https://github.com/morluto/rea/commit/2fb5e204a35723f6f843d56ca47b0db94d8fcc44), [8235210](https://github.com/morluto/rea/commit/823521085a28f825e7fee1929d852d420042d1f7), [d818c3d](https://github.com/morluto/rea/commit/d818c3d2639d3115edb8a5269a754101aea6c3bd), [19329e4](https://github.com/morluto/rea/commit/19329e43bb38ca9d64512d801b25abcb188d2512), [dadf909](https://github.com/morluto/rea/commit/dadf909dce2e8b3d055541de69b4118d21276316), [333e0ab](https://github.com/morluto/rea/commit/333e0ab0dfc126a9915907d5079ed6195190e38e), [c15c206](https://github.com/morluto/rea/commit/c15c2062ad815cc9a3222ccfaeb7ef53cacd8b72)). Thanks @Adolanium.
- **Configuration formats:** Read signed 64-bit TOML integers without rounding, handle large TOML documents without argument-spread failures, preserve JSONC trivia and Hermes YAML scalar text, and reject NUL bytes in registration environment values. ([#4937](https://github.com/morluto/rea/pull/4937), [morluto/rea#4917](https://github.com/morluto/rea/issues/4917), [c43bc0b](https://github.com/morluto/rea/commit/c43bc0bf66a4e245a0cafa40def0d0207bcbd73c), [4fd28fc](https://github.com/morluto/rea/commit/4fd28fc9e0c4f8358147af38c72ab9e814f52d61), [#3568](https://github.com/morluto/rea/pull/3568), [#4335](https://github.com/morluto/rea/pull/4335)). Thanks @alfredo1491, @gensokyo-dev, @kaoru0822-kitauji, @N0zoM1z0, @ShuaixinHuang, @xu91102.
- **Claude Code configuration:** Setup, doctor and uninstall now accept only the strict JSON format Claude Code reads, including a leading UTF-8 BOM. Comments and trailing commas are refused before backup or writing; clients that support JSONC retain their existing behavior. ([#4944](https://github.com/morluto/rea/pull/4944), [morluto/rea#4940](https://github.com/morluto/rea/issues/4940)). Thanks @kaoru0822-kitauji, @xu91102.
- **CLI output and dispatch:** Accept `--flag=value` and spaced global output values, keep array positions in filtered JSON, validate integer tokens before coercion, and exit quietly when the output reader closes. Cancellation remains effective through command finalization; app bundles and static native inspections select the intended workflow. ([#3811](https://github.com/morluto/rea/pull/3811), [#4823](https://github.com/morluto/rea/pull/4823), [2ae4e16](https://github.com/morluto/rea/commit/2ae4e1608f290c95ff7cff28c4d16a79b301d03d), [8d82579](https://github.com/morluto/rea/commit/8d8257928bbe8d43fedffc4db5fca26c8108820f), [#3781](https://github.com/morluto/rea/pull/3781), [279c590](https://github.com/morluto/rea/commit/279c590ef6bb737d4a48ea696a0ae5eb30010b3d), [#3511](https://github.com/morluto/rea/pull/3511), [morluto/rea#3321](https://github.com/morluto/rea/issues/3321), [f31b231](https://github.com/morluto/rea/commit/f31b231a92a61be6bd1800b16be0b3210ae82944)). Thanks @arjunprabhulal, @coygeek, @kaoru0822-kitauji, @ShuaixinHuang, @Sriram-PR, @Tapraj-Pathak.
- **Updates:** Route `--update` through the verified updater and bound retained package-manager diagnostics. ([4f7b5fb](https://github.com/morluto/rea/commit/4f7b5fb6023ce2ec449561782d09e3c7e5ac7c7b), [01cdbbd](https://github.com/morluto/rea/commit/01cdbbdc556262c47d04a5b3391dd9c17946d3fc)).
- **MCP contracts:** Advertise self-contained tool inputs while preserving tuple, array, Boolean and nested-JSON constraints across schema profiles. Schema-reference handling no longer rewrites literal metadata; catalog identity includes the output-schema profile. ([5e30969](https://github.com/morluto/rea/commit/5e30969cc5f1255d44ec7a1f1ca8dbac0af5e7c4), [10aa74d](https://github.com/morluto/rea/commit/10aa74dfedc3ae776f50d1fa1eb83b64ae82d3d6), [204b9f3](https://github.com/morluto/rea/commit/204b9f3ac140a993c3e458bb0e117de31818ab29), [b3389a5](https://github.com/morluto/rea/commit/b3389a5b0834265fe7a40391be315d810a53a97e), [a2e3016](https://github.com/morluto/rea/commit/a2e30168a046f3b79319eb7fbaf807b802104199), [ba98130](https://github.com/morluto/rea/commit/ba98130049fa40ade66b03611ff70c7ddc011a1d), [c5fb86d](https://github.com/morluto/rea/commit/c5fb86d1948af0a287a581b29b2b1d554743c607)). Thanks @akushonkamen, @HarsH-1877, @kaoru0822-kitauji, @xingzihai.
- **Session ordering:** Keep snapshot admission ahead of later calls on the same connection, retain shutdown signals until cleanup succeeds, and preserve inspection authority through investigation publication. ([#3661](https://github.com/morluto/rea/pull/3661), [morluto/rea#3334](https://github.com/morluto/rea/issues/3334), [8873b39](https://github.com/morluto/rea/commit/8873b39770e89eae22b7693bc492d7e423b22bea), [663d07f](https://github.com/morluto/rea/commit/663d07f02cd62c99d81c59391d2c6d745c875335)). Thanks @coygeek, @rudycelekli, @Swatantra-66.
- **JavaScript exports and resolution:** Retain CommonJS exports created with `Object.defineProperty` and track export receiver lifetimes. Resolve scoped packages, package `main`, JSON requires and `.mts`/`.cts` imports using the owning loader; overwritten `require` bindings and erased type-only imports no longer create false runtime links. ([#4725](https://github.com/morluto/rea/pull/4725), [morluto/rea#4724](https://github.com/morluto/rea/issues/4724), [#3567](https://github.com/morluto/rea/pull/3567), [morluto/rea#3541](https://github.com/morluto/rea/issues/3541), [#4838](https://github.com/morluto/rea/pull/4838), [morluto/rea#4632](https://github.com/morluto/rea/issues/4632), [#3844](https://github.com/morluto/rea/pull/3844), [morluto/rea#3383](https://github.com/morluto/rea/issues/3383), [#4164](https://github.com/morluto/rea/pull/4164), [morluto/rea#3915](https://github.com/morluto/rea/issues/3915), [#4223](https://github.com/morluto/rea/pull/4223), [#3801](https://github.com/morluto/rea/pull/3801), [morluto/rea#3393](https://github.com/morluto/rea/issues/3393), [#4165](https://github.com/morluto/rea/pull/4165), [morluto/rea#3932](https://github.com/morluto/rea/issues/3932)). Thanks @Abishek0070, @coygeek, @JNTMTMTM, @kaoru0822-kitauji, @prasad2512, @rodricksz4h5, @xu91102.
- **JavaScript value flow:** Preserve values captured before later assignments, guarded factory fallthrough and nested destructuring. Track exact property keys and setter writes; direct `eval`, compound assignments and incoming callbacks preserve unresolved effects instead of inventing certainty. ([#4196](https://github.com/morluto/rea/pull/4196), [morluto/rea#3881](https://github.com/morluto/rea/issues/3881), [#4341](https://github.com/morluto/rea/pull/4341), [morluto/rea#3963](https://github.com/morluto/rea/issues/3963), [bfb7ed0](https://github.com/morluto/rea/commit/bfb7ed0bd43edc605e00fc52c6e21e735b285904), [#3480](https://github.com/morluto/rea/pull/3480), [#3831](https://github.com/morluto/rea/pull/3831), [morluto/rea#3312](https://github.com/morluto/rea/issues/3312), [9ac76d3](https://github.com/morluto/rea/commit/9ac76d3d9486856d25395b413d7ac5ffa744cad0), [#4342](https://github.com/morluto/rea/pull/4342), [morluto/rea#4206](https://github.com/morluto/rea/issues/4206), [#4243](https://github.com/morluto/rea/pull/4243), [morluto/rea#3880](https://github.com/morluto/rea/issues/3880)). Thanks @coygeek, @kaoru0822-kitauji, @maxazure, @N0zoM1z0, @ShuaixinHuang.
- **JavaScript relationships:** Distinguish `Map` reads from network endpoints, associate response endpoints only with writes that reach the read, and preserve child-process receiver ownership and effective stdio choices. Version comparisons keep relocation and unknown relationships distinct from application changes. ([fbfdb6a](https://github.com/morluto/rea/commit/fbfdb6abe9d663be0be146a15a489e33ccba0f68), [#4759](https://github.com/morluto/rea/pull/4759), [morluto/rea#4606](https://github.com/morluto/rea/issues/4606), [#3819](https://github.com/morluto/rea/pull/3819), [morluto/rea#3362](https://github.com/morluto/rea/issues/3362), [#4336](https://github.com/morluto/rea/pull/4336), [morluto/rea#3355](https://github.com/morluto/rea/issues/3355), [556504e](https://github.com/morluto/rea/commit/556504edcbb0f8cd9eaf81a91b18ff891b8f5457), [4d980d0](https://github.com/morluto/rea/commit/4d980d017622d3175d86aaf3b0dcfbdf4ca3d091)). Thanks @Abdooo2235, @coygeek, @kaoru0822-kitauji, @Xinglan233.
- **JavaScript source admission:** Parse JavaScript with its own grammar, refuse FIFO recovery inputs without blocking, and report oversized or resource-skipped files with their actual capacity and coverage state. Malformed runtime events stay excluded rather than becoming policy-filtered facts. ([#3782](https://github.com/morluto/rea/pull/3782), [morluto/rea#3780](https://github.com/morluto/rea/issues/3780), [#3788](https://github.com/morluto/rea/pull/3788), [morluto/rea#3332](https://github.com/morluto/rea/issues/3332), [#4757](https://github.com/morluto/rea/pull/4757), [morluto/rea#4629](https://github.com/morluto/rea/issues/4629), [#3852](https://github.com/morluto/rea/pull/3852), [morluto/rea#3670](https://github.com/morluto/rea/issues/3670), [#4755](https://github.com/morluto/rea/pull/4755), [morluto/rea#4685](https://github.com/morluto/rea/issues/4685)). Thanks @andresaragon, @coygeek, @kaoru0822-kitauji, @sx4im.
- **Browser capture:** Bound CDP handshakes and command responses, preserve malformed inventory uncertainty and partial cleanup diagnostics, and report cancellation after content reads and final cleanup. Accessibility comparisons require observed coverage; attached emulation teardown limits remain explicit. ([#3555](https://github.com/morluto/rea/pull/3555), [morluto/rea#3553](https://github.com/morluto/rea/issues/3553), [#3901](https://github.com/morluto/rea/pull/3901), [morluto/rea#3379](https://github.com/morluto/rea/issues/3379), [4cc044b](https://github.com/morluto/rea/commit/4cc044be68f813e96907e6cedcf422d0a3170de3), [#4406](https://github.com/morluto/rea/pull/4406), [morluto/rea#4403](https://github.com/morluto/rea/issues/4403), [#2893](https://github.com/morluto/rea/pull/2893), [0123d59](https://github.com/morluto/rea/commit/0123d5976828bfe621bd51daa4cf39fee48a1349), [2ff0867](https://github.com/morluto/rea/commit/2ff086796ad7b8f60397956750584e170dbe2910)). Thanks @coygeek, @dakjdakd, @kaoru0822-kitauji, @maxazure, @N0zoM1z0, @YunzhouLi-hub.
- **Browser page facts:** Preserve URL path bytes when removing credentials, repeated document `Link` headers and complete COOP/COEP structured items. WebMCP discovery reads the actual schema properties and exact receiver path. ([#4731](https://github.com/morluto/rea/pull/4731), [df2fc25](https://github.com/morluto/rea/commit/df2fc251e318a1255309dba232f9f73c700777b6), [c360cfc](https://github.com/morluto/rea/commit/c360cfcd6d7901514b16dd6113eba2e0b0fdf422), [ebd5a82](https://github.com/morluto/rea/commit/ebd5a82c439f6deb3a06062225ce009eb03257cf), [38f92d1](https://github.com/morluto/rea/commit/38f92d108e3148cdf47e4ffbfe3de8a76e795d51)). Thanks @Abdooo2235, @geekwolf, @ShuaixinHuang.
- **Electron runtime capture:** Keep native IPC listener and one-shot dispatch behavior, block synchronous shell effects during capture, refuse active scenarios when the inspect fuse is disabled, and preserve fuse/transport cleanup ownership. ([#4389](https://github.com/morluto/rea/pull/4389), [morluto/rea#4390](https://github.com/morluto/rea/issues/4390), [#4605](https://github.com/morluto/rea/pull/4605), [morluto/rea#4593](https://github.com/morluto/rea/issues/4593), [#4562](https://github.com/morluto/rea/pull/4562), [morluto/rea#4393](https://github.com/morluto/rea/issues/4393), [#3605](https://github.com/morluto/rea/pull/3605), [morluto/rea#3453](https://github.com/morluto/rea/issues/3453), [f9de413](https://github.com/morluto/rea/commit/f9de41371f7536e2e22ce6e43c9314fa2f238a30), [e7df00e](https://github.com/morluto/rea/commit/e7df00e80777757b329bbe8aba6d421d8892b9d5)). Thanks @Adolanium, @gensokyo-dev, @gkobayashi0, @HarsH-1877, @kaoru0822-kitauji, @keeponlight, @N0zoM1z0, @Nitin13-git, @xu91102.
- **APK resource accuracy:** Retain completed tables and decode facts after interrupted or failed reads. Derive locale limits from resource configuration fields, disclose unselectable qualified tables, distinguish document failures from omitted entries, and bind Evidence to the decoded snapshot. ([34a88d6](https://github.com/morluto/rea/commit/34a88d6c1674710953e2c94bd0a17332cd69ca6f), [f5fcf52](https://github.com/morluto/rea/commit/f5fcf5216ffb3a266b8c00f6d9a0943051084ab5), [6385ef4](https://github.com/morluto/rea/commit/6385ef4355d7a794b3154ee38529cbddee583336), [98886ad](https://github.com/morluto/rea/commit/98886adddfcbd7b4f4236b3a9825f91878c3cb11), [dc2e811](https://github.com/morluto/rea/commit/dc2e8117b60c4481372ed89b077e6c1cf81ee653), [#4771](https://github.com/morluto/rea/pull/4771), [morluto/rea#4768](https://github.com/morluto/rea/issues/4768)). Thanks @abhay-2108, @gensokyo-dev, @Iyanuoluwa007, @kaoru0822-kitauji, @N0zoM1z0.
- **Apktool guidance:** Retain the observed version and disclose the matching platform/release advisory windows without treating a version string as proof of decoder safety. Missing prerequisites and decode refusals identify the selected APK or launcher. ([d38f099](https://github.com/morluto/rea/commit/d38f099692394ec90c582f9f0a14b036b421b160), [63696d2](https://github.com/morluto/rea/commit/63696d277328f556c971d1fc9f81ae83128d468b), [cc038da](https://github.com/morluto/rea/commit/cc038da218cada68c87700477aba3db4739791ff)). Thanks @GovinduSasanka, @peter-research.
- **Android method bodies:** Recover original bytecode bodies when Java source is omitted while preserving genuine native/abstract body absence and primary failures during retryable cleanup. ([63c65a2](https://github.com/morluto/rea/commit/63c65a22430a4bbc0c1807a0298edb867775001f), [742b6f5](https://github.com/morluto/rea/commit/742b6f587f3c4ef1b8212e7376d6f3a305b194dc), [9fa6d2b](https://github.com/morluto/rea/commit/9fa6d2b254cc390665974efb52463386a029e9fc)). Thanks @balkan16, @Chessing234.
- **ADB observations and transfers:** Preserve display-scoped focus, distinguish missing declarations from absent focus, retain numeric physical density and mark unknown permission coverage partial. Transfers use consumed snapshot identity and refuse unknown destination existence; uninstall requests explicitly select user zero. ([7086768](https://github.com/morluto/rea/commit/7086768a27242a15daa93486aefbaacc3c891e78), [3d028db](https://github.com/morluto/rea/commit/3d028dbc74a465f6f8df1813a931fbb5362e7dc3), [#4074](https://github.com/morluto/rea/pull/4074), [morluto/rea#3942](https://github.com/morluto/rea/issues/3942), [#4663](https://github.com/morluto/rea/pull/4663), [#4665](https://github.com/morluto/rea/pull/4665), [1342127](https://github.com/morluto/rea/commit/1342127e91d4db8b4113a7a4e85be909bc96e1db), [61e6880](https://github.com/morluto/rea/commit/61e688070f48016b6d178cd04c02fcc2f33e19ad), [be0b2d1](https://github.com/morluto/rea/commit/be0b2d138c46bb951fc3c8ee57ef7b0d57fed312)). Thanks @Copilot, @coygeek, @gongfpp, @Tapraj-Pathak, @yankikucuk.
- **Apple metadata:** Preserve exact plist/keyed-archive integers, negative zero, non-finite real identity and dictionary keys. Reject malformed XML before value decoding, retain undecodable dates as unknown and report unsupported OpenStep entries explicitly. ([#4324](https://github.com/morluto/rea/pull/4324), [morluto/rea#4292](https://github.com/morluto/rea/issues/4292), [2cf9c28](https://github.com/morluto/rea/commit/2cf9c28f27261b7e64af2ba7ecb4e23ea7714aee), [#4839](https://github.com/morluto/rea/pull/4839), [morluto/rea#4834](https://github.com/morluto/rea/issues/4834), [f2b7bbb](https://github.com/morluto/rea/commit/f2b7bbb31279a820f4cf32d439570f8e5381d010), [6c98708](https://github.com/morluto/rea/commit/6c98708793c23615de8053b2cd14d9f164b960c5), [78cf610](https://github.com/morluto/rea/commit/78cf6100f33ad2412c0a3d60b83f8a44a43fbbb5), [c7e9e09](https://github.com/morluto/rea/commit/c7e9e096d3e2444e4c251123b8e914e9c0941f4e), [#3793](https://github.com/morluto/rea/pull/3793), [#4105](https://github.com/morluto/rea/pull/4105), [morluto/rea#3892](https://github.com/morluto/rea/issues/3892)). Thanks @coygeek, @kaoru0822-kitauji, @mertkayacs, @pablodiazjorge, @pollychen-lab, @xu91102.
- **Apple application structure:** Derive bundle components and launchd domains from their owning directories, retain unavailable components, and select only exact `Assets.car` names as asset catalogs. Interface Builder keeps unresolved classes and counts hierarchy records beyond the returned object page. ([149a0fc](https://github.com/morluto/rea/commit/149a0fc06c3c67ee45624e6da14dfb0754473758), [5d6029e](https://github.com/morluto/rea/commit/5d6029ea62a65ec34fd739ee40223c77c6fdb63b), [c192c58](https://github.com/morluto/rea/commit/c192c58815ddeaf5cf30159e7082c742c0bc36b7), [#4094](https://github.com/morluto/rea/pull/4094), [morluto/rea#3914](https://github.com/morluto/rea/issues/3914), [#4118](https://github.com/morluto/rea/pull/4118), [morluto/rea#3958](https://github.com/morluto/rea/issues/3958), [#4162](https://github.com/morluto/rea/pull/4162), [morluto/rea#3950](https://github.com/morluto/rea/issues/3950)). Thanks @coygeek, @kaoru0822-kitauji.
- **Native names and layout:** Keep valid Objective-C records beside malformed neighbors, preserve leading U+FEFF and repeated load-command fields, and select exact arm64e.x1 slices. PE resource ownership and NativeAOT reads stay within declared image/header ranges. ([#3833](https://github.com/morluto/rea/pull/3833), [morluto/rea#3336](https://github.com/morluto/rea/issues/3336), [#4549](https://github.com/morluto/rea/pull/4549), [morluto/rea#4540](https://github.com/morluto/rea/issues/4540), [morluto/rea#4541](https://github.com/morluto/rea/issues/4541), [#4730](https://github.com/morluto/rea/pull/4730), [#4048](https://github.com/morluto/rea/pull/4048), [morluto/rea#4008](https://github.com/morluto/rea/issues/4008), [#3500](https://github.com/morluto/rea/pull/3500), [morluto/rea#3389](https://github.com/morluto/rea/issues/3389), [#4463](https://github.com/morluto/rea/pull/4463), [morluto/rea#4373](https://github.com/morluto/rea/issues/4373), [morluto/rea#4455](https://github.com/morluto/rea/issues/4455), [3fc0075](https://github.com/morluto/rea/commit/3fc007534bb2104d662b43cc712281f9d1d6a7d3), [4a076f3](https://github.com/morluto/rea/commit/4a076f368c62bdfb19415accad342e03a38d1005)). Thanks @coygeek, @Iyanuoluwa007, @jayinvers, @kaoru0822-kitauji, @kevinjosh10, @maxazure, @ShuaixinHuang.
- **Native analysis providers:** Keep Ghidra byte matches within examined coverage, retain PE PDB/function-boundary limitations and preserve configured MIPS choices. Attached IDA refuses universal Mach-O rather than silently choosing a slice; request timeout identity and captured output survive adapter handling. ([#4531](https://github.com/morluto/rea/pull/4531), [#2933](https://github.com/morluto/rea/pull/2933), [morluto/rea#2259](https://github.com/morluto/rea/issues/2259), [01a1b3f](https://github.com/morluto/rea/commit/01a1b3fda6a7dacd24a0b10619d41d12153080d4), [50dd9fc](https://github.com/morluto/rea/commit/50dd9fcad1efb47cb404192c30bdf190dcdaca58), [#4765](https://github.com/morluto/rea/pull/4765), [morluto/rea#3871](https://github.com/morluto/rea/issues/3871), [c0cfccb](https://github.com/morluto/rea/commit/c0cfccb45fe43e7eeff18e56c64caa5b121988f9)). Thanks @BeLit-Development-2, @coygeek, @kaoru0822-kitauji, @N0zoM1z0, @ShuaixinHuang.
- **Recorded crashes:** Prevent GDB from discovering an executable named by an untrusted core, reject core mapping paths that escape the isolated sysroot, and preserve pwntools worker ownership across launcher exec. Completed decode facts and cancellation reasons remain available through failed final cleanup. ([5bf4fff](https://github.com/morluto/rea/commit/5bf4fffc1b835130ddb16f96db9ce13b639591db), [b33c0df](https://github.com/morluto/rea/commit/b33c0df02e93fabca00a44f2de625b1a63637ee9), [#4435](https://github.com/morluto/rea/pull/4435), [morluto/rea#4011](https://github.com/morluto/rea/issues/4011)). Thanks @coygeek, @N0zoM1z0.
- **Cutter and Rizin:** Read Cutter’s active document from core configuration and attribute its host version correctly. Keep interrupted Rizin frames and output whitespace before completion markers, with exact command and cleanup failure attribution. ([#4507](https://github.com/morluto/rea/pull/4507), [morluto/rea#4497](https://github.com/morluto/rea/issues/4497), [#4514](https://github.com/morluto/rea/pull/4514), [morluto/rea#4498](https://github.com/morluto/rea/issues/4498), [#4459](https://github.com/morluto/rea/pull/4459), [morluto/rea#4015](https://github.com/morluto/rea/issues/4015), [d5fc7e6](https://github.com/morluto/rea/commit/d5fc7e6ebf9af050081fc4c6e18b9079cfaa5cdc), [199396f](https://github.com/morluto/rea/commit/199396f61c8f61da326d059606a992304a760147)). Thanks @coygeek, @N0zoM1z0, @Xander-AJ.
- **Windows cleanup:** Retain unpublished runtimes after failed removal, handle read-only attributes and sharing conflicts, preserve primary snapshot failures and honor caller environment overrides. ([f78d137](https://github.com/morluto/rea/commit/f78d137b42741a0ed5fb8ac161286f9a50220c4d), [d69acc3](https://github.com/morluto/rea/commit/d69acc3455c33f90a2fc4381db43d38b6af50622), [0679a79](https://github.com/morluto/rea/commit/0679a7972b4f2b02eee6a86a44f50f4c2967d3cb), [5f9ac2c](https://github.com/morluto/rea/commit/5f9ac2c98a6fe11988c41afb737df9b6d5c3a1b3), [1bbf4bd](https://github.com/morluto/rea/commit/1bbf4bd16ca134cdb2187eaf29291cae90d14a57)). Thanks @DeryFerd.
- **Managed metadata:** Compare nominal signatures and declaring identities across builds, preserve empty names without display substitutions, verify ABI-prefixed Mach-O exports exactly, and charge cached metadata once. Metadata errors report artifact file offsets; mixed-mode assemblies retain their actual boundary classification. ([#3059](https://github.com/morluto/rea/pull/3059), [#3079](https://github.com/morluto/rea/pull/3079), [#3853](https://github.com/morluto/rea/pull/3853), [morluto/rea#3352](https://github.com/morluto/rea/issues/3352), [#3974](https://github.com/morluto/rea/pull/3974), [morluto/rea#3961](https://github.com/morluto/rea/issues/3961), [#3491](https://github.com/morluto/rea/pull/3491), [morluto/rea#3449](https://github.com/morluto/rea/issues/3449), [#4418](https://github.com/morluto/rea/pull/4418), [morluto/rea#4372](https://github.com/morluto/rea/issues/4372), [#3516](https://github.com/morluto/rea/pull/3516), [morluto/rea#3366](https://github.com/morluto/rea/issues/3366)). Thanks @8rulerstar, @coygeek, @duckhunt113, @Hahaa909, @JayOfTheKeyboard, @kaoru0822-kitauji, @keeponlight, @N0zoM1z0, @tomyrms.
- **Archive and filesystem identity:** Reject colliding extraction paths before writing, classify ZIP offsets beyond EOF as malformed, normalize ZIP current-directory prefixes once, and preserve symlink and unreadable-path provenance. A directory named `*.map` is no longer mistaken for a source-map file. ([862a0b2](https://github.com/morluto/rea/commit/862a0b2631785492f8dc20c8acd396646c545fd1), [c209dcb](https://github.com/morluto/rea/commit/c209dcb209118069f123e789949eeba1ef78853e), [6b1c36e](https://github.com/morluto/rea/commit/6b1c36e336eeacbb7159b211e687b4b84f99b9f5), [25c2222](https://github.com/morluto/rea/commit/25c22222abe7a681491bdb4d7316250fdcf32973), [d6e4fe4](https://github.com/morluto/rea/commit/d6e4fe4a4dd8914ec3db50d2a7fc3d0802649262), [#4131](https://github.com/morluto/rea/pull/4131), [morluto/rea#3909](https://github.com/morluto/rea/issues/3909)). Thanks @Adkid-Zephyr, @coygeek, @jackie-cqz, @kaoru0822-kitauji, @maxazure, @XY-code1.
- **Evidence and reconstruction:** Authenticate all retained inventory substeps, preserve source-attributed caveats in composed workflows, and require comparable cited Evidence before declaring reconstruction coverage or readiness. Missing Evidence and residual replay uncertainty stay explicit. ([9ac91e0](https://github.com/morluto/rea/commit/9ac91e0c1e13e91cd21341635ad5be89b0e8642d), [c4608a2](https://github.com/morluto/rea/commit/c4608a29620ea3e7bb8259b47357b942fe4e4709), [#4075](https://github.com/morluto/rea/pull/4075), [morluto/rea#3357](https://github.com/morluto/rea/issues/3357), [#3526](https://github.com/morluto/rea/pull/3526), [morluto/rea#3363](https://github.com/morluto/rea/issues/3363), [153017d](https://github.com/morluto/rea/commit/153017d750322188c1a112b2284c1b8065c67320)). Thanks @coygeek, @gensokyo-dev, @kaoru0822-kitauji, @N0zoM1z0, @rudycelekli.
- **Offline data:** Preserve exact UTC packet-capture instants, SQLite column identity and snapshot admission failures, and cooked WASM directive literals. Cleanup cancellation retains completed EVM/WASM observations rather than losing them. ([796faa0](https://github.com/morluto/rea/commit/796faa0e4e6bf745bcdc33acef65af8ccfc3622d), [748ac7f](https://github.com/morluto/rea/commit/748ac7f0df340f0d0569eca2e803a59cd51ffdbd), [bc51d0e](https://github.com/morluto/rea/commit/bc51d0ea5df7dcee1fcc42dfae3ab0c62c9cca5e), [4f87f02](https://github.com/morluto/rea/commit/4f87f02bb19bf77349a683a5bcb2da2d1da427d0), [d694aef](https://github.com/morluto/rea/commit/d694aef227e90164e6654ea9e5ce6a854d099404), [321cacb](https://github.com/morluto/rea/commit/321cacb84a020f447e1110df5c58fa71d99b7bf1)). Thanks @himamaheswarreddy, @mertkayacs, @rollroyces, @viniciusdsandrade.
- **Kuna recovery:** Share concurrent single-function requests and cache repeats, validate warm exports once, stream string inventories beyond the former fixed buffer ceiling, and keep unproven processes in their run slots until close confirms release. Failure messages distinguish final buffer refusals from configurable deadlines. ([#4045](https://github.com/morluto/rea/pull/4045), [#4169](https://github.com/morluto/rea/pull/4169), [#4049](https://github.com/morluto/rea/pull/4049), [#4318](https://github.com/morluto/rea/pull/4318), [#4052](https://github.com/morluto/rea/pull/4052), [#4098](https://github.com/morluto/rea/pull/4098), [#4343](https://github.com/morluto/rea/pull/4343)). Thanks @kaoru0822-kitauji.
- **Flutter snapshots:** Check symbol-table ranges against loadable segments and retain completed inspection facts when cancellation occurs during snapshot cleanup. ([#3859](https://github.com/morluto/rea/pull/3859), [morluto/rea#3346](https://github.com/morluto/rea/issues/3346), [#4766](https://github.com/morluto/rea/pull/4766), [morluto/rea#4717](https://github.com/morluto/rea/issues/4717), [#4762](https://github.com/morluto/rea/pull/4762), [morluto/rea#4640](https://github.com/morluto/rea/issues/4640)). Thanks @coygeek, @kaoru0822-kitauji.
- **Native metadata recovery:** Keep unknown Objective-C category classes, empty protocol names, undecoded Swift aliases and unfinished property types explicit instead of discarding neighboring valid records. Preserve unnamed segment identity and exclude a dylib’s own install name from dependencies. ([#4093](https://github.com/morluto/rea/pull/4093), [morluto/rea#4012](https://github.com/morluto/rea/issues/4012), [#4078](https://github.com/morluto/rea/pull/4078), [morluto/rea#3873](https://github.com/morluto/rea/issues/3873), [#4085](https://github.com/morluto/rea/pull/4085), [morluto/rea#3934](https://github.com/morluto/rea/issues/3934), [#3825](https://github.com/morluto/rea/pull/3825), [morluto/rea#3345](https://github.com/morluto/rea/issues/3345), [#4051](https://github.com/morluto/rea/pull/4051), [morluto/rea#3875](https://github.com/morluto/rea/issues/3875), [#4050](https://github.com/morluto/rea/pull/4050), [morluto/rea#3872](https://github.com/morluto/rea/issues/3872)). Thanks @coygeek, @kaoru0822-kitauji.
- **Document selection:** Preserve explicitly selected plist paths and bundle defaults, and only use document fallback after confirming its applicability. Finalization keeps completed selection facts and cancellation outcomes. ([e6c4877](https://github.com/morluto/rea/commit/e6c487799bb64f559e66b69be1863e6ec5187548), [#3774](https://github.com/morluto/rea/pull/3774), [9df559b](https://github.com/morluto/rea/commit/9df559b7e712298a761ff26e9caae8d659b7e54b)). Thanks @kaoru0822-kitauji.
- **Capture and comparison accuracy:** Admit schema-valid HAR numeric values while retaining exact-publication limits, preserve empty URL path segments, and keep unrelated process traces and unnamed reference endpoints distinct or unknown. ([#3159](https://github.com/morluto/rea/pull/3159), [morluto/rea#3145](https://github.com/morluto/rea/issues/3145), [#3792](https://github.com/morluto/rea/pull/3792), [morluto/rea#3307](https://github.com/morluto/rea/issues/3307), [#4178](https://github.com/morluto/rea/pull/4178), [morluto/rea#3931](https://github.com/morluto/rea/issues/3931), [#3809](https://github.com/morluto/rea/pull/3809), [morluto/rea#3388](https://github.com/morluto/rea/issues/3388)). Thanks @coygeek, @gensokyo-dev, @kaoru0822-kitauji, @N0zoM1z0, @pablodiazjorge, @xu91102.
- **Evidence publication:** Recheck staged output identity after its final write, classify missing review Evidence and duplicate ledger entries as caller input, and preserve the selected freshness requirement in comparison parameters. ([#4764](https://github.com/morluto/rea/pull/4764), [morluto/rea#4613](https://github.com/morluto/rea/issues/4613), [#4452](https://github.com/morluto/rea/pull/4452), [morluto/rea#4444](https://github.com/morluto/rea/issues/4444), [#4065](https://github.com/morluto/rea/pull/4065), [morluto/rea#4000](https://github.com/morluto/rea/issues/4000)). Thanks @amyanger, @coygeek, @kaoru0822-kitauji, @Rinkia, @Swatantra-66.
### ⚠ BREAKING CHANGES
- **Saved Evidence:** Evidence and analysis snapshots now require explicit nullable `relationship_coverage` in their identity commitments. Artifact inventories require scoped coverage and explicit `integrity_contradictions`; obsolete `coverage_status` and prose-based coverage inference are removed. Regenerate old Evidence/snapshots and inventory observations with this release: saved files are not migrated and omitted facts are not filled with guessed defaults. ([db8128d](https://github.com/morluto/rea/commit/db8128dc0d1c4f731f3d19877fa00d25cf17f657), [0405c9d](https://github.com/morluto/rea/commit/0405c9d361de6d904dfa16cc0b6d8350407f2c9b), [a9588be](https://github.com/morluto/rea/commit/a9588be4ebdd20eeda975cb63ffcdb1cd868b340)).
- **Browser capture results:** Consumers must retain resolved capture limits, event families and omission facts. Set desired budgets through the supported capture options; omitted caller inputs still select defaults, but stored observations must report the resolved values. Admitted content reads queue behind bounded concurrency. ([57a23f0](https://github.com/morluto/rea/commit/57a23f0a2da0fdc3835e203718ded8acea5a34e6)).
- **Android version codes:** `inspect_adb_package.version_code` is a decimal string rather than a JSON number. Read it as a string, or use `BigInt` for numeric comparison. ([#3485](https://github.com/morluto/rea/pull/3485), [morluto/rea#3368](https://github.com/morluto/rea/issues/3368), [7666a48](https://github.com/morluto/rea/commit/7666a482b69dc7cabdbac3bbaf89a43e32300192)). Thanks @coygeek, @kaoru0822-kitauji.
- **Native UI failures:** Failed or cancelled native UI scenarios now return typed errors with the collected report in `partial_observation`. Handle the error path and read that report instead of expecting a successful result containing failed steps. ([d909919](https://github.com/morluto/rea/commit/d9099190ab3a819bd3a9fd62a8714cef398016d5)).
- **Provider status:** `binary_session` candidate availability can be unknown during owner finalization. Preserve that state rather than treating it as unavailable; internal consumers must await the asynchronous session status API. ([150e996](https://github.com/morluto/rea/commit/150e996a86f2bec4982ba87f37bf502a8d5e8214)).
- **Swift symbol locations:** `swift_symbols` entries may have a null `address` when the provider supplies no location. Handle unknown addresses; optional `evidence.reported_address` retains the original provider value. ([8116dc0](https://github.com/morluto/rea/commit/8116dc06512b4609d8db63cebc9f2ff8cb964151)).
- **Linux Hopper admission:** Older builds previously admitted by a fixed allowlist need retained publisher-signed package evidence or an approved update to the current public demo. Update explicit `HOPPER_LAUNCHER_PATH` selections when migrating; the live feed offers only its current release. The official RPM metadata mismatch remains unresolved and is refused. ([#3590](https://github.com/morluto/rea/pull/3590), [morluto/rea#3577](https://github.com/morluto/rea/issues/3577), [70cd4cb](https://github.com/morluto/rea/commit/70cd4cbfb849b61f05d97d2690372a9782831426)). Thanks @gensokyo-dev, @kaoru0822-kitauji, @N0zoM1z0.
Full contribution record: [571 commits and 236 merged PRs](https://github.com/morluto/rea/blob/rea-agents-6.6.0/docs/releases/6.6.0.contributions.json).
## [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).
+39 -1
View File
@@ -21,8 +21,13 @@ For analysis workflows and agent interfaces, follow the
[performance priorities](AGENTS.md#performance). Explain the selected output
scope, reuse/cache decisions, and relevant before/after measurements when these
costs change.
Use the [analysis optimization skill](.agents/skills/rea-analysis-optimization/SKILL.md)
to locate provider and shared-workflow bottlenecks, preserve Evidence, and
compare equivalent real tasks before claiming an improvement.
When adding or changing an MCP tool, follow the [tool design guide](docs/tool-design.md) and preserve the canonical contracts and generated catalog.
When adding or changing an admission ceiling, budget, or deadline, follow
[admission limits and recovery](docs/design/limits-and-recovery.md).
## Reporting issues
@@ -225,7 +230,10 @@ 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.
this includes `run-exclusive` commands, which use the native Job Object to keep
the artifact lock until every command descendant has exited. Without the
artifacts, 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,
@@ -233,6 +241,19 @@ 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.
The general source suite (`npm run test:fast`, `npm run test:changed`, and
`npm run test:local`) runs on Linux in CI, and Windows CI runs a curated lane.
On a Windows checkout, some source tests fail because fixtures use POSIX paths,
`#!/bin/sh` tool stubs, or POSIX rename semantics. Compare failures against the
base revision, or run the suite on Linux or WSL, before attributing them to a
change. See [testing](docs/testing.md#developer-commands) for command scopes
and lane selection.
`git submodule update --init --recursive` fails at the pinned Unblob submodule
on Windows and can leave later submodules unchecked out. Apply the
[Unblob checkout workaround](docs/firmware-analysis.md#windows-submodule-checkout),
then rerun the command.
## Documentation website
The VitePress site uses the Markdown files in `docs/`. Run `npm run docs:dev`
@@ -255,6 +276,23 @@ The separate GitHub Pages host uses only the manual
**Settings → Pages → Build and deployment → Source** must be set to
**GitHub Actions** before deploying there.
### Translation corrections
README editions and website editions have separate translation sources:
- Find the language's `readme` path in the
[language inventory](website/locales/languages.json) and edit that file.
Locale codes and README suffixes can differ. Use the
[English README](README.md) to check commands, requirements, and analysis claims.
- Edit `msgstr` values in `website/locales/<language>/messages.po` for website
text. Follow the [website translation guide](website/locales/README.md) for
the Python environment, catalog validation, and local preview. Preserve source
strings, contexts, placeholders, commands, paths, and recorded evidence.
Before submitting a website translation, run the catalog and website checks in
that guide. Translated HTML in `website/_site/` is generated; edit the PO catalog
rather than the generated pages.
## Development feedback and PR verification
For routine merges, wait for the required checks to pass for the current PR
+52 -28
View File
@@ -1,7 +1,7 @@
<div align="center">
**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)
· [Ελληνικά](docs/readme/README_el.md) · [മലയാളം](docs/readme/README_ml.md) · [Roman Urdu](docs/readme/README_ur-Latn.md)
# REA: Reverse Engineer Anything
@@ -90,6 +90,12 @@ Setup supports Claude Code, Codex, Cursor, Gemini CLI, Grok Build and
[installation and setup](docs/installation.md) for provider configuration and
manual MCP registration.
MCP uses compact input schemas by default to reduce context overhead. Set
`REA_MCP_INPUT_SCHEMA_PROFILE=full` in your MCP registration when you need the
complete advertised field guidance and nested validation details. Both profiles
keep the full tool set and canonical server validation. See
"Compact input schema profile" in the MCP contracts guide.
### Ask your agent
```text
@@ -132,6 +138,14 @@ For a CLI installed with `npm install --global` or `bun add --global`:
rea update
```
`rea --update` is an alias for this verified workflow and accepts the same global
output flags. Use `rea update` when combining it with other command controls.
Updater commands retain at most 4 MiB of stdout and stderr combined. Oversized
metadata replies are rejected after the command exits; verbose installs continue
with bounded diagnostics. Human progress output respects terminal backpressure
and waits for submitted writes to finish.
Bun global updates require Bun 1.2.17 or later. With an older Bun release,
upgrade Bun manually before running `rea update`.
@@ -163,34 +177,44 @@ CLI commands use the same workflows.
## What you can analyze
REA requires Node.js 22.x (>=22.19), 24.x (>=24.11), or 26+, plus npm.
REA requires Node.js 22.x (>=22.23.4), 24.x (>=24.22.1), or 26.12.1+, plus npm.
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](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; 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) |
| Android devices | Devices, packages, processes, logs, dumps, screen captures, and APK/file transfers | Caller-supplied adb (emulator or hardware); [ADB guide](docs/adb-device-analysis.md) |
| JEB projects | Engine-backed project units, artifact digests and type/method pseudo-code | A caller-started JEB client serving MCP, selected with REA_JEB_MCP_URL; [JEB guide](docs/jeb-analysis.md) |
| Android resources | Decoded manifest, string tables, locales and version facts | Caller-supplied Apktool launcher; [resource guide](docs/apktool-resource-analysis.md) |
| Flutter apps | Dart snapshot hash, per-ABI library digests and engine build provenance | None (static APK parsing); [Flutter guide](docs/flutter-analysis.md) |
| Firmware | Regions, extraction results and native-analysis handoffs | Binwalk / Unblob on Linux; [firmware guide](docs/firmware-analysis.md) |
| Recorded packets | Selected fields, repeated values, filters and decode-as rules | Linux: TShark 4.x + prlimit; [capture guide](docs/packet-captures.md) |
| Packet summaries | Endpoints, DNS, TLS SNI, protocol distribution and HTTP requests | Caller-supplied tshark and capinfos (Linux-verified); [pcap guide](docs/pcap-analysis.md) |
| Recorded logic signals | Decoder annotations, sample ranges and diagnostics | Linux: sigrok-cli 0.7.2 + prlimit; [signal recordings](docs/signal-recordings.md) |
| Packages and resources | File inventories, digests, plists, Apple bundle anatomy and extracted resources | [Artifact and JavaScript guide](docs/javascript-artifact-reconstruction.md), [Apple applications](docs/apple-application-analysis.md) |
| Process behavior | Terminal output, interactions, exit and filesystem observations, and run comparisons | Linux/macOS with a native PTY; [process capture](docs/process-capture.md) |
| Target | What REA returns | Requirements and guide |
| ----------------------- | ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| Desktop installations | Runtime candidates, supporting files, component markers and next probes | Built-in bounded directory/header inspection; [desktop applications](docs/desktop-applications.md) |
| 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 |
| GPU code objects | CUDA/HIP targets, selected kernel parameters and resources, retained PTX text | Built-in static ELF/PE/fatbin/CUBIN/PTX inspection; [GPU code objects](docs/gpu-code-objects.md) |
| Model artifacts | GGUF / safetensors metadata, tensor declarations and derived totals | Built-in header inspection; optional full-file hash; [model files](docs/model-artifacts.md) |
| IL2CPP metadata | Unity global-metadata.dat assembly images, type and method names and index ranges | Built-in static decoding of versions 24, 27, 29, 31 and 39; no Unity required |
| Godot PCK | Archive inventory and extraction, original header/directory facts and selected content identities | Built-in static formats 2, 3 and 4; no Godot 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) |
| PE certificates | Authenticode WIN_CERTIFICATE entries and PKCS#7 signer/X.509 identity fields | Built-in static PE inspection; [PE certificates](docs/pe-certificates.md) |
| ELF notes | PT_NOTE/SHT_NOTE owners, types, GNU build-id and ABI-tag fields | Built-in static ELF inspection; [ELF notes](docs/elf-notes.md) |
| Erlang BEAM | IFF chunks, atoms, exports, imports, lambdas and the Code header | Built-in static BEAM inspection; [Erlang BEAM](docs/erlang-beam.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; 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) |
| APK signatures | v1/v2/v3/v3.1 signers, certificate fingerprints, key rotation and digest self-consistency | None (static only); `inspect_apk_signature` |
| Android devices | Devices, packages, processes, logs, dumps, screen captures, and APK/file transfers | Caller-supplied adb (emulator or hardware); [ADB guide](docs/adb-device-analysis.md) |
| JEB projects | Engine-backed project units, artifact digests and type/method pseudo-code | A caller-started JEB client serving MCP, selected with REA_JEB_MCP_URL; [JEB guide](docs/jeb-analysis.md) |
| Android resources | Decoded manifest, string tables, locales and version facts | Caller-supplied Apktool launcher; [resource guide](docs/apktool-resource-analysis.md) |
| Flutter apps | Dart snapshot hash, per-ABI library digests and engine build provenance | None (static APK parsing); [Flutter guide](docs/flutter-analysis.md) |
| Firmware | Regions, extraction results and native-analysis handoffs | Binwalk / Unblob on Linux; [firmware guide](docs/firmware-analysis.md) |
| Recorded packets | Selected fields, repeated values, filters and decode-as rules | Linux: TShark 4.x + prlimit; [capture guide](docs/packet-captures.md) |
| Packet summaries | Endpoints, DNS, TLS SNI, protocol distribution and HTTP requests | Caller-supplied tshark and capinfos (Linux-verified); [pcap guide](docs/pcap-analysis.md) |
| Recorded logic signals | Decoder annotations, sample ranges and diagnostics | Linux: sigrok-cli 0.7.2 + prlimit; [signal recordings](docs/signal-recordings.md) |
| Packages and resources | File inventories, digests, plists, Apple bundle anatomy and extracted resources | [Artifact and JavaScript guide](docs/javascript-artifact-reconstruction.md), [Apple applications](docs/apple-application-analysis.md) |
| Process behavior | Terminal output, interactions, exit and filesystem observations, and run comparisons | Linux/macOS with a native PTY; [process capture](docs/process-capture.md) |
| PyInstaller executables | CArchive members, Python version, entry scripts and extracted bytecode | None (static only); [Python packaged applications](docs/python-packaged-applications.md) |
Static JavaScript and .NET inspection read the supplied files without running
the application. Runtime capture runs or interacts with the selected target
@@ -345,7 +369,7 @@ Report vulnerabilities through [SECURITY.md](SECURITY.md).
## Star history
🎉 **250,000 GitHub stars — thank you!**
🎉 **300,000 GitHub stars — thank you!**
Thanks to everyone using REA, reporting bugs, requesting features, testing builds, and contributing fixes.
+272 -32
View File
@@ -10,16 +10,24 @@ import jadx.api.plugins.input.insns.Opcode;
import jadx.core.dex.info.MethodInfo;
import jadx.core.dex.nodes.*;
import java.io.PrintStream;
import java.io.StringReader;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.stream.Collectors;
import kotlin.Unit;
import kotlin.ResultKt;
import kotlin.coroutines.Continuation;
import kotlin.coroutines.CoroutineContext;
import kotlin.coroutines.EmptyCoroutineContext;
import kotlin.coroutines.intrinsics.IntrinsicsKt;
import kotlinx.coroutines.BuildersKt;
import kotlinx.io.CoreKt;
import kotlinx.io.JvmCoreKt;
import kotlinx.serialization.json.JsonObject;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.xml.sax.InputSource;
/** REA-owned metadata boundary for the unmodified jadx-headless-mcp 0.7.1 JAR. */
class ReaJadxBridge {
@@ -27,9 +35,13 @@ class ReaJadxBridge {
private final SessionHolder holder;
private JadxSession indexedSession;
private Map<String, List<ClassSnapshot>> classes = Map.of();
private JadxSession manifestSession;
private ManifestSnapshot manifestSnapshot;
private record MethodSnapshot(String name, String shortId, Map<String, Object> summary) {}
private record MethodSnapshot(String name, String rawName, String shortId, List<String> bodyAbsenceMarkers,
Map<String, Object> summary) {}
private record ClassSnapshot(JavaClass node, List<MethodSnapshot> methods, Map<String, Object> summary) {}
private record ManifestSnapshot(String text, Map<String, Object> summary) {}
private static final class InputFailure extends IllegalArgumentException {
final String field;
@@ -44,15 +56,94 @@ class ReaJadxBridge {
private static CallToolResult json(Object value) { return text(JSON.toJson(value), false); }
/** Translate the typed load outcome before upstream drops its exception identity. */
static Object loadResult(Object result) {
try { ResultKt.throwOnFailure(result); }
catch (Throwable failure) {
if (failure instanceof java.util.concurrent.CancellationException
|| failure instanceof Error && !(failure instanceof OutOfMemoryError)) return result;
if (failure instanceof OutOfMemoryError) {
String reason = failure.getMessage();
boolean heap = "Java heap space".equals(reason) || "GC overhead limit exceeded".equals(reason);
Map<String, Object> reply = new LinkedHashMap<>();
reply.put("kind", "jvm-memory-failure");
reply.put("memory_area", heap ? "heap" : "unknown");
reply.put("exception_type", "java.lang.OutOfMemoryError");
if (reason != null) reply.put("message", reason);
reply.put("max_heap_bytes", Runtime.getRuntime().maxMemory());
return text(JSON.toJson(reply), true);
}
return text("load failed: " + failure.getMessage(), true);
}
SessionHolder.LoadResult loaded = (SessionHolder.LoadResult) result;
Map<String, Object> reply = new LinkedHashMap<>();
reply.put("state", "LOADED");
reply.put("apk_path", loaded.getApkPath());
reply.put("class_count", loaded.getClassCount());
reply.put("raw_class_count", loaded.getRawClassCount());
reply.put("resource_count", loaded.getResourceCount());
reply.put("load_duration_ms", loaded.getLoadDurationMs());
reply.put("decompile_timeout_ms", loaded.getDecompileTimeoutMs());
reply.put("threads", loaded.getThreads());
reply.put("code_cache_size", loaded.getCodeCacheSize());
reply.put("resources", loaded.getResourceMode());
if (!loaded.getIncludePackages().isEmpty()) reply.put("include_packages", loaded.getIncludePackages());
if (!loaded.getExcludePackages().isEmpty()) reply.put("exclude_packages", loaded.getExcludePackages());
return json(reply);
}
private Object load(kotlinx.serialization.json.JsonObject arguments, SessionConfig defaults,
Continuation<? super CallToolResult> continuation) {
JsonObject args = JsonParser.parseString(arguments.toString()).getAsJsonObject();
String path = string(args, "path");
SessionConfig options = defaults.withLoadOverrides(
optionalString(args, "include_packages"), optionalString(args, "exclude_packages"),
args.has("threads") ? integer(args, "threads", 1) : null,
args.has("code_cache_size") ? integer(args, "code_cache_size", 64) : null,
optionalString(args, "resources"));
clearManifest();
return loadCall(next -> holder.load(path, options, next), continuation);
}
static Object loadCall(kotlin.jvm.functions.Function1<Continuation<SessionHolder.LoadResult>, Object> load,
Continuation<? super CallToolResult> continuation) {
var forwarded = new java.util.concurrent.atomic.AtomicBoolean();
Continuation<SessionHolder.LoadResult> resumed = new Continuation<>() {
public CoroutineContext getContext() { return continuation.getContext(); }
public void resumeWith(Object result) {
if (!forwarded.compareAndSet(false, true)) throw new IllegalStateException("Load resumed twice");
Object translated;
try { translated = loadResult(result); }
catch (Throwable failure) { translated = ResultKt.createFailure(failure); }
// Keep the consumer outside the translation catch, including inline completion.
continuation.resumeWith(translated);
}
};
Object result;
try { result = load.invoke(resumed); }
catch (Throwable failure) {
if (forwarded.get()) ResultKt.throwOnFailure(ResultKt.createFailure(failure));
result = ResultKt.createFailure(failure);
}
if (result == IntrinsicsKt.getCOROUTINE_SUSPENDED()) return result;
Object translated = loadResult(result);
ResultKt.throwOnFailure(translated);
return translated;
}
private static String optionalString(JsonObject args, String name) {
return args.has(name) ? string(args, name) : null;
}
private static String string(JsonObject args, String name) {
JsonElement value = JsonParser.parseString(args.toString()).getAsJsonObject().get(name);
JsonElement value = args.get(name);
if (value == null || !value.isJsonPrimitive() || !value.getAsJsonPrimitive().isString())
throw new IllegalArgumentException(name + " must be a string");
return value.getAsString();
}
private static int integer(JsonObject args, String name, int fallback) {
JsonElement value = JsonParser.parseString(args.toString()).getAsJsonObject().get(name);
JsonElement value = args.get(name);
if (value == null) return fallback;
if (!value.isJsonPrimitive() || !value.getAsJsonPrimitive().isNumber())
throw new IllegalArgumentException(name + " must be a nonnegative integer");
@@ -71,6 +162,77 @@ class ReaJadxBridge {
return current;
}
private void clearManifest() {
manifestSession = null;
manifestSnapshot = null;
}
/** Original archive identity, never a deobfuscated basename or name suffix. */
private ManifestSnapshot manifest(JadxSession current) {
if (manifestSession != current || manifestSnapshot == null) {
ResourceFile selected = null;
for (ResourceFile resource : current.getResources()) {
if (resource.getType() != ResourceType.MANIFEST
|| !resource.getOriginalName().equals("AndroidManifest.xml")) continue;
if (selected != null)
throw new IllegalArgumentException("Root AndroidManifest.xml resource identity is ambiguous");
selected = resource;
}
if (selected == null) throw new IllegalArgumentException("Root AndroidManifest.xml not found");
var content = selected.loadContent();
if (content == null || content.getDataType() != jadx.core.xmlgen.ResContainer.DataType.TEXT)
throw new IllegalArgumentException("Root AndroidManifest.xml did not decode as text");
String xml = content.getText().getCodeStr();
ManifestSnapshot snapshot = new ManifestSnapshot(xml, manifestSummary(xml));
manifestSnapshot = snapshot;
manifestSession = current;
}
return manifestSnapshot;
}
/** One parsed XML owner supplies namespace-aware values alongside the exact decoded text. */
static Map<String, Object> manifestSummary(String xml) {
Element root;
try {
// Match REA's existing JDK XML setup: document types are unsupported.
// Parse an in-memory string; XInclude and external DTD/schema I/O stay disabled.
DocumentBuilderFactory factory = DocumentBuilderFactory.newDefaultNSInstance();
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
factory.setXIncludeAware(false);
factory.setAttribute(javax.xml.XMLConstants.ACCESS_EXTERNAL_DTD, "");
factory.setAttribute(javax.xml.XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
root = factory.newDocumentBuilder().parse(new InputSource(new StringReader(xml)))
.getDocumentElement();
} catch (javax.xml.parsers.ParserConfigurationException | org.xml.sax.SAXException
| java.io.IOException failure) {
throw new IllegalArgumentException("Root AndroidManifest.xml is not supported well-formed XML", failure);
}
if (!root.getTagName().equals("manifest") || root.getNamespaceURI() != null)
throw new IllegalArgumentException("AndroidManifest.xml root must be an unnamespaced <manifest>");
String android = "http://schemas.android.com/apk/res/android";
String minSdk = "";
String targetSdk = "";
List<String> permissions = new ArrayList<>();
for (Node child = root.getFirstChild(); child != null; child = child.getNextSibling()) {
if (!(child instanceof Element element) || element.getNamespaceURI() != null) continue;
if (element.getTagName().equals("uses-sdk")) {
minSdk = element.getAttributeNS(android, "minSdkVersion");
targetSdk = element.getAttributeNS(android, "targetSdkVersion");
} else if (element.getTagName().equals("uses-permission")) {
String permission = element.getAttributeNS(android, "name");
if (!permission.isEmpty()) permissions.add(permission);
}
}
Map<String, Object> summary = new LinkedHashMap<>();
summary.put("package", root.getAttribute("package"));
summary.put("version_name", root.getAttributeNS(android, "versionName"));
summary.put("version_code", root.getAttributeNS(android, "versionCode"));
summary.put("min_sdk", minSdk);
summary.put("target_sdk", targetSdk);
summary.put("permissions", List.copyOf(permissions));
return Collections.unmodifiableMap(summary);
}
private ClassSnapshot selected(JsonObject args) {
JadxSession current = session();
if (indexedSession != current) {
@@ -90,10 +252,13 @@ class ReaJadxBridge {
if (!shortId.startsWith(rawName + "("))
throw new IllegalStateException("JADX method ID has no raw-name prefix: " + shortId);
String descriptor = shortId.substring(rawName.length());
List<String> bodyAbsenceMarkers = new ArrayList<>();
if (method.getAccessFlags().isNative()) bodyAbsenceMarkers.add("native_method");
if (method.getAccessFlags().isAbstract()) bodyAbsenceMarkers.add("abstract_method");
String signature = name + "(" + method.getArgTypes().stream()
.map(Object::toString).collect(Collectors.joining(", "))
+ "): " + method.getReturnType().toString();
return new MethodSnapshot(name, shortId, Map.of(
return new MethodSnapshot(name, rawName, shortId, List.copyOf(bodyAbsenceMarkers), Map.of(
"name", name, "signature", signature,
"dex_descriptor", descriptor,
"is_constructor", method.isConstructor(), "def_pos", 0));
@@ -140,16 +305,52 @@ class ReaJadxBridge {
private JadxSession.SmartCode methodBody(JadxSession current, ClassSnapshot cls,
MethodSnapshot selected, JavaMethod method) {
if (!selected.shortId().equals("<clinit>()V"))
return current.getMethodBodySmart(method, current.getMaxSourceBytes(), true);
// Access flags belong to the parsed member, before code generation rewrites it.
// These methods have no Java body; source slicing can include unrelated members.
if (!selected.bodyAbsenceMarkers().isEmpty())
return new JadxSession.SmartCode("", "java", false, selected.bodyAbsenceMarkers());
// Java code generation can fold all or part of <clinit> into field declarations.
// Its method text cannot establish coverage, even when a static block remains.
JadxSession.MethodSmali smali = current.getMethodSmali(cls.node(), method.getName());
if (!smali.getFound() || smali.getBlocks().size() != 1)
throw new IllegalArgumentException("Static initializer disassembly requires one exact method: " + selected.shortId());
if (selected.shortId().equals("<clinit>()V"))
return exactMethodSmali(current, cls, selected, List.of("static_initializer_disassembly"));
String java = current.getMethodSource(method);
if (java.startsWith("// ERROR:")) throw new IllegalArgumentException(java);
// The upstream placeholder means only that Java code was omitted, including
// methods merged into synthetic bridges. Access flags established absence above.
if (java.isEmpty() || java.equals(
"// method exists but has no decompiled body (native/abstract)"))
return exactMethodSmali(current, cls, selected, List.of("java_body_unavailable"));
List<String> markers = DecompileFailureKt.detectDecompileFailure(java);
if (markers.stream().anyMatch(DecompileFailureKt.getSTRONG_FAILURE_MARKERS()::contains))
return exactMethodSmali(current, cls, selected, markers);
return new JadxSession.SmartCode(
TextUtilKt.truncateToBytes(smali.getBlocks().get(0), current.getMaxSourceBytes()),
"smali", true, List.of("static_initializer_disassembly"));
TextUtilKt.truncateToBytes(java, current.getMaxSourceBytes()), "java", false, markers);
}
private JadxSession.SmartCode exactMethodSmali(JadxSession current, ClassSnapshot cls,
MethodSnapshot selected, List<String> markers) {
JadxSession.MethodSmali smali = current.getMethodSmali(cls.node(), selected.rawName());
String block = selectMethodSmali(smali, selected.shortId());
return new JadxSession.SmartCode(
TextUtilKt.truncateToBytes(block, current.getMaxSourceBytes()), "smali", true, markers);
}
private static String selectMethodSmali(JadxSession.MethodSmali smali, String shortId) {
if (!smali.getFound())
throw new IllegalArgumentException("JADX could not disassemble " + shortId
+ (smali.getNote() == null ? "" : ": " + smali.getNote()));
// Upstream returns every same-name overload, and can return an unterminated
// trailing block. Match the full original identity before applying output limits.
List<String> matches = smali.getBlocks().stream().filter(block -> {
String header = block.lines().findFirst().orElse("").strip();
return header.startsWith(".method ") && header.endsWith(" " + shortId);
}).toList();
if (matches.size() != 1)
throw new IllegalArgumentException("Disassembly could not establish one selected method: " + shortId);
String block = matches.get(0);
if (!block.stripTrailing().endsWith("\n.end method"))
throw new IllegalArgumentException("Selected method disassembly is incomplete: " + shortId);
return block;
}
// The local transport admits 8 MiB frames. Reserve 1 KiB for the fixed
@@ -203,7 +404,8 @@ class ReaJadxBridge {
return new DirectInvokes(sites, exhaustive[0]);
}
private CallToolResult handle(String name, JsonObject args) {
private CallToolResult handle(String name, kotlinx.serialization.json.JsonObject input) {
JsonObject args = JsonParser.parseString(input.toString()).getAsJsonObject();
if (name.equals("rea_jvm_status")) return json(Map.of(
"engine_reported_version", BuildInfo.VERSION,
"max_heap_bytes", Runtime.getRuntime().maxMemory(),
@@ -211,14 +413,30 @@ class ReaJadxBridge {
"java_version", System.getProperty("java.version"),
"metadata_scope", "parsed_members"));
JadxSession current = session();
if (name.equals("get_app_info")) return json(manifest(current).summary());
if (name.equals("get_android_manifest"))
return text(TextUtilKt.truncateToBytes(manifest(current).text(), current.getMaxSourceBytes()), false);
if (name.equals("list_classes")) {
List<String> names = current.getClassFqns();
String query = args.has("query") ? string(args, "query") : "";
int offset = integer(args, "offset", 0);
int limit = integer(args, "limit", 200);
int start = Math.min(offset, names.size());
int end = (int) Math.min((long) start + limit, names.size());
return json(Map.of("total", names.size(), "offset", offset, "limit", limit,
"items", names.subList(start, end)));
int end = start;
List<String> items;
if (query.isEmpty()) {
end = (int) Math.min((long) start + limit, names.size());
items = names.subList(start, end);
} else {
items = new ArrayList<>();
while (end < names.size() && items.size() < limit) {
String nameInInventory = names.get(end++);
if (nameInInventory.contains(query)) items.add(nameInInventory);
}
}
return json(Map.of("total_classes", names.size(), "next_offset", end,
"scope", "top_level", "offset", offset, "limit", limit,
"items", items));
}
ClassSnapshot cls = selected(args);
if (name.equals("get_class_summary")) return json(cls.summary());
@@ -230,6 +448,7 @@ class ReaJadxBridge {
JadxSession.SmartCode code = methodBody(current, cls, overloads.get(index), method);
return json(Map.of("class_name", cls.summary().get("full_name"),
"method_name", overloads.get(index).name(), "full_name", method.getFullName(),
"dex_descriptor", overloads.get(index).summary().get("dex_descriptor"),
"overload_index", index, "overload_count", overloads.size(),
"mode", code.getKind(), "fell_back", code.getFellBack(),
"markers", code.getMarkers(), "body", code.getText()));
@@ -241,7 +460,7 @@ class ReaJadxBridge {
if (name.equals("get_xrefs_to_class")) uses = cls.node().getUseIn();
else {
List<MethodSnapshot> overloads = methods(cls, args);
if (overloads.size() != 1 && !args.containsKey("overload_index"))
if (overloads.size() != 1 && !args.has("overload_index"))
throw new InputFailure("overload_index", "Select one of " + overloads.size() + " method overloads");
overloadIndex = integer(args, "overload_index", 0);
if (overloadIndex >= overloads.size())
@@ -284,30 +503,52 @@ class ReaJadxBridge {
return json(result);
}
private static void registerBridgeTool(Server server, ReaJadxBridge bridge, String name,
ToolSchema schema, String description) {
Tool tool = new Tool(name, schema, description, null, null, null, null, null, null);
server.addTool(tool, (connection, request, continuation) -> {
try { return bridge.handle(name, request.getArguments()); }
catch (InputFailure error) { return text(JSON.toJson(Map.of(
"kind", "invalid-input", "field", error.field, "message", error.getMessage())), true); }
catch (IllegalArgumentException error) { return text(error.getMessage(), true); }
});
}
public static void main(String[] args) throws Exception {
PrintStream protocolOut = System.out;
System.setOut(System.err);
SessionHolder holder = new SessionHolder(new SessionConfig(
1048576, 0, 90000L, 1, List.of(), List.of(), 64, ResourceMode.FULL));
SessionConfig defaults = new SessionConfig(
1048576, 0, 90000L, 1, List.of(), List.of(), 64, ResourceMode.FULL);
SessionHolder holder = new SessionHolder(defaults);
ServerCapabilities capabilities = new ServerCapabilities(
new ServerCapabilities.Tools(null), null, null, null, null, null, null, Map.of());
Server server = new Server(new Implementation("rea-jadx-bridge", "1", null, null, null),
Server server = new Server(new Implementation("rea-jadx-bridge", "5", null, null, null),
new ServerOptions(capabilities), "", ignored -> Unit.INSTANCE);
SessionToolsKt.registerSessionTools(server, holder);
ManifestToolsKt.registerManifestTools(server, holder);
ClassToolsKt.registerClassTools(server, holder);
MethodToolsKt.registerMethodTools(server, holder);
XrefToolsKt.registerXrefTools(server, holder);
ReaJadxBridge bridge = new ReaJadxBridge(holder);
Set<String> consumed = Set.of("load_apk", "get_app_info", "get_android_manifest",
Tool loadTool = server.getTools().get("load_apk").getTool();
server.removeTool("load_apk");
server.addTool(loadTool, (connection, request, continuation) ->
bridge.load(request.getArguments(), defaults, continuation));
Set<String> consumed = Set.of("load_apk",
"list_classes", "get_class_summary", "get_method_body",
"get_xrefs_to_class", "get_xrefs_to_method");
for (String name : new ArrayList<>(server.getTools().keySet()))
if (!consumed.contains(name)) server.removeTool(name);
Tool diagnostics = new Tool("rea_jvm_status",
new ToolSchema(null, new JsonObject(Map.of()), List.of(), null),
new ToolSchema(null, new kotlinx.serialization.json.JsonObject(Map.of()), List.of(), null),
"Observed worker JVM and metadata scope", null, null, null, null, null, null);
server.addTool(diagnostics, (connection, request, continuation) -> bridge.handle("rea_jvm_status", request.getArguments()));
for (String name : List.of("get_app_info", "get_android_manifest")) {
String description = name.equals("get_app_info")
? "Namespace-aware summary of the exact root AndroidManifest.xml resource."
: "Exact decoded root AndroidManifest.xml text, bounded by the session source byte limit.";
registerBridgeTool(server, bridge, name,
new ToolSchema(null, new kotlinx.serialization.json.JsonObject(Map.of()), List.of(), null), description);
}
for (String name : List.of("list_classes", "get_class_summary",
"get_method_body", "get_xrefs_to_class", "get_xrefs_to_method")) {
Tool tool = server.getTools().get(name).getTool();
@@ -320,20 +561,18 @@ class ReaJadxBridge {
};
Map<String, kotlinx.serialization.json.JsonElement> properties = new LinkedHashMap<>();
for (String field : fields) properties.put(field, tool.getInputSchema().getProperties().get(field));
if (name.equals("list_classes")) properties.put("query",
new kotlinx.serialization.json.JsonObject(Map.of(
"type", kotlinx.serialization.json.JsonElementKt.JsonPrimitive("string"))));
String description = switch (name) {
case "list_classes" -> "Indexed pages of all top-level class names, with offset and limit.";
case "list_classes" -> "Pages of case-sensitive matching top-level class names; offset and next_offset are inventory positions, limit bounds matching rows. Omitted or empty query inventories all names.";
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 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);
server.addTool(narrowed, (connection, request, continuation) -> {
try { return bridge.handle(name, request.getArguments()); }
catch (InputFailure error) { return text(JSON.toJson(Map.of(
"kind", "invalid-input", "field", error.field, "message", error.getMessage())), true); }
catch (IllegalArgumentException error) { return text(error.getMessage(), true); }
});
registerBridgeTool(server, bridge, name,
new ToolSchema(null, new kotlinx.serialization.json.JsonObject(properties),
tool.getInputSchema().getRequired(), null), description);
}
CountDownLatch done = new CountDownLatch(1);
server.onClose(() -> { done.countDown(); return Unit.INSTANCE; });
@@ -359,6 +598,7 @@ class ReaJadxBridge {
BuildersKt.runBlocking(EmptyCoroutineContext.INSTANCE,
(scope, continuation) -> server.close(continuation));
} finally {
bridge.clearManifest();
BuildersKt.runBlocking(EmptyCoroutineContext.INSTANCE,
(scope, continuation) -> holder.unload(continuation));
}
+622 -108
View File
@@ -11,6 +11,7 @@ import json
import hashlib
import hmac
import os
import re
import secrets
import socket
import stat
@@ -28,14 +29,156 @@ if os.name == "nt":
import cutter
try:
from PySide6.QtCore import QObject, Signal
from PySide6.QtCore import QCoreApplication, QObject, Signal
except ImportError:
from PySide2.QtCore import QObject, Signal
from PySide2.QtCore import QCoreApplication, QObject, Signal
# Request and response frame budget in bytes, excluding the trailing LF.
# REA_BRIDGE_MAX_REQUEST_BYTES selects it. Unset stays 16777216.
# 7464 is the authenticated frame of the largest bounded symbol name.
# The 64-receipt recovery status is smaller with current_file null. A path
# that fits is kept. A path that does not fit is null, not the descriptor path.
# 536870887 is Node's 536870888-character string ceiling minus the framing LF.
# The ceiling is the encoded payload; JSON expansion is measured before it.
# A fitting response is signed from bounded JSON pieces and written in 64 KiB
# chunks, so this maximum does not retain several copies of the frame.
_BRIDGE_MAX_REQUEST_BYTES_SETTING = "REA_BRIDGE_MAX_REQUEST_BYTES"
DEFAULT_BRIDGE_MAX_REQUEST_BYTES = 16 * 1024 * 1024
MINIMUM_BRIDGE_MAX_REQUEST_BYTES = 7464
MAXIMUM_BRIDGE_MAX_REQUEST_BYTES = 536870887
# The outer authenticated frame around the escaped JSON payload, excluding LF.
_AUTHENTICATED_PREFIX = b'{"response":"'
_AUTHENTICATED_PROOF_MARK = b'","proof":"'
_PROOF_HEX_LENGTH = 64
_AUTHENTICATED_FRAME_OVERHEAD = (
len(_AUTHENTICATED_PREFIX)
+ len(_AUTHENTICATED_PROOF_MARK)
+ _PROOF_HEX_LENGTH
+ len(b'"}')
)
_AUTHENTICATED_WRITE_BUFFER_BYTES = 64 * 1024
# Raw JSON is produced in pieces no larger than this many characters.
# One character expands to at most six JSON bytes (\u00XX) before the outer
# string escape, so a piece stays far below the frame ceiling.
_JSON_CHUNK_CHARS = 64 * 1024
# Fixed admission cap. It does not follow REA_BRIDGE_MAX_REQUEST_BYTES.
# A further accept is closed. Starting a worker must not stop the listener.
MAX_CONNECTION_WORKERS = 8
def _close_socket(connection: socket.socket) -> None:
try:
connection.close()
except OSError:
pass
def _bridge_max_request_bytes_constraint():
return (
"REA_BRIDGE_MAX_REQUEST_BYTES must be a positive decimal integer "
"from %d to %d bytes; the default is %d bytes (16 MiB). "
"A value below %d bytes cannot carry the largest admitted "
"symbol-name command, and a larger value is refused so the encoded "
"payload plus its framing LF stays one string under Node's "
"single-string ceiling."
% (
MINIMUM_BRIDGE_MAX_REQUEST_BYTES,
MAXIMUM_BRIDGE_MAX_REQUEST_BYTES,
DEFAULT_BRIDGE_MAX_REQUEST_BYTES,
MINIMUM_BRIDGE_MAX_REQUEST_BYTES,
)
)
def _parse_bridge_max_request_bytes(raw):
"""Refuse a non-decimal or out-of-range ceiling. Never fall back."""
constraint = _bridge_max_request_bytes_constraint()
if isinstance(raw, bool):
raise RuntimeError(constraint)
if isinstance(raw, int):
value = raw
elif isinstance(raw, str):
# ASCII digits only. str.isdigit() accepts numerals such as U+0668
# that the TypeScript setting rejects.
if re.fullmatch(r"[1-9][0-9]*", raw) is None:
raise RuntimeError(constraint)
value = int(raw)
else:
raise RuntimeError(constraint)
if (
value < MINIMUM_BRIDGE_MAX_REQUEST_BYTES
or value > MAXIMUM_BRIDGE_MAX_REQUEST_BYTES
):
raise RuntimeError(constraint)
return value
def _resolve_bridge_max_request_bytes():
"""Prefer an injected global, then the process environment, then 16 MiB."""
injected = globals().get(_BRIDGE_MAX_REQUEST_BYTES_SETTING)
if injected is not None:
return _parse_bridge_max_request_bytes(injected), "setting"
raw = os.environ.get(_BRIDGE_MAX_REQUEST_BYTES_SETTING)
if raw is None:
return DEFAULT_BRIDGE_MAX_REQUEST_BYTES, "default"
return _parse_bridge_max_request_bytes(raw), "setting"
MAX_FRAME_BYTES, BRIDGE_MAX_REQUEST_BYTES_SOURCE = (
_resolve_bridge_max_request_bytes()
)
def _bridge_max_request_bytes_recovery():
return (
"The effective REA_BRIDGE_MAX_REQUEST_BYTES value is %d bytes "
"(source %s; default %d, floor %d, maximum %d). "
"Set REA_BRIDGE_MAX_REQUEST_BYTES in the environment that starts REA "
"to a decimal integer from %d to %d, then restart REA. "
"Restart Hopper or Cutter from that same environment so the bridge "
"uses this ceiling."
% (
MAX_FRAME_BYTES,
BRIDGE_MAX_REQUEST_BYTES_SOURCE,
DEFAULT_BRIDGE_MAX_REQUEST_BYTES,
MINIMUM_BRIDGE_MAX_REQUEST_BYTES,
MAXIMUM_BRIDGE_MAX_REQUEST_BYTES,
MINIMUM_BRIDGE_MAX_REQUEST_BYTES,
MAXIMUM_BRIDGE_MAX_REQUEST_BYTES,
)
)
def _frame_limit_exceeded():
return ValueError(
"frame-limit-exceeded: Bridge request exceeds the %d-byte payload limit. %s"
% (MAX_FRAME_BYTES, _bridge_max_request_bytes_recovery())
)
MAX_FRAME_BYTES = 16 * 1024 * 1024
SOCKET_TIMEOUT_SECONDS = 20
AUTH_FRAME_TIMEOUT_SECONDS = 1
# The client request deadline. The authenticated frame read may use up to
# this long; it does not grow past it when the byte ceiling is raised.
REQUEST_DEADLINE_SECONDS = 25
def _authenticated_frame_timeout_seconds():
"""Time to hash and transfer one maximum-sized authenticated frame.
The hello frame stays at one second. The following command frame is
built only after that challenge, so its read budget follows the
configured payload size, at one second per mebibyte, and never exceeds
the 25 second client deadline.
"""
mebibytes = (MAX_FRAME_BYTES + (1024 * 1024) - 1) // (1024 * 1024)
return min(
REQUEST_DEADLINE_SECONDS,
max(AUTH_FRAME_TIMEOUT_SECONDS, mebibytes),
)
MAX_DESCRIPTOR_BYTES = 64 * 1024
MAX_COMPLETION_RECEIPTS = 64
@@ -326,15 +469,16 @@ class _RequestDispatcher(QObject):
self._receipt_lock = threading.Lock()
self.current_file = self._read_current_file()
try:
self.cutter_version = str(cutter.core().getVersionInformation())
value = QCoreApplication.applicationVersion()
self.cutter_version = value if isinstance(value, str) and value else None
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
value = cutter.core().getConfig("file.path")
return value if isinstance(value, str) and value else None
except Exception:
return None
@@ -479,6 +623,17 @@ class _BridgeServer(threading.Thread):
self.token = secrets.token_urlsafe(32)
self.poisoned = False
self.connection_ordinal = 0
# The client proof is inside the authenticated frame, so that read
# cannot start after proof. It runs off the accept loop. One
# authenticated command still runs at a time. completed_connection
# is the longest prefix of these ordinals whose handlers have
# terminated, not "this status ordinal minus one".
self._command_lock = threading.Lock()
self._connections_lock = threading.Lock()
self._connections: set[socket.socket] = set()
self._workers: list[threading.Thread] = []
self._finished_through = 0
self._finished_ordinals: set[int] = set()
self.directory = _bridge_directory()
self.descriptor = self.directory / f"cutter-{os.getpid()}-{self.session_id}.json"
@@ -499,6 +654,7 @@ class _BridgeServer(threading.Thread):
"current_file": self.dispatcher.current_file,
"cutter_version": self.dispatcher.cutter_version,
"identity_status": "partial",
"max_request_bytes": MAX_FRAME_BYTES,
}
if os.name == "nt":
_write_windows_descriptor(self.directory, self.descriptor, json.dumps(payload).encode("utf-8"))
@@ -540,8 +696,46 @@ class _BridgeServer(threading.Thread):
continue
except OSError:
break
with self._connections_lock:
self._workers = [
worker for worker in self._workers if worker.is_alive()
]
at_capacity = len(self._workers) >= MAX_CONNECTION_WORKERS
if at_capacity:
_close_socket(connection)
continue
self.connection_ordinal += 1
connection_ordinal = self.connection_ordinal
worker = threading.Thread(
target=self._serve_connection,
args=(connection, connection_ordinal),
name="rea-cutter-bridge-connection",
daemon=True,
)
with self._connections_lock:
self._connections.add(connection)
self._workers.append(worker)
try:
worker.start()
except RuntimeError:
with self._connections_lock:
self._connections.discard(connection)
if worker in self._workers:
self._workers.remove(worker)
self._note_connection_finished(connection_ordinal)
_close_socket(connection)
with self._connections_lock:
workers = list(self._workers)
for worker in workers:
worker.join(timeout=1)
self.server.close()
try:
self.descriptor.unlink(missing_ok=True)
except OSError:
pass
def _serve_connection(self, connection: socket.socket, connection_ordinal: int) -> None:
try:
with connection:
connection.settimeout(AUTH_FRAME_TIMEOUT_SECONDS)
authenticated = False
@@ -549,60 +743,73 @@ class _BridgeServer(threading.Thread):
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
return
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)
authenticated_frame = self._receive_frame(
connection, _authenticated_frame_timeout_seconds()
)
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):
presented_proof = authenticated_frame.get("proof") if isinstance(authenticated_frame, dict) else None
if not isinstance(request_bytes, str) or not isinstance(presented_proof, str):
self._send(connection, {"ok": False, "error": "authentication-failed"})
continue
return
# Update the prefix and the request separately. One joined
# preimage would retain another copy of a maximum-sized command.
expected_proof = hmac.new(self.token.encode(), digestmod=hashlib.sha256)
expected_proof.update(
f"rea-cutter-client-v2:{self.session_id}:{client_nonce}:{server_nonce}:".encode()
)
expected_proof.update(request_bytes.encode())
if not hmac.compare_digest(presented_proof, expected_proof.hexdigest()):
self._send(connection, {"ok": False, "error": "authentication-failed"})
return
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
return
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)
with self._command_lock:
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)
return
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)
return
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"] = self._completion_watermark()
self._send_authenticated(connection, reply, client_nonce, server_nonce)
except socket.timeout:
if authenticated:
self._send_authenticated(connection, {
@@ -613,77 +820,217 @@ class _BridgeServer(threading.Thread):
}, 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
finally:
self._note_connection_finished(connection_ordinal)
with self._connections_lock:
self._connections.discard(connection)
def _note_connection_finished(self, ordinal: int) -> None:
"""Record one accepted connection as terminated.
The watermark is the longest prefix 1..W that has terminated. A later
status cannot report an earlier connection that is still reading or
still waiting to run.
"""
with self._connections_lock:
if ordinal <= self._finished_through or ordinal in self._finished_ordinals:
return
self._finished_ordinals.add(ordinal)
while self._finished_through + 1 in self._finished_ordinals:
self._finished_through += 1
self._finished_ordinals.discard(self._finished_through)
def _completion_watermark(self) -> str:
with self._connections_lock:
return f"{self._finished_through:016x}"
@staticmethod
def _receive_frame(connection: socket.socket) -> dict[str, Any]:
def _receive_frame(
connection: socket.socket,
timeout_seconds: float = AUTH_FRAME_TIMEOUT_SECONDS,
) -> 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")
deadline = time.monotonic() + timeout_seconds
newline = -1
while newline < 0:
if len(data) > MAX_FRAME_BYTES:
raise _frame_limit_exceeded()
remaining = deadline - time.monotonic()
if remaining <= 0:
raise socket.timeout("authentication frame timed out")
connection.settimeout(remaining)
# MAX_FRAME_BYTES limits the JSON payload; the terminating LF is
# framing and may be the one byte beyond that payload budget.
part = connection.recv(min(65536, MAX_FRAME_BYTES + 1 - len(data)))
if not part:
raise ValueError("incomplete authentication frame")
scanned = len(data)
data.extend(part)
line, remainder = bytes(data).split(b"\n", 1)
if len(line) > MAX_FRAME_BYTES or remainder:
# The delimiter is one byte, so it cannot straddle a chunk.
# Search only the bytes just received.
newline = data.find(b"\n", scanned)
if newline > MAX_FRAME_BYTES:
raise _frame_limit_exceeded()
if newline != len(data) - 1:
raise ValueError("invalid framed request")
value = json.loads(line)
value = json.loads(bytes(data[:newline]))
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:
if self._deliver_authenticated(connection, value, client_nonce, server_nonce):
return
try:
connection.sendall(encoded)
except OSError:
pass
# Keep the recovery payload. Include the observed path when it
# fits. An explicit null path means unknown, so the client does
# not pair this generation with the descriptor's older path.
bounded = self._bounded_status_response(value)
if bounded is not None:
observed_path = value.get("current_file")
if isinstance(observed_path, str) and observed_path:
with_path = dict(bounded)
with_path["current_file"] = observed_path
with_path.pop("message", None)
if self._deliver_authenticated(
connection, with_path, client_nonce, server_nonce
):
return
if self._deliver_authenticated(
connection, bounded, client_nonce, server_nonce
):
return
command = self._bounded_command_response(value)
if command is not None:
observed_path = value.get("current_file")
if isinstance(observed_path, str) and observed_path:
with_path = dict(command)
with_path["current_file"] = observed_path
with_path.pop("message", None)
if self._deliver_authenticated(
connection, with_path, client_nonce, server_nonce
):
return
if self._deliver_authenticated(
connection, command, client_nonce, server_nonce
):
return
value = self._truncated_response(value)
if self._deliver_authenticated(connection, value, client_nonce, server_nonce):
return
# This fixed-size fallback is checked through the same signed framing path.
self._deliver_authenticated(connection, {
"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.",
}, client_nonce, server_nonce)
def _encode_authenticated_response(self, value: dict[str, Any], client_nonce: str, server_nonce: str) -> bytes | None:
def _deliver_authenticated(
self, connection: socket.socket, value: dict[str, Any], client_nonce: str, server_nonce: str
) -> bool:
proof = self._authenticated_proof(value, client_nonce, server_nonce)
if proof is None:
return False
try:
self._write_authenticated_frame(connection, value, proof)
except OSError:
# A disconnected caller must not fall through to another payload.
pass
return True
def _authenticated_proof(
self, value: dict[str, Any], client_nonce: str, server_nonce: str
) -> str | 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)
# The payload budget excludes the terminating LF. Fragments are
# measured and signed here, then discarded. The write pass encodes
# them again so a maximum-sized reply is not held in several copies.
frame_size = _AUTHENTICATED_FRAME_OVERHEAD
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 fragment in _response_fragments(value):
frame_size += len(_escape_json_fragment(fragment))
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'
proof.update(fragment.encode("utf-8"))
return proof.hexdigest()
def _encode_authenticated_response(
self, value: dict[str, Any], client_nonce: str, server_nonce: str
) -> bytes | None:
"""Return one exact frame. The socket path writes the same pieces."""
proof = self._authenticated_proof(value, client_nonce, server_nonce)
if proof is None:
return None
return b"".join(self._iter_authenticated_frame(value, proof))
def _iter_authenticated_frame(self, value: dict[str, Any], proof: str):
yield _AUTHENTICATED_PREFIX
for fragment in _response_fragments(value):
yield _escape_json_fragment(fragment)
yield _AUTHENTICATED_PROOF_MARK + proof.encode("ascii") + b'"}\n'
def _write_authenticated_frame(
self, connection: socket.socket, value: dict[str, Any], proof: str
) -> None:
pending = bytearray()
for chunk in self._iter_authenticated_frame(value, proof):
pending.extend(chunk)
if len(pending) >= _AUTHENTICATED_WRITE_BUFFER_BYTES:
connection.sendall(pending)
pending.clear()
if pending:
connection.sendall(pending)
@staticmethod
def _bounded_status_response(value: dict[str, Any]) -> dict[str, Any] | None:
if "completed_commands" not in value and "completed_connection" not in value:
return None
bounded: dict[str, Any] = {
"ok": value.get("ok") is True,
"session_id": value.get("session_id"),
"document_generation": value.get("document_generation"),
"identity_status": value.get("identity_status", "partial"),
"completed_commands": value.get("completed_commands"),
"current_file": None,
"message": (
"current_file is unknown because the status frame exceeded "
"REA_BRIDGE_MAX_REQUEST_BYTES. Do not reuse an older descriptor "
"path with this document_generation."
),
}
if "completed_connection" in value:
bounded["completed_connection"] = value["completed_connection"]
return bounded
@staticmethod
def _bounded_command_response(value: dict[str, Any]) -> dict[str, Any] | None:
if "execution_state" not in value and "document_generation" not in value:
return None
bounded: dict[str, Any] = {
"ok": value.get("ok") is True,
"output": None,
"execution_state": value.get("execution_state", "unknown"),
"output_truncated": True,
"current_file": None,
"message": (
"current_file is unknown because the result frame exceeded "
"REA_BRIDGE_MAX_REQUEST_BYTES. The command completion state and "
"document_generation are preserved. Do not reuse an older "
"descriptor path with this document_generation."
),
}
generation = value.get("document_generation")
if isinstance(generation, int) and not isinstance(generation, bool):
bounded["document_generation"] = generation
if value.get("identity_status") == "partial":
bounded["identity_status"] = "partial"
if value.get("ok") is not True and isinstance(value.get("error"), str):
bounded["error"] = value["error"]
return bounded
@staticmethod
def _truncated_response(value: dict[str, Any]) -> dict[str, Any]:
@@ -741,6 +1088,17 @@ class _BridgeServer(threading.Thread):
self.server.close()
except OSError:
pass
with self._connections_lock:
connections = list(self._connections)
for connection in connections:
try:
connection.shutdown(socket.SHUT_RDWR)
except OSError:
pass
try:
connection.close()
except OSError:
pass
class ReaCutterPlugin(cutter.CutterPlugin):
@@ -774,6 +1132,128 @@ def create_cutter_plugin() -> ReaCutterPlugin:
return ReaCutterPlugin()
def _json_object_key(key: Any) -> str:
"""Match CPython's JSON object-key text before quoting."""
if isinstance(key, str):
return key
if key is True:
return "true"
if key is False:
return "false"
if key is None:
return "null"
if isinstance(key, int):
return str(key)
if isinstance(key, float):
return json.dumps(key, separators=(",", ":"))
raise TypeError("keys must be str, int, float, bool or None")
def _response_fragments(value: Any):
"""Yield compact JSON pieces whose concatenation is the encoded value.
String contents are sliced before encoding so a large output never becomes
one frame-sized fragment. Each piece is at most ``_JSON_CHUNK_CHARS``
characters, or the six-byte ``\\u00XX`` expansion of one such slice.
"""
if isinstance(value, str):
yield '"'
for start in range(0, len(value), _JSON_CHUNK_CHARS):
yield json.dumps(
value[start:start + _JSON_CHUNK_CHARS], ensure_ascii=False
)[1:-1]
yield '"'
return
if isinstance(value, dict):
yield "{"
first = True
for key, item in value.items():
if not first:
yield ","
first = False
yield from _response_fragments(_json_object_key(key))
yield ":"
yield from _response_fragments(item)
yield "}"
return
if isinstance(value, (list, tuple)):
yield from _list_fragments(value)
return
token = _scalar_token(value)
if token is None:
raise TypeError("response value is not JSON")
yield token
def _scalar_token(value: Any) -> str | None:
"""Compact JSON for one scalar. None means the value needs structure."""
if type(value) is int:
return str(value)
if value is None:
return "null"
if value is True:
return "true"
if value is False:
return "false"
if isinstance(value, float):
return json.dumps(value, separators=(",", ":"))
return None
def _list_fragments(value: list[Any] | tuple[Any, ...]):
"""Encode a list, batching scalars into bounded pieces.
One ``json.dumps`` per number makes a dense array miss the client
deadline. A batch is at most ``_JSON_CHUNK_CHARS`` of compact JSON.
"""
yield "["
first = True
batch: list[str] = []
batch_len = 0
def flush_batch() -> str | None:
nonlocal first, batch, batch_len
if not batch:
return None
piece = ",".join(batch)
batch = []
batch_len = 0
if first:
first = False
return piece
return "," + piece
for item in value:
token = _scalar_token(item)
if token is None:
emitted = flush_batch()
if emitted is not None:
yield emitted
if first:
first = False
else:
yield ","
yield from _response_fragments(item)
continue
extra = len(token) if not batch else len(token) + 1
if batch and batch_len + extra > _JSON_CHUNK_CHARS:
emitted = flush_batch()
if emitted is not None:
yield emitted
extra = len(token)
batch.append(token)
batch_len += extra
emitted = flush_batch()
if emitted is not None:
yield emitted
yield "]"
def _escape_json_fragment(fragment: str) -> bytes:
"""Escape one raw JSON piece for the outer response string."""
return json.dumps(fragment, ensure_ascii=False)[1:-1].encode("utf-8")
def _within_response_limit(value: Any) -> bool:
pending = [value]
seen: set[int] = set()
@@ -782,20 +1262,37 @@ def _within_response_limit(value: Any) -> bool:
current = pending.pop()
if isinstance(current, str):
size += 2
if len(current) > MAX_FRAME_BYTES - size:
return False
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)
# Count UTF-8 bytes without allocating a copy of the whole output.
try:
size += sum(
len(current[start:start + 65536].encode("utf-8"))
for start in range(0, len(current), 65536)
)
except UnicodeEncodeError:
# Unencodable output also needs the signed omission fallback.
return False
size += current.count('"') + current.count("\\")
# A short escape is two JSON bytes and was already counted as one.
# Other controls expand to a six-byte \\u00XX sequence.
size += sum(
1 if ord(character) in (0x08, 0x09, 0x0A, 0x0C, 0x0D) else 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)
count = len(current)
# Braces, one colon per entry, and commas between entries.
# Keys and values are counted when they are visited.
size += 2 + count + max(0, count - 1)
if size > MAX_FRAME_BYTES:
return False
pending.extend(current.keys())
@@ -805,12 +1302,29 @@ def _within_response_limit(value: Any) -> bool:
if identity in seen:
return False
seen.add(identity)
size += 2 + 2 * len(current)
count = len(current)
# Brackets and commas between items. Each item is counted below.
size += 2 + max(0, count - 1)
if size > MAX_FRAME_BYTES:
return False
pending.extend(current)
elif current is None:
size += 4
elif current is True:
size += 4
elif current is False:
size += 5
elif isinstance(current, int):
size += len(str(current))
elif isinstance(current, float):
size += len(json.dumps(current, separators=(",", ":")))
else:
size += 16
try:
size += len(
json.dumps(current, ensure_ascii=False, separators=(",", ":"))
)
except (TypeError, ValueError):
return False
if size > MAX_FRAME_BYTES:
return False
return True
+65 -12
View File
@@ -1,38 +1,91 @@
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import ghidra.app.plugin.core.analysis.AutoAnalysisManager;
import ghidra.app.services.Analyzer;
import ghidra.app.util.headless.HeadlessScript;
import ghidra.program.model.listing.Program;
import ghidra.util.classfinder.ClassSearcher;
/** Disable the named auto-analyzers before default auto-analysis. */
/**
* Disable the named auto-analyzers before default auto-analysis.
*
* REA_GHIDRA_DISABLED_ANALYZERS is process-wide, but Ghidra registers only the
* installed analyzers whose canAnalyze(program) accepts this program. A name an
* installed analyzer provides without being registered here is not applicable
* and is recorded instead of rejected; a name no installed analyzer provides
* aborts the import. The outcome is stored as a program property that the
* bridge reports during its handshake.
*/
public final class ReaGhidraAnalysisOptions extends HeadlessScript {
static final String REPORT_OPTION = "REA disabled analyzers";
private static final String SETTING = "REA_GHIDRA_DISABLED_ANALYZERS";
@Override
public void run() throws Exception {
String[] arguments = getScriptArgs();
if (arguments.length != 1 || arguments[0].isBlank()) {
throw new IllegalArgumentException(
"ReaGhidraAnalysisOptions requires one comma-separated analyzer-name argument");
"ReaGhidraAnalysisOptions requires one comma-separated " + SETTING + " argument");
}
String[] names = arguments[0].split(",");
boolean[] registered = new boolean[names.length];
AutoAnalysisManager analysis = AutoAnalysisManager.getAnalysisManager(currentProgram);
Map<String, String> options = getCurrentAnalysisOptionsAndValues(currentProgram);
for (String requestedName : names) {
Set<String> installed = null;
for (int index = 0; index < names.length; index++) {
monitor.checkCancelled();
String name = requestedName.trim();
if (analysis.getAnalyzer(name) == null || !options.containsKey(name)) {
throw new IllegalArgumentException("Unknown Ghidra analyzer for this program: " + name);
String name = names[index].trim();
names[index] = name;
if (analysis.getAnalyzer(name) != null) {
if (!options.containsKey(name)) {
throw new IllegalStateException(SETTING + " names " + name +
", which Ghidra registered for this program without an enablement option");
}
registered[index] = true;
continue;
}
if (installed == null) installed = installedAnalyzerNames();
if (!installed.contains(name)) {
throw new IllegalArgumentException(SETTING + " names " + name +
", which is not an analyzer in this Ghidra installation");
}
}
for (String requestedName : names) {
for (int index = 0; index < names.length; index++) {
monitor.checkCancelled();
setAnalysisOption(currentProgram, requestedName.trim(), "false");
if (registered[index]) setAnalysisOption(currentProgram, names[index], "false");
}
options = getCurrentAnalysisOptionsAndValues(currentProgram);
for (String requestedName : names) {
JsonArray analyzers = new JsonArray();
for (int index = 0; index < names.length; index++) {
monitor.checkCancelled();
String name = requestedName.trim();
if (!"false".equals(options.get(name))) {
throw new IllegalStateException("Ghidra analyzer remained enabled: " + name);
String name = names[index];
if (registered[index] && !"false".equals(options.get(name))) {
throw new IllegalStateException(SETTING + " names " + name +
", which remained enabled for this program");
}
JsonObject analyzer = new JsonObject();
analyzer.addProperty("name", name);
analyzer.addProperty("status", registered[index] ? "disabled" : "not_applicable");
analyzers.add(analyzer);
}
JsonObject report = new JsonObject();
report.addProperty("format", "rea-ghidra-disabled-analyzers-v1");
report.add("analyzers", analyzers);
currentProgram.getOptions(Program.PROGRAM_INFO).setString(REPORT_OPTION, report.toString());
println("REA disabled analyzers: " + report);
}
/** The same analyzer universe AutoAnalysisManager filters by canAnalyze(program). */
private static Set<String> installedAnalyzerNames() {
Set<String> names = new HashSet<>();
for (Analyzer analyzer : ClassSearcher.getInstances(Analyzer.class)) {
names.add(analyzer.getName());
}
return names;
}
}
+71 -18
View File
@@ -140,6 +140,7 @@ public final class ReaGhidraBridge extends HeadlessScript {
"params"
);
private static final String[] CAPABILITIES = {
"read_binary_inventory_summary",
"annotate_native_function",
"set_address_name",
"set_addresses_names",
@@ -427,6 +428,7 @@ public final class ReaGhidraBridge extends HeadlessScript {
);
}
return switch (request.method) {
case "read_binary_inventory_summary" -> binaryInventorySummary(request.params);
case "inspect_native_load_image" -> inspectNativeLoadImage(request.params);
case "read_bytes" -> readMemoryBytes(request.params);
case "search_bytes" -> searchMemoryBytes(request.params, descriptor);
@@ -456,6 +458,7 @@ public final class ReaGhidraBridge extends HeadlessScript {
FunctionAnalysisObservation observation = analyzeFunctionObservation(request.params);
JsonObject result = functionObservation(observation.dossier(), observation.limitations());
result.add("relationship_coverage", observation.relationshipCoverage());
if (observation.decompilerFailed()) result.addProperty("cache_policy", "live");
yield result;
}
case "set_address_name", "set_addresses_names" -> {
@@ -520,6 +523,11 @@ public final class ReaGhidraBridge extends HeadlessScript {
String seeds = currentProgram.getOptions(ghidra.program.model.listing.Program.PROGRAM_INFO)
.getString("REA analysis seeds", null);
if (seeds != null) result.add("analysis_seeds", JsonParser.parseString(seeds));
// Written by ReaGhidraAnalysisOptions before auto-analysis; REA checks it
// against the committed profile.
String disabledAnalyzers = currentProgram.getOptions(ghidra.program.model.listing.Program.PROGRAM_INFO)
.getString("REA disabled analyzers", null);
if (disabledAnalyzers != null) result.add("disabled_analyzers", JsonParser.parseString(disabledAnalyzers));
// Written by ReaGhidraMipsSoftFloat after analysis; REA requires it
// exactly when the committed profile declares soft-float storage.
String softFloat = currentProgram.getOptions(ghidra.program.model.listing.Program.PROGRAM_INFO)
@@ -1260,9 +1268,11 @@ public final class ReaGhidraBridge extends HeadlessScript {
result.add("procedure", function == null ? JsonNull.INSTANCE : GSON.toJsonTree(canonicalAddress(function.getEntryPoint())));
result.addProperty("architecture", currentProgram.getLanguageID().getIdAsString());
result.addProperty("mode", currentProgram.getLanguage().getLanguageDescription().getVariant());
if (currentProgram.getLanguageID().getIdAsString().startsWith("MIPS:")) {
if (currentProgram.getLanguageID().getIdAsString().startsWith("MIPS:") ||
currentProgram.getLanguageID().getIdAsString().startsWith("ARM:")) {
JsonArray decoderContext = new JsonArray();
var modeRegister = currentProgram.getRegister("ISA_MODE");
String regName = currentProgram.getLanguageID().getIdAsString().startsWith("MIPS:") ? "ISA_MODE" : "TMode";
var modeRegister = currentProgram.getRegister(regName);
if (modeRegister != null) {
var modeValue = currentProgram.getProgramContext().getValue(modeRegister, address, false);
JsonObject context = new JsonObject();
@@ -1538,12 +1548,23 @@ public final class ReaGhidraBridge extends HeadlessScript {
requireKeys(params, Set.of("procedure"));
Function function = resolveProcedure(requireString(params, "procedure"));
InstructionScan scan = scanInstructions(function, Integer.MAX_VALUE);
DecompileResults decompilation = decompile(function);
DecompileResults decompilation;
String decompilerFailure = null;
try {
decompilation = decompile(function);
} catch (RequestFailure failure) {
// The listing, reference and CFG facts below do not depend on the
// native decompiler. Cancellation still ends the request.
if (!failure.code.equals("decompile_failed")) throw failure;
decompilation = null;
decompilerFailure = failure.getMessage().strip();
}
String pseudocode = decompiledText(decompilation);
JsonObject availability = new JsonObject();
availability.add("pseudocode", facetAvailability(
pseudocode != null,
function.isExternal() ? "Ghidra does not decompile external functions."
decompilerFailure != null ? decompilerFailure
: function.isExternal() ? "Ghidra does not decompile external functions."
: function.getBody().isEmpty() ? "Ghidra has no function body to decompile."
: "Ghidra completed decompilation without returning pseudocode."
));
@@ -1592,9 +1613,16 @@ public final class ReaGhidraBridge extends HeadlessScript {
result.add("basic_blocks", blocks);
result.add(
"native_api",
nativeApiBoundary(function, decompilation)
decompilerFailure == null
? nativeApiBoundary(function, decompilation)
: unavailableNativeApiBoundary(decompilerFailure)
);
result.add(
"native_value_flow",
decompilerFailure == null
? nativeValueFlow(decompilation)
: unavailableNativeValueFlow(decompilerFailure)
);
result.add("native_value_flow", nativeValueFlow(decompilation));
// Successful full queries distinguish collected empty sets from missing facts.
// Availability does not claim that Ghidra resolved every runtime relationship.
for (String facet : List.of("comments", "callers", "callees", "references", "strings_names", "cfg")) {
@@ -1641,6 +1669,12 @@ public final class ReaGhidraBridge extends HeadlessScript {
!decompilation.getErrorMessage().isBlank()) {
limitations.add("Ghidra decompiler diagnostic: " + decompilation.getErrorMessage());
}
if (decompilerFailure != null) {
limitations.add(
decompilerFailure + (decompilerFailure.endsWith(".") ? " " : ". ") +
"Pseudocode, the native API boundary and p-code value flow are unavailable for this procedure; assembly, comments, callers, callees, references, strings, names and basic blocks come from the Ghidra program listing and were still collected."
);
}
result.add("limitations", limitations);
monitor.checkCancelled();
int missingCallers = 0;
@@ -1649,25 +1683,16 @@ public final class ReaGhidraBridge extends HeadlessScript {
if (unrepresentedCaller(reference, callerFunctions)) missingCallers++;
}
return new FunctionAnalysisObservation(result, calls.limitations(),
callRelationshipCoverage(calls, missingCallers));
callRelationshipCoverage(calls, missingCallers), decompilerFailure != null);
}
private JsonObject nativeValueFlow(DecompileResults decompilation) throws Exception {
HighFunction highFunction =
decompilation == null ? null : decompilation.getHighFunction();
if (highFunction == null) {
JsonObject unavailable = new JsonObject();
unavailable.addProperty("available", false);
unavailable.addProperty(
"reason",
return unavailableNativeValueFlow(
"Ghidra has no HighFunction p-code model for this procedure"
);
JsonArray limitations = new JsonArray();
limitations.add(
"No p-code operations or def-use relationships were recovered for this procedure."
);
unavailable.add("limitations", limitations);
return unavailable;
}
List<PcodeOpAST> selected = new ArrayList<>();
@@ -1974,6 +1999,18 @@ public final class ReaGhidraBridge extends HeadlessScript {
return result;
}
private JsonObject binaryInventorySummary(JsonObject params) throws Exception {
requireKeys(params, Set.of());
JsonObject result = new JsonObject();
result.addProperty("document", currentProgram.getName());
result.add("segments", segments());
// Count the owning inventories, preserving their exact discovery rules
// without serializing function identities or string values for counts.
result.addProperty("procedure_count", procedures().size());
result.addProperty("string_count", strings().size());
return result;
}
private JsonArray listNames(JsonObject params) throws Exception {
requireKeys(params, Set.of("document", "address"));
requireDocument(params);
@@ -2000,6 +2037,10 @@ public final class ReaGhidraBridge extends HeadlessScript {
private JsonArray listSegments(JsonObject params) {
requireKeys(params, Set.of("document"));
requireDocument(params);
return segments();
}
private JsonArray segments() {
JsonArray result = new JsonArray();
String imageBase = canonicalAddress(currentProgram.getImageBase());
MemoryBlock[] blocks = currentProgram.getMemory().getBlocks();
@@ -2339,6 +2380,18 @@ public final class ReaGhidraBridge extends HeadlessScript {
return result;
}
private static JsonObject unavailableNativeValueFlow(String reason) {
JsonObject unavailable = new JsonObject();
unavailable.addProperty("available", false);
unavailable.addProperty("reason", reason);
JsonArray limitations = new JsonArray();
limitations.add(
"No p-code operations or def-use relationships were recovered for this procedure."
);
unavailable.add("limitations", limitations);
return unavailable;
}
private static JsonObject unavailableNativeApiBoundary(String reason) {
JsonObject unavailable = new JsonObject();
unavailable.addProperty("available", false);
@@ -4857,7 +4910,7 @@ public final class ReaGhidraBridge extends HeadlessScript {
private record InventoryItem(Address address, String value, JsonObject facts) {}
private record AddressNameRequest(String inputAddress, Address address, String name) {}
private record FunctionEntry(Function function, InventoryItem item) {}
private record FunctionAnalysisObservation(JsonObject dossier, List<String> limitations, JsonObject relationshipCoverage) {}
private record FunctionAnalysisObservation(JsonObject dossier, List<String> limitations, JsonObject relationshipCoverage, boolean decompilerFailed) {}
private record CallBoundaryObservation(List<String> limitations, Integer unrepresentedCalleeCalls, JsonArray unresolvedCalls) {}
private record SwiftStringLiteralDecode(JsonArray literals, JsonArray omissions) {}
private record SwiftLiteralLayout(
+166 -25
View File
@@ -8,12 +8,100 @@ API access on this thread: moving dispatch to a worker can deadlock Hopper.
import json
import hmac
import os
import re
import socket
from typing import Any, Optional, Protocol, Sequence
BAD_ADDRESSES = (-1, 0xFFFFFFFFFFFFFFFF, None)
# The native label setter truncates NSString names at this UTF-16 extent.
HOPPER_SYMBOL_NAME_UTF16_UNITS = 1024
# Request JSON payload budget in bytes, excluding the trailing LF.
# REA_BRIDGE_MAX_REQUEST_BYTES selects it. Unset stays 16777216.
# 7464 is the authenticated Cutter frame of the largest bounded symbol name.
# Cutter's 64-receipt recovery status is smaller with a null path. A path
# that does not fit is null, not an older descriptor path.
# 536870887 is Node's 536870888-character string ceiling minus the framing LF.
# The ceiling is the encoded payload; JSON expansion is measured before it.
_BRIDGE_MAX_REQUEST_BYTES_SETTING = "REA_BRIDGE_MAX_REQUEST_BYTES"
DEFAULT_BRIDGE_MAX_REQUEST_BYTES = 16 * 1024 * 1024
MINIMUM_BRIDGE_MAX_REQUEST_BYTES = 7464
MAXIMUM_BRIDGE_MAX_REQUEST_BYTES = 536870887
def _bridge_max_request_bytes_constraint():
return (
"REA_BRIDGE_MAX_REQUEST_BYTES must be a positive decimal integer "
"from %d to %d bytes; the default is %d bytes (16 MiB). "
"A value below %d bytes cannot carry the largest admitted "
"symbol-name command, and a larger value is refused so the encoded "
"payload plus its framing LF stays one string under Node's "
"single-string ceiling."
% (
MINIMUM_BRIDGE_MAX_REQUEST_BYTES,
MAXIMUM_BRIDGE_MAX_REQUEST_BYTES,
DEFAULT_BRIDGE_MAX_REQUEST_BYTES,
MINIMUM_BRIDGE_MAX_REQUEST_BYTES,
)
)
def _parse_bridge_max_request_bytes(raw):
"""Refuse a non-decimal or out-of-range ceiling. Never fall back."""
constraint = _bridge_max_request_bytes_constraint()
if isinstance(raw, bool):
raise RuntimeError(constraint)
if isinstance(raw, int):
value = raw
elif isinstance(raw, str):
# ASCII digits only. str.isdigit() accepts numerals such as U+0668
# that the TypeScript setting rejects.
if re.fullmatch(r"[1-9][0-9]*", raw) is None:
raise RuntimeError(constraint)
value = int(raw)
else:
raise RuntimeError(constraint)
if (
value < MINIMUM_BRIDGE_MAX_REQUEST_BYTES
or value > MAXIMUM_BRIDGE_MAX_REQUEST_BYTES
):
raise RuntimeError(constraint)
return value
def _resolve_bridge_max_request_bytes():
"""Prefer the bootstrap injection, then the process environment, then 16 MiB."""
injected = globals().get(_BRIDGE_MAX_REQUEST_BYTES_SETTING)
if injected is not None:
return _parse_bridge_max_request_bytes(injected), "setting"
raw = os.environ.get(_BRIDGE_MAX_REQUEST_BYTES_SETTING)
if raw is None:
return DEFAULT_BRIDGE_MAX_REQUEST_BYTES, "default"
return _parse_bridge_max_request_bytes(raw), "setting"
MAX_REQUEST_BYTES, BRIDGE_MAX_REQUEST_BYTES_SOURCE = (
_resolve_bridge_max_request_bytes()
)
def _bridge_max_request_bytes_recovery():
return (
"The effective REA_BRIDGE_MAX_REQUEST_BYTES value is %d bytes "
"(source %s; default %d, floor %d, maximum %d). "
"Set REA_BRIDGE_MAX_REQUEST_BYTES in the environment that starts REA "
"to a decimal integer from %d to %d, then restart REA. "
"Restart Hopper or Cutter from that same environment so the bridge "
"uses this ceiling."
% (
MAX_REQUEST_BYTES,
BRIDGE_MAX_REQUEST_BYTES_SOURCE,
DEFAULT_BRIDGE_MAX_REQUEST_BYTES,
MINIMUM_BRIDGE_MAX_REQUEST_BYTES,
MAXIMUM_BRIDGE_MAX_REQUEST_BYTES,
MINIMUM_BRIDGE_MAX_REQUEST_BYTES,
MAXIMUM_BRIDGE_MAX_REQUEST_BYTES,
)
)
_rea_session_document = None
_search_inventory_cache = {}
_pseudocode_cache = {}
@@ -236,6 +324,15 @@ def _name_matches(document, address, name):
return observed == name or (not name and observed is None)
def _bookmark_name(document, address):
getter = getattr(document, "getBookmarkName", None)
if not callable(getter):
raise CapabilityUnavailableError(
"Hopper's public Python API does not expose getBookmarkName"
)
return getter(address)
def _address(document, value=None):
"""Keep explicit hexadecimal coordinates distinct from symbol names."""
if value is None:
@@ -431,10 +528,6 @@ def _procedure_map(document):
return result
def _strings(document):
return {address: record["value"] for address, record in _string_inventory(document).items()}
def _string_record(segment, address, display):
"""Read one native typed object, retaining its shortened display separately."""
length = segment.getObjectLength(address)
@@ -521,7 +614,7 @@ def _search_inventory(document, kind):
if kind == "procedure":
values = _procedure_map(document)
elif kind == "string":
values = _strings(document)
values = _string_inventory(document)
elif kind == "name":
values = {_hex(address): value for address, value in _name_map(document).items()}
else:
@@ -548,23 +641,17 @@ def _search_results(document, kind, params):
else:
raise CapabilityUnavailableError("Regex matching requires REA's supervised worker; the native bridge only executes literal searches")
selected = []
for item in _search_inventory(document, kind):
if not matches(item[1]):
continue
selected.append(item)
if not selected:
return []
if kind == "string":
records = _string_inventory(document)
return [{"address": address, **records[address]} for address, _ in selected]
return [
{
"address": address,
"value": value,
}
for address, value in selected
return [
{"address": address, **record}
for address, record in _search_inventory(document, kind)
if matches(record["value"])
]
return [
{"address": address, "value": value}
for address, value in _search_inventory(document, kind)
if matches(value)
]
def _unavailable(reason):
@@ -741,6 +828,7 @@ def _dispatch(method, params):
"version": "1.0.0",
"run_id": REA_RUN_ID,
"process_id": os.getpid(),
"max_request_bytes": MAX_REQUEST_BYTES,
}
if method in ("shutdown", "shutdown_document"):
document = _session_document()
@@ -1004,15 +1092,46 @@ def _dispatch(method, params):
observed = getter(address)
return observed == comment or (comment == "" and observed is None)
if method == "list_bookmarks":
return [{"address": _hex(item), "name": document.getBookmarkName(item)} for item in document.getBookmarks()]
return [{"address": _hex(item), "name": _bookmark_name(document, item)} for item in document.getBookmarks()]
if method == "set_bookmark":
address = _address(document, params.get("address"))
_segment(document, address)
name = params.get("name")
if name is not None:
_api_text(name, "Bookmark name")
document.setBookmarkAtAddress(address, name)
return document.hasBookmarkAtAddress(address)
has_bookmark = getattr(document, "hasBookmarkAtAddress", None)
if not callable(has_bookmark):
raise CapabilityUnavailableError(
"Hopper's public Python API does not expose hasBookmarkAtAddress"
)
exists = has_bookmark(address)
if name is None and exists:
# Omission means preserve the existing bookmark and its name.
return True
if not callable(getattr(document, "getBookmarkName", None)):
raise CapabilityUnavailableError(
"Hopper's public Python API does not expose getBookmarkName"
)
if name == "" and exists:
rename = getattr(document, "renameBookmarkAtAddress", None)
if not callable(rename):
raise CapabilityUnavailableError(
"Hopper's public Python API does not expose renameBookmarkAtAddress"
)
rename(address, name)
else:
setter = getattr(document, "setBookmarkAtAddress", None)
if not callable(setter):
raise CapabilityUnavailableError(
"Hopper's public Python API does not expose setBookmarkAtAddress"
)
setter(address, name)
if not has_bookmark(address):
return False
observed = _bookmark_name(document, address)
if name is None:
return observed is None or observed == ""
return observed == name or (name == "" and observed is None)
if method == "unset_bookmark":
address = _address(document, params.get("address"))
# Permit removing a legacy orphan bookmark, but do not report success
@@ -1030,9 +1149,23 @@ def _serve_connection(connection):
should_stop = False
try:
while True:
line = file.readline()
# Bound the read itself. The payload budget excludes LF, so permit
# one additional byte for the delimiter while still refusing an
# oversized unterminated peer without waiting for EOF.
line = file.readline(MAX_REQUEST_BYTES + 1)
if not line:
break
if not line.endswith(b"\n"):
message = (
"Bridge request exceeds the %d-byte payload limit. %s"
% (MAX_REQUEST_BYTES, _bridge_max_request_bytes_recovery())
if len(line) > MAX_REQUEST_BYTES
else "Incomplete bridge request frame"
)
_write_message(file, {"id": 0, "error": _safe_diagnostic(
InvalidRequestError(message)
)})
break
request_id = None
authenticated = False
should_stop = False
@@ -1057,6 +1190,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",
@@ -1148,13 +1285,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
+48 -4
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)
@@ -630,8 +662,9 @@ 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()
exit_observed = _retire_failed_launch(
process, config, listener, stdout, stderr
)
return {
"status": "launch-error",
"error": error.GetCString(),
@@ -640,15 +673,26 @@ def trace(debugger, config):
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)
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()
+132
View File
@@ -15,6 +15,8 @@ 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")
)
@@ -23,6 +25,136 @@ 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)
+65 -7
View File
@@ -12,16 +12,29 @@ struct ProcessIdentityObservation: Encodable {
let pid: Int32
let state: String
let identity: String?
let effective_uid: UInt32?
let lifecycle: String?
let relation: ProcessRelationObservation?
let reason: String?
}
private struct ProcessMetadata {
struct ProcessRelationObservation: Encodable {
let state: String
let parent_pid: Int32
let process_group_id: Int32
}
struct ProcessMetadata {
let identity: String
let effectiveUid: UInt32
let lifecycle: String
let parentPid: Int32
let processGroupId: Int32
let pointerSize: Int
}
struct ProcessSnapshot {
fileprivate let metadata: [Int32: ProcessMetadata]
let metadata: [Int32: ProcessMetadata]
let failure: String?
}
@@ -75,6 +88,10 @@ func readProcessSnapshot(
process.kp_proc.p_pid,
ProcessMetadata(
identity: "\(start.tv_sec):\(start.tv_usec)",
effectiveUid: process.kp_eproc.e_ucred.cr_uid,
lifecycle: process.kp_proc.p_stat == SZOMB ? "zombie" : "non-zombie",
parentPid: process.kp_eproc.e_ppid,
processGroupId: process.kp_eproc.e_pgid,
pointerSize: process.kp_proc.p_flag & P_LP64 != 0 ? 8 : 4
)
)
@@ -87,20 +104,61 @@ func readProcessSnapshot(
}
func readProcessIdentities(pids: [Int32]) -> [ProcessIdentityObservation] {
let snapshot = readProcessSnapshot()
readProcessIdentities(pids: pids, snapshot: readProcessSnapshot())
}
func readProcessIdentities(
pids: [Int32],
snapshot: ProcessSnapshot
) -> [ProcessIdentityObservation] {
return pids.map { pid in
guard pid > 0 else {
return unavailableIdentity(pid: pid, reason: "invalid_process_id")
}
guard let metadata = snapshot.metadata[pid] else {
return unavailableIdentity(
if let failure = snapshot.failure {
return unavailableIdentity(pid: pid, reason: failure)
}
// This records absence from this successful KERN_PROC_ALL snapshot only.
// Unlike the Linux procfs path, it has no PID-specific kill(0)
// corroboration; callers must not treat it as universal or future exit
// proof.
return ProcessIdentityObservation(
pid: pid,
reason: snapshot.failure ?? "process_not_in_snapshot"
state: "absent",
identity: nil,
effective_uid: nil,
lifecycle: nil,
relation: nil,
reason: "not-in-snapshot"
)
}
return ProcessIdentityObservation(pid: pid, state: "readable", identity: metadata.identity, reason: nil)
return ProcessIdentityObservation(
pid: pid,
state: "readable",
identity: metadata.identity,
effective_uid: metadata.effectiveUid,
lifecycle: metadata.lifecycle,
relation: ProcessRelationObservation(
state: "observed",
parent_pid: metadata.parentPid,
process_group_id: metadata.processGroupId
),
reason: nil
)
}
}
private func unavailableIdentity(pid: Int32, reason: String) -> ProcessIdentityObservation {
ProcessIdentityObservation(pid: pid, state: "unavailable", identity: nil, reason: reason)
ProcessIdentityObservation(
pid: pid,
state: "unavailable",
identity: nil,
effective_uid: nil,
lifecycle: nil,
relation: nil,
reason: reason
)
}
enum RunTokenReadError: Error {
+16 -3
View File
@@ -20,8 +20,9 @@ def inspect(request):
root = Path(os.environ["REA_CORE_CONTEXT_REQUEST"]).parent
empty = root / "no-libraries"
empty.mkdir(mode=0o700)
for command in ["set auto-load off", "set debuginfod enabled off", "set auto-solib-add off", "set history save off", "set pagination off",
"set sysroot ./no-libraries", "set solib-search-path ./no-libraries"]:
for command in ["set auto-load off", "set debuginfod enabled off", "set auto-solib-add off", "set history save off", "set pagination off", "set target-file-system-kind unix",
"set sysroot ./no-libraries", "set solib-search-path ./no-libraries",
"set debug-file-directory ./no-libraries"]:
gdb.execute(command, to_string=True)
entry = request["gdbinit"]
try:
@@ -38,7 +39,19 @@ def inspect(request):
# relative filename in the owned cwd so no selected path becomes GDB syntax.
if Path(request["snapshot_path"]) != root.parent / "object.snapshot":
raise ContextFailure("decoder", "Core context request did not select the shared owned snapshot.")
# GDB 16 searches AT_EXECFN/NT_FILE outside sysroot when no executable BFD
# is selected. Occupy that slot with the already running, owned debugger;
# exec-file loads no symbols and never runs it. Clear it before any query.
# /proc/self/exe identifies this process, not the core's historical PID.
architecture = {"x86_64-little-endian": "i386:x86-64", "aarch64-little-endian": "aarch64"}.get(request["architecture"])
if architecture is None:
raise ContextFailure("unsupported", "Unsupported recorded core architecture.")
gdb.execute("exec-file /proc/self/exe", to_string=True)
# The placeholder must not select the core's architecture. Reuse the
# decoder's validated profile, including for cross-architecture GDB builds.
gdb.execute("set architecture " + architecture, to_string=True)
gdb.execute("core-file ../object.snapshot", to_string=True)
gdb.execute("file", to_string=True)
inferior = gdb.selected_inferior()
if inferior.connection is None or inferior.connection.type != "core" or gdb.current_progspace().filename is not None:
raise ContextFailure("decoder", "Requested core-only boundary was not established; no mapping query performed.")
@@ -82,7 +95,7 @@ def main():
try:
encoded = json.dumps(reply, ensure_ascii=True, allow_nan=False).encode("utf-8")
if len(encoded) > OUTPUT_BYTES:
encoded = json.dumps({"ok": False, "reason": "output-limit", "message": "Complete debugger context exceeds64 MiB reply budget."}).encode("utf-8")
encoded = json.dumps({"ok": False, "reason": "output-limit", "message": "Complete debugger context exceeds the 64 MiB reply budget."}).encode("utf-8")
descriptor = os.open(request["reply_path"], os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)
with os.fdopen(descriptor, "wb") as handle:
handle.write(encoded)
+126
View File
@@ -0,0 +1,126 @@
"""Producer regressions with controlled GDB state, not native debugger qualification."""
import json
import os
from pathlib import Path
import runpy
import sys
import tempfile
import types
import unittest
from unittest.mock import patch
BRIDGE = Path(__file__).with_name("core_context.py")
class CoreContextTests(unittest.TestCase):
def inspect(self, failure=None, connection="core", symbols=None, architecture="x86_64-little-endian", objfile="recorded-executable"):
commands, reads, queries = [], [], []
state = {"executable": None, "symbols": None, "connection": None}
gdb = types.ModuleType("gdb")
gdb.VERSION = "16.3"
gdb.selected_inferior = lambda: types.SimpleNamespace(
connection=state["connection"], pid=123)
gdb.current_progspace = lambda: types.SimpleNamespace(filename=state["symbols"])
def execute(command, to_string):
self.assertTrue(to_string)
commands.append(command)
if command == failure:
raise RuntimeError("controlled command failure")
if command == "exec-file /proc/self/exe":
state["executable"] = "/proc/self/exe"
elif command == "core-file ../object.snapshot":
if state["executable"] is None:
reads.append("recorded-executable")
state["executable"] = state["symbols"] = "recorded-executable"
state["connection"] = types.SimpleNamespace(type=connection)
elif command == "file":
state["executable"] = None
state["symbols"] = symbols
gdb.execute = execute
pwndbg = types.ModuleType("pwndbg")
pwndbg.aglib = types.ModuleType("pwndbg.aglib")
pwndbg.aglib.vmmap = types.ModuleType("pwndbg.aglib.vmmap")
pwndbg.lib = types.ModuleType("pwndbg.lib")
pwndbg.lib.version = types.ModuleType("pwndbg.lib.version")
pwndbg.lib.version.__version__ = "2026.09.15"
def maps():
self.assertIsNone(state["executable"], "Helper executable must be discarded before mapping queries")
queries.append("maps")
return [types.SimpleNamespace(start=4096, end=8192, offset=0, flags=5, objfile=objfile)]
pwndbg.aglib.vmmap.get = maps
modules = {module.__name__: module for module in [
gdb, pwndbg, pwndbg.aglib, pwndbg.aglib.vmmap, pwndbg.lib, pwndbg.lib.version]}
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
stage = root / "debugger"
stage.mkdir()
plugin = root / "gdbinit.py"
plugin.write_text("")
request = stage / "request.json"
reply = stage / "reply.json"
request.write_text(json.dumps({"gdbinit": str(plugin), "architecture": architecture, "snapshot_path": str(root / "object.snapshot"), "reply_path": str(reply)}))
with patch.dict(sys.modules, modules), patch.dict(os.environ, REA_CORE_CONTEXT_REQUEST=str(request)):
runpy.run_path(str(BRIDGE))
result = json.loads(reply.read_text())
return result, commands, reads, queries
def test_core_load_never_opens_recorded_executable_and_queries_only_after_discard(self):
result, commands, reads, queries = self.inspect()
self.assertTrue(result["ok"], result)
self.assertEqual(reads, [])
self.assertEqual(queries, ["maps"])
self.assertEqual(commands[-4:], ["exec-file /proc/self/exe", "set architecture i386:x86-64", "core-file ../object.snapshot", "file"])
self.assertLess(commands.index("set debug-file-directory ./no-libraries"), commands.index("core-file ../object.snapshot"))
self.assertIsNone(result["value"]["executable"])
self.assertEqual(result["value"]["maps"][0]["current_file_identity"], "unknown")
def test_failed_guard_core_load_or_discard_never_queries_mappings(self):
for command in ["exec-file /proc/self/exe", "set architecture i386:x86-64", "core-file ../object.snapshot", "file"]:
with self.subTest(command=command):
result, commands, reads, queries = self.inspect(failure=command)
self.assertFalse(result["ok"])
self.assertEqual(reads, [])
self.assertEqual(queries, [])
if command in ["exec-file /proc/self/exe", "set architecture i386:x86-64"]:
self.assertNotIn("core-file ../object.snapshot", commands)
def test_cross_architecture_uses_the_validated_decoder_profile(self):
result, commands, reads, queries = self.inspect(architecture="aarch64-little-endian")
self.assertTrue(result["ok"], result)
self.assertEqual(reads, [])
self.assertEqual(queries, ["maps"])
self.assertLess(commands.index("set architecture aarch64"), commands.index("core-file ../object.snapshot"))
def test_unknown_architecture_refuses_before_loading_core(self):
result, commands, reads, queries = self.inspect(architecture="unknown")
self.assertFalse(result["ok"])
self.assertEqual(result["reason"], "unsupported")
self.assertNotIn("core-file ../object.snapshot", commands)
self.assertEqual(reads, [])
self.assertEqual(queries, [])
def test_output_limit_message_separates_the_words_and_names_the_limit(self):
result, _, reads, queries = self.inspect(objfile="m" * (64 * 1024 * 1024))
self.assertFalse(result["ok"])
self.assertEqual(result["reason"], "output-limit")
self.assertNotIn("exceeds64", result["message"])
self.assertEqual(result["message"], "Complete debugger context exceeds the 64 MiB reply budget.")
self.assertEqual(reads, [])
self.assertEqual(queries, ["maps"])
def test_wrong_connection_or_remaining_symbols_never_queries_mappings(self):
for options in [{"connection": "native"}, {"symbols": "unexpected-symbols"}]:
with self.subTest(options=options):
result, _, reads, queries = self.inspect(**options)
self.assertFalse(result["ok"])
self.assertEqual(reads, [])
self.assertEqual(queries, [])
if __name__ == "__main__":
unittest.main()
+16
View File
@@ -29,7 +29,13 @@ export default defineConfig({
items: [
{ text: "Native binaries", link: "/native-investigation" },
{ text: "Go build metadata", link: "/go-binaries" },
{ text: "IL2CPP metadata", link: "/il2cpp-metadata" },
{ text: "Model artifacts", link: "/model-artifacts" },
{ text: "Rust dependencies", link: "/rust-binaries" },
{ text: "PE certificates", link: "/pe-certificates" },
{ text: "ELF notes", link: "/elf-notes" },
{ text: "CPython pyc files", link: "/python-pyc" },
{ text: "Erlang BEAM", link: "/erlang-beam" },
{
text: "JavaScript and Electron",
link: "/javascript-artifact-reconstruction",
@@ -43,12 +49,18 @@ export default defineConfig({
text: "Web network captures",
link: "/web-network-captures",
},
{ text: "Agent environments", link: "/agent-environments" },
{ 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: "Python packaged applications",
link: "/python-packaged-applications",
},
{ text: "Android applications", link: "/android-analysis" },
{ text: "Apple applications", link: "/apple-application-analysis" },
{ text: "Desktop installations", link: "/desktop-applications" },
{ text: "Firmware", link: "/firmware-analysis" },
{ text: "SQLite databases", link: "/sqlite-databases" },
{ text: "Packet captures", link: "/packet-captures" },
@@ -75,6 +87,10 @@ export default defineConfig({
items: [
{ text: "MCP contracts", link: "/mcp-contracts" },
{ text: "Tool design", link: "/tool-design" },
{
text: "Admission limits and recovery",
link: "/design/limits-and-recovery",
},
{ text: "Testing", link: "/testing" },
{ text: "Client verification", link: "/client-verification" },
{
+51 -11
View File
@@ -90,7 +90,7 @@ existing analysis families directly, for example
| `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 |
| `inspect_adb_package` | read | `dumpsys package` projection: versions, install times, installer, flags, and count of listed requested-permission rows (including Unicode names) |
| `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 |
@@ -98,16 +98,42 @@ existing analysis families directly, for example
| `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 |
| `uninstall_adb_package` | **mutates device** | Requests package removal for user 0 via `adb shell pm uninstall --user 0`, without data retention |
| `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 |
When `push_adb_file` receives an existing directory, adb places the local file
inside it under the local basename; Evidence reports that effective file path
while retaining the caller's directory path in the input parameters. The
private read-only staging copy preserves the source file's executable bits.
For `inspect_adb_window`, `focus_contexts` retains each display header, its exact
display ID when recognized, and every focus declaration with its original line.
Declarations without a display header are explicitly `unscoped`. A missing
declaration differs from a declaration whose `value` is JSON `null`.
`source_section` identifies the selected dumpsys section; `observed_lines`
preserves its focus declarations and display headers in source order.
`focus_summary` is `single_context` only for one context with no repeated focus
field. The singular `focused_window` and `focused_app` summarize that context;
they do not establish device-wide focus. Multiple contexts or repeated fields
produce `ambiguous` with null singular fields and all declarations retained.
When no focus field was declared, the status is `unavailable`. REA reads the
displays section only when the windows section declares neither focus field.
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
- **Directory names retain whitespace and newlines.** Symlink targets do too,
@@ -115,8 +141,10 @@ device first. The install verdict and host APK digest remain available.
labelled hex in `unparsed_lines` and make directory coverage partial.
- **Inferences carry their basis.** Device `kind` reports
`kind_basis` (serial prefix or USB transport); APK `role` reports
`role_basis: file_name`; `emulator_observed` is the build's own
`ro.kernel.qemu` value, `null` when absent.
`role_basis: file_name`; `emulator_observed` is derived from the build's
`ro.kernel.qemu` property: `true` only when its value is `"1"`, `false` for
any other reported value, and `null` when the property is absent. The raw
property value remains available in `properties`.
- **Device paths are not host paths.** `base_apk_device_path` values live on
the device filesystem. Only pulls and captures produce host-local paths,
each paired with byte size and SHA-256 digest. A pulled file's host name is
@@ -141,14 +169,19 @@ device first. The install verdict and host APK digest remain available.
APKs keep other names report the `unknown` role rather than guessing.
- **Partial results keep facts.** Failed individual pulls return successful
artifacts with `coverage: "partial"` and per-file `failures`. Listings
that contain unrecognized lines report them as `unparsed_lines` and mark
partial coverage instead of dropping rows.
and package searches that contain unrecognized lines report the first ten
as `unparsed_lines`, state the exact total in a limitation when more were
seen, and mark 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 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
unless `overwrite` is set. A failed or unexpected existence probe stops
transfer even with `overwrite`; only a clean `test -e` exit of 1 with no
output is interpreted as absence. 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. A missing command or output without a
recognizable digest reports `device_digest_source: "unavailable"` with an
unverified-transfer limitation; 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`).
@@ -160,14 +193,21 @@ device first. The install verdict and host APK digest remain available.
The app/activity lifecycle tools use `am start -W` and `am force-stop`.
- Per-invocation budgets: client/devices 10-20 s, shell reads 20-60 s with
64 KiB-8 MiB capture caps (logcat 4 MiB, screen 32 MiB), transfers and
bugreports 600 s. Exceeded budgets report `resource_constraint`; stopped
commands report `provider_timeout`.
bugreports 600 s. Output capture overflow reports `resource_constraint`
and names the byte allowance. Deadline expiration, including the
client-version probe, reports `provider_timeout` and names the effective
milliseconds. This release has no runtime override for those deadlines or
capture caps. Command cancellation preserves the caller's exact reason;
a later abort does not replace an already completed command failure.
- Pull and capture outputs are additive: the destination must not already
exist. Re-running a completed pull refuses the existing directory instead
of overwriting evidence.
- 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
+851
View File
@@ -0,0 +1,851 @@
# Agent environments from recorded captures
Use `rea environment-serve` to serve an environment manifest as an agent
environment: an MCP server whose tools answer from what one retained web
network capture observed, plus the environment card the agentenv framework
reads to discover it. By default the server derives the
[state the capture observed](#the-state-a-capture-observed) at startup and
answers read tools from it, and the write tools whose effect the capture
confirmed by changing it; `--replay-only` replays the capture's records
instead. Nothing is fetched from the recorded application; every answer comes
from retained Evidence. `rea environment-export` writes the same pair as a
bundle folder `agent-env run` builds and runs; see
[Export a bundle](#export-a-bundle).
```sh
rea inspect-web-network-capture /absolute/session.har har --format json > capture.json
rea inspect-analysis-view view.json --format json > manifest.json
rea environment-serve manifest.json --evidence capture.json --port 8765
```
The first command inspects the capture; the second projects the
[environment manifest](web-network-captures.md#environment-manifest) view from
it, where `view.json` wraps the capture Evidence as the inline source:
```json
{
"source": { "kind": "inline", "evidence": { "evidence_id": "ev_…" } },
"view": {
"kind": "environment-manifest",
"template_rules": ["after-uid-literal", "opaque-token", "sibling-variance"]
}
}
```
The third command serves that manifest from the state it derives from the
capture; see [Serve from the state](#serve-from-the-state). Both served files
are complete REA Evidence JSON as the commands write it with `--format json`;
the `evidence` member of `view.json` is the whole of `capture.json`.
## From a browser scenario
The capture can also come from REA itself: a
[browser scenario](browser-scenario-contract.md) records a scripted session
in a browser REA launches, and its Evidence is a manifest parent like a HAR
inspection. Select the `network` event family and response bodies. A replayed
reply carries up to 256 KiB of a recorded body, but the scenario keeps at most
`max_network_body_bytes` of each body (64 KiB by default), and only within its
allocation budgets: base64 is charged about 11 bytes per body byte, and
redaction more the shorter a declared secret is (about 35 bytes per body byte
for a 12-character `admin_password`). A body over
`max_network_body_allocation_bytes` (1 MiB by default) is kept as `unavailable`
with that reason; one over `max_event_allocation_bytes` (4 MiB) or
`max_total_event_allocation_bytes` (16 MiB) loses its whole content event,
headers included, which the capture lists among its omissions. Either way its
tool replays no body. The example raises all four so that 256 KiB bodies are
kept when the declared secret has at least 12 characters:
```json
{
"browser": { "mode": "launch", "executable_path": "/usr/bin/chromium" },
"start_url": { "url": "http://127.0.0.1:5230/auth", "query": [] },
"secrets": [
{ "secret_id": "admin_password", "environment_variable": "APP_PASSWORD" }
],
"actions": [
{
"step_id": "user",
"action": "fill",
"locator": { "kind": "css", "selector": "input[name=username]" },
"value": { "source": "literal", "value": "admin" }
},
{
"step_id": "password",
"action": "fill",
"locator": { "kind": "css", "selector": "input[type=password]" },
"value": { "source": "secret", "secret_id": "admin_password" }
},
{
"step_id": "sign-in",
"action": "press",
"locator": { "kind": "css", "selector": "input[type=password]" },
"key": "Enter"
},
{
"step_id": "first-memo",
"action": "goto",
"destination": { "url": "http://127.0.0.1:5230/m/1", "query": [] },
"wait_until": "load"
}
],
"capture": {
"events": ["network"],
"network": {
"request_body": true,
"response_body": true,
"header_values": false
},
"limits": {
"max_network_body_bytes": 262144,
"max_network_body_allocation_bytes": 12582912,
"max_event_allocation_bytes": 12582912,
"max_total_event_allocation_bytes": 67108864
}
}
}
```
```sh
APP_PASSWORD=… rea capture-browser-scenario scenario.json --format json > capture.json
rea inspect-analysis-view view.json --format json > manifest.json # view.json wraps capture.json as before
rea environment-serve manifest.json --evidence capture.json --port 8765
```
Each network transaction becomes one record, in request order, and the
record's ordinal is its zero-based position; a transaction whose URL carried a
redacted secret is left unrepresented rather than templated, and one the
capture cannot attribute to a single request and response is left
unrepresented too. Tools are chosen by response media type, so a scenario that
selected neither response bodies nor header values is refused; one that
selected header values but no response bodies yields a manifest but no state,
so it is served by replay. The browser
scenario redacts credential headers and the secrets it declares; values the
application returns in response bodies, such as session tokens, are kept, and
the replayed body is the bytes the browser exposes for each response, with any
declared secret replaced by its redaction marker (a body that redaction had to
empty replays no body).
## What a manifest is
An environment manifest is a projection of one application's recorded request
lines into a tool surface: one tool per method and path template on the
primary origin, with inferred path parameters, observed query parameter
names, value-free body shapes and pointers to the parent records each tool
cites. It carries no bodies and no headers. The manifest's
`parent_evidence_id` names the capture Evidence it was projected from; the
serve command refuses a manifest whose parent is not the supplied `--evidence`
file with an `invalid_request` error naming both identifiers, so a manifest can
never be served from records it did not cite.
The MCP catalog and environment card use a 1 KiB UTF-8 budget for each
captured display sample. Observation, body-key and cited-record lists show
whole values with omission counts; long method, path and query-name displays
show a marked prefix. Complete values remain in manifest Evidence. Argument
names longer than 64 characters use collision-safe `path_<n>` or `query_<n>` aliases; replay maps
those aliases to the complete captured names.
Discovery is admitted as a complete catalog under a 1 MiB serialization
reservation before the listener binds. Reservation charges up to six bytes per
UTF-16 code unit for JSON escaping/UTF-8 expansion, plus schema overhead;
a catalog may therefore be refused even when its compact JSON is smaller.
This was calibrated with 1500 query fields and eight concurrent SDK discovery
requests (183,798-byte card, 507.27 MiB sampled process-family RSS). Over-capacity
catalogs return `resource_constraint`, retain complete input Evidence, and require
a smaller capture or manifest. Fields and tools are never silently omitted.
Served from the state, each description is longer and the card declares the
data plane, so a catalog replay admits can exceed the reservation; the state
is then unavailable and the server serves replay instead (see
[Serve from the state](#serve-from-the-state)).
## What replay means and does not mean
Replay answers every tool with `--replay-only` or when the state is unavailable,
and, served from the state, each read tool whose view the state does not back.
Each such MCP tool call selects one of the records its manifest tool cites and
returns that record's recorded response. Selection compares the supplied path
parameters against the record's path segments at the parameter indexes, then
the supplied query values against the record's query string, and keeps the
record with the most matches; ties keep manifest order. A query argument accepts
one string or an array of strings for repeated values; the array is matched
against every occurrence in captured order. The raw recorded query remains the
replay evidence. The result reports
where the answer came from:
```json
{
"status": 200,
"content_type": "application/json",
"body": { "id": 2, "login": "probe" },
"replay": {
"record_ordinal": 5,
"evidence_id": "ev_…",
"exact_match": true,
"recorded_path": "/api/users/2",
"recorded_query": null,
"recorded_line_truncated": false,
"body_recorded": true,
"body_encoding": "utf-8",
"truncated": false
}
}
```
`exact_match` is true when every supplied path parameter and query occurrence
matches the recorded one; it says nothing about parameters the caller omitted.
Comparison probes retain the raw query spelling and its decoded parameter pairs
in request order, including the interleaving of repeated values across names.
Standalone tool calls express repeated values as per-name arrays; when matching
records differ only in cross-name order, `exact_match` is false because those arguments cannot
identify that order. Recorded query parsing runs only when supplied query
arguments affect selection.
A path value matches a recorded segment as written or after percent-decoding;
when both kinds of match are available, the record whose segment equals the
value as written is chosen. `recorded_query` keeps the `?` of an explicit empty
query, and a recorded path or query longer than 1 KiB is cut in the reply with
`recorded_line_truncated: true`; selection always uses the full recorded line.
`content_type` also has a 1 KiB display bound with `content_type_truncated`; body
interpretation uses the full recorded media type.
When no record matches, the tool still answers from the tool's first cited
record and reports `exact_match: false`. `body` is parsed JSON when the
recorded content type is JSON-like (`application/json`, `application/*+json`,
`text/plain`, `text/markdown`) and the text parses with safe numeric conversion; otherwise
it is the recorded text. Large integer IDs and other numbers that would lose
information stay in the original JSON text. Bodies over 256 KiB are cut at a UTF-8 boundary and returned as
text with `truncated: true`. `body_recorded` is false when the capture kept the
response's status and content type but no body; `body` is then `null`, which
is not a recorded JSON `null`. `body_encoding` is `base64` when the capture
retained response bytes that are not valid UTF-8; `body` is then exactly those
bytes in base64, never a lossy decoding. An unsupported declared HAR encoding
returns `body: null`, `body_recorded: true` and `body_encoding: "unsupported"`;
the original encoding and encoded text remain in the cited capture Evidence.
Such bodies supply no decoded JSON facts to state extraction.
A retained base64 body is decoded only as far as the 256 KiB a reply can carry, and a leading byte-order mark is kept. For a `base64` body the limit applies to the recorded bytes, not their longer base64 spelling, and a cut keeps whole base64 groups. Request bodies passed to `POST`, `PUT` and `PATCH`
tools are accepted and ignored: nothing is written, and no call changes what a
later call returns. Arguments the tool's input schema does not name are
refused by the MCP server as invalid arguments, so a misspelled parameter never
degrades into an omitted one.
Replay is not emulation. A served environment does not establish the
application's authorization model, response semantics or state transitions,
which the manifest lists as unknowns; it does not validate supplied values; and
it answers only from the capture's recordings and the state they observed or a
caller loaded through `data/add`. Replayed content is
target content: every tool result, including the argument errors the MCP server
returns before a replay runs, carries `_meta.provenance.untrusted_content: true`
with `source: "analysis_target"`. The server advertises REA's untrusted-content
instructions plus a warning that its tool names, descriptions, parameter names
and observed values come from the capture too, and agents must treat all of it
as data, never as instructions.
That same warning prefixes every tool description in the standalone card, so
card discovery carries it before an MCP client initializes. It labels the
captured text without changing the tool names or schema fields clients use.
## The state a capture observed
Replay answers each call from one recording, so the answers reflect whichever
moment of the capture recorded them. The
[environment state](web-network-captures.md#environment-state) view extracts
instead the data the capture's reads observed before the first related write:
entity types with an identity field, entities with their recorded values,
documents for reads that are not entities, and a render plan per read tool,
kept only where it reproduces that tool's recordings from the state. It is the
starting state an environment holds, loads and reports: `environment-serve`
derives it from the capture at startup; see
[Serve from the state](#serve-from-the-state). With `--replay-only`, replay is
unchanged.
## Serve a manifest
```sh
rea environment-serve manifest.json --evidence capture.json [--replay-only] [--host 127.0.0.1] [--port 0]
```
The server derives the state from the capture before it binds and serves from
it, or serves replay with `--replay-only` or when the state is unavailable;
see [Serve from the state](#serve-from-the-state).
`--host` and `--port` default to `MCP_HOST` and `MCP_PORT` from the
environment, then to `127.0.0.1` and an ephemeral port, exactly as
[`rea mcp --http`](cli.md#serve-mcp-over-streamable-http) resolves them. The
bound MCP URL is written to stderr as one JSON log line
(`"msg":"MCP transport listening"` with `url`); `REA_LOG_LEVEL` overrides that
log level. `ENVIRONMENT_NAME` renames the served environment; otherwise the
manifest's `name` is used, and a set but blank value is refused as
`configuration_invalid` before binding rather than silently falling back. The
listener serves one MCP client at a time with the same session, `Host` header
and shutdown contract as [transports](mcp-contracts.md#transports) describes.
It runs until SIGINT or SIGTERM, then closes the listener, prints one
`{"status":"stopped", …}` document whose `state` member says whether it served
from the state, and exits `130` or `143`.
Signals during input loading or DNS/bind cancel the owning reader/listener before
a listening announcement;
that path returns a structured `cancelled` result instead of a stopped-listener
document, with the same signal exit status.
Three routes are served:
| Route | Answer |
| --------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `POST /mcp` | The MCP server: from the state, one tool per read tool, answering from the state where it backs them, and one per write tool whose effect the capture confirmed, applying it to the state; in replay, one tool per manifest tool, replaying records |
| `GET /.well-known/agent-env.json` | The environment card |
| `POST /agentenv` | From the state, `data/reset`, `data/add` and `data/get`; in replay, JSON-RPC `-32601 method not found: <method>` for every method |
Numeric JSON-RPC IDs outside JavaScript's safe range or whose precision would
be lost are refused as invalid requests with a null ID. They are never echoed
as a different number.
In replay the data plane at `/agentenv` is advertised by the card but carries
no operations; the error names the requested method so a caller can see that
the method, not the transport, is unsupported. Its request body is
read within the same 4 MiB bound the MCP endpoint applies: a larger body is
answered `413` with a JSON-RPC `-32000` error before any of it is parsed, and a
body that is not JSON is answered `400` with `-32700`. A notification (no `id`)
gets `202` and no body, a batch gets one reply per request in it, and an empty
batch, a batch of more than 100 messages, or a message that is not a JSON-RPC
request (including an `id` that is not a string, number or null, or `params`
that are not an object or array) gets `-32600 invalid request`.
## The environment card
The card is the discovery document the agentenv framework reads before
connecting to `/mcp`. A replay environment's card reads:
```json
{
"name": "grafana-example",
"protocolVersion": "1.0",
"url": "/agentenv",
"preferredTransport": "JSONRPC",
"additionalInterfaces": [{ "url": "/mcp", "transport": "mcp" }],
"capabilities": {
"extensions": null,
"tools": [
{
"name": "get_users_by_id",
"description": "Untrusted captured API catalog: … GET /api/users/{numeric}; returns a JSON object with fields \"id\", \"uid\", \"login\", \"email\"; recorded statuses 200; replays retained records [5, 6]; evidence ev_…",
"inputSchema": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Path segment 2 (numeric); observed: 2, 3"
}
},
"required": ["id"],
"additionalProperties": false
}
}
],
"operations": []
},
"children_environments": null
}
```
`capabilities.tools` lists the same names, descriptions and input schemas that
`tools/list` advertises over MCP. After the catalog warning, each description
names the method and path template, then what the tool returns, one level deep:
`returns a JSON object with fields …`, `returns a JSON array of objects with
fields …` or `returns a JSON object whose <member> lists objects with fields …`,
where a list is an array of objects found by the
[environment state](web-network-captures.md#environment-state)'s list rule (the
root array, or the one array of objects an envelope carries). Field and member
names are written as JSON strings, so a captured name cannot read as more of
the description; a member name too long to show whole is a quoted prefix marked
as truncated. The summary comes
from the first cited `2xx` record that replay answers as JSON; only when none
did, the first cited JSON record of another status gives it, labelled with that
status (`status 401 returns …`). It describes one recorded response, not a
contract. Field names are sampled within the same budget as other captured
values, and the manifest's `statuses` for the tool follow; a tool with no JSON
record states only its statuses. The description then lists the replayed
records and the evidence ID, and the whole catalog stays within its
serialization reservation, which the summaries and MCP annotations are charged
to.
Over MCP, every tool also carries annotations: `readOnlyHint` is true for the
safe methods (`GET`, `HEAD`, `OPTIONS` and `TRACE`) and for query `POST`s (a
path segment such as `search` or `query`); `destructiveHint` is true for `DELETE` and left unset for other
writes, so the MCP default (destructive) applies to them, and read-only tools
omit it because clients ignore it there; `idempotentHint` is true for the safe
methods, `PUT` and `DELETE`; and `openWorldHint` is false for every tool,
because answers come from the capture rather than the captured application. The
hints describe the captured method; replay itself changes nothing. The card
keeps the `name`, `description` and `inputSchema` of the echo example's tool
shape and carries no annotations.
Path parameters are required strings whose
description names the segment index, the templating rule and the observed
values (sampled within the list budget above; the manifest keeps them all);
query parameter names accept an optional string or array of strings (for
repeated values in captured order); write methods accept an
optional `body` object described by the recorded top-level key types. Each
input keeps its own argument: a query name equal to a path parameter's name is
advertised as `query_<name>`, a query name longer than 64 characters as
`query_<n>` (its position) with the captured name sampled in its description,
and the write body as `request_body` when a query parameter is named `body`. Every
schema closes with `additionalProperties: false`, a key the echo example's
schema does not carry.
In replay, `extensions`, `operations` and `children_environments` are fixed to
`null`, `[]` and `null`: a replay environment declares no extensions, no
data-plane operations and no children. Served
[from the state](#serve-from-the-state), the card declares the three data-plane
operations and one intake extension instead, and the catalog holds the read
tools and only the write tools whose effect the capture confirmed: a read tool
is described as answering from the state where the state backs it, and a
served write tool by the effect it applies to the state and the quoted body members it
accepts.
## Serve from the state
```sh
rea environment-serve manifest.json --evidence capture.json
```
Before it binds, the server derives the
[environment state](web-network-captures.md#environment-state) the capture
observed: it runs the `environment-state` view on the capture in-process with
the manifest's `template_rules` in the same order, exactly as
`inspect-analysis-view` projects it from the same capture, so the state
Evidence id it reports is the one that projection produces. The view's own
budget and refusals apply, and the result is checked like the view's Evidence
for this manifest: extracted from the served capture, with the manifest's
rules, naming only manifest tools. The server keeps one live state, shared by
every MCP session and the data plane, starting from the derived state.
A state that is unavailable does not stop the server: it logs a warning naming
the error `code` and message before it binds, then serves replay. The state is
unavailable when it cannot be derived, such as from a
`capture_browser_scenario` capture that selected header values but no response
bodies or over the view's budget, when `data/get` would write it past the 32 MiB
`data/add` intake limit, and when its catalog under the served name
exceeds the [discovery reservation](#what-a-manifest-is). That check runs
first, so no state is derived for it, and replay is then admitted exactly as
with `--replay-only`. `--replay-only`
serves replay without deriving anything; the card, tools and `/agentenv`
answers are then exactly those of replay. The stopped document's `state`
member reports which applied:
- `{"served": true, "evidence_id": "ev_…"}`: served from the derived state.
- `{"served": false, "reason": "replay-only"}`: `--replay-only` was given.
- `{"served": false, "reason": "unavailable", "code": "unsupported_target", "message": "…"}`:
the state is unavailable; `code` and `message` are those of the refusal
(`resource_constraint` for a catalog over the reservation), as the warning
logged them.
Each read tool answers according to its view in the state:
| View | Answer |
| ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `item` | The entity the path arguments name, rendered into the recorded envelope of `envelope_ordinal`. Arguments match as replay matches them: each collection segment equals the held collection's segment as written or after percent-decoding, and the identity is the last argument percent-decoded, then as written; values are never re-split on `/`. Any entity the state holds answers, including one `data/add` loaded; query arguments only decide `replay.exact_match`. An entity the state does not hold answers the tool's latest recorded 400, 404 or 410 response with `state.view` `missing`, or is a tool error when none was recorded; one lacking a field every recording carried is a tool error, unless a read showed the application leaves that field out; a body is never invented. |
| `list` | When the arguments name one of the lines the view checked exactly (its latest recordings before the first related write; every path and query value equal to the recording's, and every recorded query parameter supplied), that recorded envelope with its array rendered from the state: every entity of the collection in state order for `membership: "all"` or a line `all_record_ordinals` names, or the recorded members still held. A call with no query argument answers from such a line of the collection its path arguments name that only pages its list, with `replay.exact_match` false. On a line the view's `query` answers, and for a call whose every query argument has a role there (an argument-less call aside), the answer is the query evaluated over the state: filtered, searched, sorted and paged, with counters at the number listed and `query.totals` at the number matched, and `state.membership: "query"`; such a call answers on a query or whole line of its collection with `replay.exact_match` false. A value an `inert` parameter never recorded, or a parameter without a role, replays its recording. A member lacking a field every recording carried makes the call a tool error rather than a partial list. |
| `document` | When the arguments name the document's checked line exactly, the state's value for it; a tool error when the state holds none. |
| `recorded` | The recording, as replay answers it. |
A read with repeated query values uses replay, because the state query model
does not infer repeated-parameter semantics. A repeated-query write is refused
before it can change state for the same reason.
An item or list view that always spells a clock field otherwise than the
entity's stored value names that spelling in `clock_formats`, and a value it
never recorded, such as a created entity's clock, is rendered in it: a list that
writes `+00:00` where item reads write `Z` lists a new entity the same way.
A list or document call that names no checked line, including a line recorded
after a write or with other query values, answers from the recording. Every
answer has the replay shape plus `state`: `{"source": "state", "view": …}`
naming the entity, collection or document path, or `{"source": "recording",
"reason": …}` with the view's reason, `no-view`, `unrecorded-request`, `page-exceeded` (a call naming a page of a whole list that the collection has outgrown) or `projection` (a call to an item tool whose recordings asked for some fields only, naming such a parameter for an entity the state holds; the answer is a recording, so it does not show served writes). A
state answer carries the base record's status, content type and `replay`
provenance with `truncated: false`; one whose JSON would exceed 256 KiB is
refused as a `resource_constraint` tool error rather than cut. Extraction does
not state-back a document whose rendered JSON exceeds 256 KiB; that read stays
recorded (`rendered-oversize`) and replays the captured body. Query `POST`s
stay served and answer from their recordings with the reason `query-body`.
A line establishes whole-collection membership only from the beginning:
`offset`, `start`, `_start` and `page` must be zero when present. Positive or
malformed positions do not by themselves establish whole membership. A separate
query model may describe supported paging; an empty later page cannot start
returning earlier entities merely because it was empty in the capture.
`POST`, `PUT`, `PATCH` and `DELETE` tools are served only when the state's
[write plan](web-network-captures.md#environment-state) for them is served:
the capture's later reads confirmed the effect the plan models. Other write
tools are not served, so no call claims a change it does not make; each one's
plan states why in its `reason`. A served write changes the live state, which
every MCP session and `data/get` then see:
| Effect | Call |
| ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `create` | Adds an entity to the collection the path names, built from the observed entity with each ruled field taken from the call or generated, at the plan's `insert` position. A plan tied to its exemplar's collection writes only there. |
| `update` | Sets the sent members on the entity the path names, constants for members accepted only with their recorded value, and the clock, increment, slug, unique and renewed changes the plan models. |
| `delete` | Removes the entity the path names. |
| `document` | Merges the body into the plan's document object, or replaces it for `PUT`. |
Its answer has the status and content type of the plan's recorded response,
with the written entity or its identity rendered in, no body, or the recorded
body. A call whose body is not an object, sends a member the capture did not
show stored, sends a fixed member with another value or a member of another
JSON type, or lacks a required member is a tool error naming the accepted
members; so is a rendered answer missing a field the recorded response
carried, and an update or delete of an entity the state does not hold answers
the recorded 400, 404 or 410 of the tool or of an item view of the type, if
any. A refused call leaves the state unchanged. Values a write generates are
deterministic: the clock starts at `write_clock` and advances one second per
served write, sequences take one past both the largest held value and the
largest they generated, and UUIDs and tokens derive from the state Evidence
id, tool, field and write count, skipping held values. `data/reset` and
`data/add` restart the clock, counter and sequences, so the same calls after a
reset give the same state. Answers carry `state: {"source": "state", "view":
"create" | "update" | "delete", …}` or `"document-write"`.
The data plane serves three JSON-RPC methods:
| Method | Result |
| ------------ | --------------------------------------------------------------------------------------------------------- |
| `data/reset` | `{}` after restoring the state the Evidence holds |
| `data/add` | `{}` after replacing the whole state with one valid `rea-state/v1` document |
| `data/get` | `{"parts": [{"kind": "data", "data": <rea-state/v1 document>}]}` with `state_evidence_id` and the `types` |
Calls run one at a time in arrival order across requests. Every message of a
batch is queued when the batch arrives, so another request's call never lands
between them and each message sees the changes of those before it; a reset
sent while a download is loading applies after that load. `data/add`
takes exactly one part: a `data` part whose `data` is the document, or a
`file` part whose `file` carries `bytes` (base64 of UTF-8 JSON) or a `uri`,
either a `file://` URL of a local regular file, whose host is empty or
`localhost`, or an `https://` URL fetched without following redirects and
within 30 seconds, including reading the body; `mimeType`, when given, is
`application/json`. A `file://` URL naming another host, or a UNC path such as
`file:////server/share/state.json`, is refused before anything is opened, so a
load never reaches a network share. A loaded document is at most 32
MiB, and so is the state as `data/get` writes it back with the members a
document may omit (`types`, `state_evidence_id`, record citations), so a
reported state always loads again; an inline part also stays within the 4 MiB
request body bound, so larger states arrive by `uri`. Anything else, including `{"parts": []}`, is `-32602
invalid params`; a download that fails or answers a non-2xx status is
`-32000`. Errors never echo the uri or the content; a file or download failure
names its category in `error.data.reason`: `not-found`, `permission`,
`not-regular-file`, `changed` or `read-failed` for files, `timeout`,
`http-status` or `network` (connection, TLS or a refused redirect) for
downloads, and `cancelled` for a download the server's stop or its caller's
departure ended.
Served creates also bound additional collection history since the last reset
or `data/add` to 1 MiB of deterministic accounting: UTF-8 bytes for each new
collection spelling and its possible sequence keys, plus a 64-byte allowance
per collection and potential sequence-key record. The score bounds identifier
payload and a fixed record allowance; it is not a measurement or guarantee of
process memory. Collections
already present in the Evidence or loaded state are part of the state itself;
the store indexes their unique spellings so they remain known after deletion,
but they do not consume this additional-history budget. A create that would
exceed the budget is refused before the state changes. `data/reset` or `data/add`
clears generated history; if the same calls would hit the limit again, use
fewer distinct collection paths. The 1 MiB score ceiling is 1/32 of the
separate 32 MiB state-intake ceiling, reserving a small, explicit allowance
for identifiers retained beyond the loaded state. A loaded-state path that is
later deleted and then introduced again may consume the additional-history
budget when the store must retain it beyond the source entity.
Stopping the server cancels every pending call, and a caller that leaves before
its reply cancels its own: a download in progress ends, and calls not yet run
are answered `-32000` with `error.data.reason` `cancelled` without running. A
cancelled call leaves the state unchanged, and shutdown never waits for a
download.
One `/agentenv` response carries at most 64 MiB of replies, twice the 32 MiB
a loaded document may hold: room for a `data/get` of a loaded state, while a
batch of reads cannot multiply a large state into one body. Replies are
measured in order as they are produced, in bounded parts and before they are
assembled, and room is kept for every later reply's budget error and the
batch's brackets and commas, so one that would take the response past the
budget is never built whole and is answered instead with `-32000` whose `error.data` gives
`max_response_bytes` and the `reply_bytes` it would have taken. That call still
ran, so repeat such a read in another request. A reply no larger than that
error, such as a write's `{}`, is always kept.
```json
{
"rea_state": {
"version": 1,
"entities": [
{
"type": "/api/users",
"collection": "/api/users",
"id": "2",
"fields": { "id": 2, "login": "probe" }
}
],
"documents": []
}
}
```
Each entity's `type` is a state type's `collection_template`, its `collection`
matches that template segment by segment (a template parameter matches any one
segment), its identity field's value is its `id`, and its type, collection and
id are unique; `view_fields` is optional and names item or list views of its
type. Views render their values over the entity's fields, so a view's values
must not set the type's identity field to a value whose text differs from the
entity's `id`. Spell a collection as requests spell it, as `data/get` reports it: list
calls find entities under the checked line's recorded path. State collection
identity preserves repeated separators inside captured paths: `/api/books`
and `/api//books` are distinct. A single trailing slash continues to name the
same collection. Each document
names a `document` view once, at its initial document's `path`. The `version`,
and the `state_evidence_id` and `types` that `data/get` reports, must match
the served state, so a `data/get` result loads back unchanged. A number without
an exact native value, or whose serialized spelling would not be read as exact
(an integer from 2^53 below 1e21, such as `1e20`, in any spelling), or a
`__proto__` member is refused rather than altered.
An invalid document is `-32602` naming the first problem by JSON pointer, as in
`/rea_state/entities/3/collection` (also in `error.data.path`); a problem
inside caller data (`fields`, `value`, `types`) is located at that member, so
the pointer never names the caller's keys. A refused document changes nothing;
a valid one replaces the whole state at once. The order of `entities` is the
state order lists use.
Record citations stay Evidence provenance. An entity's `record_ordinals` may
be omitted or empty and a document's `record_ordinal` omitted; otherwise they
must be the served Evidence's citations for that entity or document, or the
document is refused. After loading, an entity or document whose values equal the Evidence's cites
the Evidence's records, and one whose values were edited or added cites none
(`record_ordinals: []`, no `record_ordinal`), so `data/get` never presents a
loaded value as recorded.
The card declares the operations and what `data/add` accepts:
```json
{
"extensions": [
{
"uri": "urn:agentenv:intake/v1",
"description": "data/add replaces the whole environment state with one rea-state/v1 document; data/get returns the current state in the same format.",
"params": {
"add": [
{ "part": "data", "format": "rea-state/v1", "load": "replace" },
{
"part": "file",
"format": "rea-state/v1",
"mimeTypes": ["application/json"],
"load": "replace"
}
],
"get": [{ "part": "data", "format": "rea-state/v1" }]
}
}
],
"operations": ["data/reset", "data/add", "data/get"]
}
```
The state starts as a projection of recorded reads and changes only through
served write tools and the data plane. List and document calls answer from the
state only for the lines their views checked, and every recorded view answers
from recordings; item calls answer for any held entity but render only the
envelope one recording carried. Values copied outside entity objects, derived
values and counters other than those that count a list stay as recorded, and a
served write changes only the one entity or document it writes: side effects
on counters, relations and copies of entities in other collections, reordering
after an update and referential rules on delete are not modelled. An exported
bundle carries no state file: its server derives the state at startup, as the
export does to predict what it serves.
## Export a bundle
`rea environment-export` writes the same pair as a bundle folder the agentenv
framework runs directly, shaped like its own echo example:
```sh
rea environment-export manifest.json --evidence capture.json ./grafana-example --json
agent-env run ./grafana-example
```
```text
grafana-example/
README.md provenance, what it serves, run command, rights note
envs/grafana-example/Dockerfile pinned image: node:24.22.1-bookworm-slim + rea-agents@<this version>
envs/grafana-example/env.toml environment_name = "grafana-example"
envs/grafana-example/manifest.json byte-exact copy of manifest.json
envs/grafana-example/evidence.json byte-exact copy of capture.json
artifacts/check_grafana-example/verify.py the verifier
tasks/smoke.json deploy_env (local sandbox), then env_outcome_verifier
```
The command reads each file once, after checking that reading and parsing the
whole file, including sealing traversal, fits the process's live heap headroom (a file that does not is
refused as a `resource_constraint` before it is read), then authenticates and cross-checks the
pair as `environment-serve` does, so a manifest projected from another capture
is refused with the same `invalid_request` before anything is written, and the
bytes it copies are the bytes it authenticated. The bundle directory must be
absent or empty; an occupied directory is refused and nothing in it changes.
After checking the bundle name, the export prepares the state from the capture
exactly as the bundle's server will at startup under that name, to predict what
the bundle serves; an unavailable state, including one whose catalog exceeds
the discovery reservation, does not stop the export, and the bundle then
serves replay. The complete tool catalog the bundle serves must also fit the
server's advertised catalog budget before any files are written. Privately parsed JSON is sealed once so authentication
and replay share immutable response trees rather than copying the capture repeatedly.
`--name` replaces the manifest's portable kebab-case name as the env folder and env id
(Windows device names such as `con` and `lpt1` are refused);
`--package-tarball <path>` copies a packed `rea-agents` tarball into the env
folder and makes the Dockerfile install it instead of the registry package and
remove it in the same layer, which is how a checkout rather than a release is
run. The command prints the
written files with their sizes, the pinned package source, the number of tools
the bundle serves as `tool_count`, and in `state` whether it serves from the
state, in the shape of the serve command's stopped document.
Export uses the shared output-tree owner to check directory identities around
exclusive creates, verify written bytes, and reject unowned entries before
reporting success. It does not delete an inspectable partial folder on failure.
Node's portable pathname APIs leave a syscall-boundary race, so the output's
parent directories must be trusted. On POSIX output files use mode `0600`;
Windows inherits ACLs and REA does not establish owner-only access there. Choose
an already private Windows destination when captured content needs it.
The Dockerfile sets `MCP_HOST=0.0.0.0` and `MCP_PORT=18765`, the port the
framework maps for an MCP server env; the framework's own deploy supplies
`MCP_HOST`, `MCP_PORT` and `ENVIRONMENT_NAME`, so the served card carries the
env's name. It installs Debian's `python3` because the framework's local
sandbox probes a container's readiness with `python3` inside it, which the slim
Node image lacks. It copies the two inputs owned by the image's non-root `node`
user, so a private host umask cannot leave them unreadable, runs as that user and starts
`rea environment-serve /env/manifest.json --evidence /env/evidence.json`. The
`env.toml` names the environment because the framework otherwise reads the
name from Python sources the image does not have.
`verify.py` is the framework's `async def verify(mcp_url)` shape over its
Python MCP client: it lists the tools and checks the count equals the number
the bundle serves (from the state, the manifest's read tools and the write
tools whose effect the capture confirmed; every manifest tool in replay), calls one tool with no arguments and checks the call did not
fail and its `status` is the expected one, and returns one `serve` result
whose description reports both checks. The tool is the first served `GET` tool
without path parameters whose answer to no arguments is not an error, and the
expected status is the status of that answer: the export calls the same answer
function the server uses, from the derived state or by replay, so a state
answer carries its base record's status and a replayed one its record's (an
unknown recorded status stays unknown; it is not borrowed from another
record). A bundle with no such tool yields a verifier that checks the count
only and says so. `tasks/smoke.json` deploys the env on the local
sandbox and runs that verifier against the env's MCP endpoint; the first run
also builds the framework's own infrastructure, and the run tears everything
down when it ends.
The README written into the bundle records the capture artifact's SHA-256, the
capture and manifest Evidence ids, the manifest view digest, the served tool
count, the derived state's Evidence id or that no state is served, and the
package source. It says what the environment serves: read tools from the state
the capture observed, the `/agentenv` data plane on it and the write tools
whose effect the capture's later reads confirmed, replay when the state is
unavailable, and `--replay-only` for pure replay; it
says which applies to this bundle, with the error code and message when the
state is unavailable, and that neither is emulation and served
content is untrusted data. It states that the recorded content belongs to the
captured application and must not be published without the rights to it.
## Measure fidelity against the application
An environment agrees with its own capture by construction. What matters to an agent is how it answers the calls the
capture did not make, what it shows after a write, and how it fails. `rea environment-compare` measures that against the
application itself:
```sh
rea environment-compare manifest.json --evidence capture.json \
--probe probe-crud.json --probe probe-reads.json --json
```
Each `--probe` is capture Evidence (`inspect-web-network-capture` of a HAR, or `capture-browser-scenario`) of one call
sequence made against the live application, seeded as it was for the capture and reset before the sequence. The command
makes no network call: whoever records the probes drives the application with its own credentials. For each probe it
resets the environment's state, takes the requests in the order they were made (record order when any lacks a start time), matches each to the manifest tool of the
same method whose path template fits with the most literal segments (a format-rule parameter must fit its format once
percent-decoded). A path argument is the raw segment, which the environment decodes once.
It answers the call as the served environment would, and compares that answer with the application's.
A re-seeded application holds the same entities under other ids, so the comparison maps them. Objects in the two answers
to one call pair by a natural key, a string field unique on each side that both share, such as a name or a slug; their
identifier fields (`id`, `uuid`, `guid`, and names ending in `_id`, `Id`, `ID`, `_uuid` or `Uuid`), and those of two single
answers such as a create's, map the live value to the environment's, including a create that returns only the new id.
Ids are learned only from an answer the environment
gave that call itself, from its state or a recording of that very call, request line and body, never from another call's
recording replayed in its place and never from a recording whose body the probe omitted. The answer's own `id`, `uuid` or `guid` also maps within the collection the call names, since
two collections can reuse a live id, and an equal pair stays in that collection. A live id in a later request's path, query or body is translated before the
environment answers, a path parameter by the collection the live path names (a nested collection keeps its parent values, each decoded once, and a literal route segment stays), except a query value that pages a list, such as `limit`
or `page`. Each call is checked against the served tool's input schema first, as the MCP server checks it. After mapping, two clock values compare by format only (ISO-8601 strings, and epoch numbers under
`at`, `time`, `date`, `timestamp`, `created`, `updated` or `modified` field names, including snake-case and camel-case forms), and two different values each side generated on its own compare equal: both UUIDs, or one length of
eight or more with the same character classes and a digit. The report counts each kind of normalised value.
ISO clock offsets are accepted only within RFC 3339's `00`–`23` hour and `00`–`59` minute ranges.
Timestamp ordering retains submillisecond precision when the source records it.
Comparison keeps response representations distinct: JSON values compare structurally, text compares as text, and a
retained base64 body compares by bytes (including with UTF-8 text carrying those same bytes), never by its base64
spelling. ID mappings apply at identifier fields, `ids`/`uuids`/`guids` lists, an entity's own identity within its
collection, and a string field learning confirmed as a state-held ID. ID mappings leave other numeric and string values unchanged. A
global conflict involving an equal pair is not used to normalize either pair; the report counts it as an ID conflict.
A step agrees when the statuses and the normalised bodies are equal. The report gives agreement overall, per probe and
per tool, `agreement_ignoring_order` for steps that differ only in item order, and each step's differences, at most 20,
with a JSON pointer and both values cut short:
| Category | Meaning |
| --------------- | ------------------------------------------------------------------------------------------------------------ |
| `status` | The statuses differ |
| `missing-field` | The application's object has a member the environment's lacks |
| `extra-field` | The environment's object has a member the application's lacks |
| `value` | Values differ, or one is an object or array and the other is not |
| `length` | Arrays differ in length, or not every item pairs |
| `order` | Paired items come in a different order |
| `clock-format` | Both are clocks, in different formats |
| `refused` | The environment refused the call |
| `not-served` | The request's tool is one the environment does not offer, such as a withheld write |
| `no-tool` | No manifest tool matches the request |
| `unmapped-id` | The application answered with success and the environment did not, for a path id the comparison never mapped |
What a probe did not observe is not counted as the application's behaviour. A record without a usable request line,
such as a redacted URL or a transaction the capture cannot attribute, is not replayed, nor is a request to another
origin than the application's (`other-origin`: the probe's origin with the most JSON-like records, as the manifest
chooses its own), and a request with no recorded
answer is made, since it may have changed the application, but not compared: each probe lists them under `unobserved` with the reason, and the summary counts them. A live
body the probe did not record compares by status only and is counted as `unobserved_bodies`, unless the answer never
carries one (204, 205, 304, `HEAD`). A browser probe captured without `capture.network.response_body` is refused, as
is one that makes POST, PUT or PATCH calls without `capture.network.request_body`.
Agreement covers only the calls the probes make, after the mapping the answers allow. Values compared by form, clocks
and generated values, can still differ in content, and an equal answer after a write shows the state matched for that
read, not that the write had no other effect.
## Compatibility verification
`npm run verify:agentenv:environment` is the hosted `real-agentenv` lane
described in [testing](testing.md#exported-environment-bundles). It projects
the Grafana-shaped source-owned capture through the built CLI, packs the
checkout, exports a bundle with `--package-tarball`, runs it with the pinned
`agentenv-framework` release on the runner's Docker and requires the smoke task
to pass with no container left behind other than the framework's own
image-registry helper, which the receipt reports. It proves that one exported bundle runs
on one framework release; it does not claim any hosted platform or other
framework versions.
## Resource and lifecycle boundaries
The Evidence files are read once at startup through the bounded JSON input
reader and authenticated as Evidence before the listener binds; a file that is
not Evidence, a manifest of another view kind, a capture of another format, or
a manifest projected from a different capture is refused before any port is
opened. The state is derived once, in-process and before binding, from the
already authenticated capture: it is projected as retained Evidence, so the
capture's strings are not charged again as inline view input, and the view's
96 MiB budget bounds the derivation. Because authentication covers the capture's records, a manifest whose
parent check passes cites only records the capture retains. The server keeps
only the cited records' request lines and response text in memory, plus the
live state when it serves from one: the derived initial state, or one
validated document whose JSON was at most 32 MiB. Data-plane calls run one at a time, so
at most one loaded document is being read and parsed at once; `data/get`
serializes the held state whole. No process is launched and nothing is
written to disk. No outbound network request is made unless a `data/add` call names an
`https://` uri, which is fetched once within the limits above; a `file://` uri
is read once. A listener that fails after binding is logged and ends the command with
a `resource_constraint` error and exit `1` instead of serving nothing.
+79 -15
View File
@@ -103,9 +103,10 @@ that every class or engine operation will load successfully. Discovery limits
each ZIP metadata read to 8 MiB before allocation; a larger directory reports
the exhausted read budget and the selected JAR path.
The current metadata bridge is verified on macOS arm64 with OpenJDK 21 and the
public Appium ApiDemos fixture. The POSIX adapter also supports Linux; the new
bridge has not yet undergone real verification on Linux/JDK 25.
The filtered class-page bridge (protocol 2) is verified on Linux x86-64 with
Temurin 21, public Appium ApiDemos 6.0.18 and NewPipe 0.29.1 APKs. Earlier
metadata-bridge records covered macOS arm64/OpenJDK 21 and Windows x64/JDK 21;
this protocol revision has not been rerun on those hosts or Linux/JDK 25.
Windows x64 uses an owned Job Object with bidirectional stdio, atomic job
assignment and kill-on-owner-close. Real Windows verification uses JDK 21 and
the pinned JADX 0.7.1 JAR; see the real verification lane below.
@@ -146,10 +147,20 @@ 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
selected overload. Native/abstract methods with no decompiled body report
`body_status: "not_available"` without inferring which implementation flag caused it.
Smali fallbacks select one complete block using the original method name and DEX
descriptor, before applying the source byte limit. This preserves the selected
overload even when Java code generation renames a method. If the selected Java
body was omitted, including a method merged into a synthetic bridge, REA returns
its original smali with the `java_body_unavailable` marker. The marker describes
missing Java; it does not infer why the method was omitted or name a replacement.
Unresolved or duplicate disassembly identities fail instead of attributing another
method's instructions to the selection.
Native and abstract methods are identified from access flags
captured before code generation. They report `body_status: "not_available"`, an
empty source, and the `native_method` or `abstract_method` marker; their declarations
are not passed through the engine's source slicer. Other missing decompiled bodies
do not establish either access flag.
Static initializers (`<clinit>`) return smali with the
`static_initializer_disassembly` marker. Java decompilation can move their
@@ -160,10 +171,19 @@ the same source byte limit and explicit truncation reporting.
## Interpret the result
- Package inspection retains decoded manifest XML, summary declarations,
permissions and class/resource counts. Summary fields are extracted by the
upstream engine and should be checked against the retained XML when needed.
permissions and class/resource counts. REA's bridge selects the exact root
`AndroidManifest.xml` resource by original archive name and manifest type,
independently of asset order. One namespace-aware JDK XML parse supplies
summary attributes and direct permission/SDK declarations; namespace prefixes
and XML entity spelling do not change their values. Raw decoded XML is retained
exactly and only its displayed text is subject to the existing source byte cap.
Summaries use the complete selected XML before that cap. Document types are
unsupported; parsing does not load external DTDs, schemas, or XInclude files.
Signature verification is explicitly `not_performed`.
- Class search consumes all pages and checks their counts. An empty `query`
- Class search filters the engine's cached top-level names before transferring
pages, then follows contiguous inventory positions without rescanning earlier
rows. It checks selection, scope, examined ranges and counts before reporting
complete coverage. Complete producer replies stay in Evidence; an empty `query`
inventories all class-name rows; class names are decompiler representations.
Repeated names remain repeated in `matches` so the inventory row count and
producer evidence stay visible. `duplicate_names` identifies matching names
@@ -233,11 +253,19 @@ rea project-android-application-graph '{"inventory_evidence":[<inventory_artifac
```
The projection reports exact component paths and hashes, runtime-family hints,
and path-based bridge hypotheses. It does not decode DEX or claim observed
runtime calls. Framework hints match complete native library names, such as
and path-based bridge hypotheses. File and slice occurrences supply component
endpoints; directory trees and symlinks retain their identities in the source
inventory without being classified as code files. It does not decode DEX or
claim observed runtime calls. DEX/JVM runtime hints summarize the classified
bytecode component lists. Framework hints match complete native library names,
such as
`libreactnativejni.so`, `libreactnative.so`, `libhermes.so`,
`libhermes_executor.so`, `libflutter.so`, and `libunity.so`; unrelated names
such as `libcommunity.so` and `libreactivity.so` do not identify those families.
The signing list retains the `META-INF/MANIFEST.MF`, `.SF`, `.RSA`, `.DSA`, and
`.EC` paths selected by Android's v1 verifier, including nested paths. These are
inventory metadata candidates, not proof that a v1 signature verifies or that
the APK is signed; use `inspect-apk-signature` for signature analysis.
The bridge basis is inferred from the native path and repeated for each managed
component, so it does not identify a specific JNI binding. Bridge hypotheses form a DEX/JVM-class by native-library
Cartesian product. The projection computes the product and exact serialized
@@ -299,9 +327,37 @@ settings. Evidence reports the worker's observed `Runtime.maxMemory()` rounded
down to MiB and retains exact bytes, Java version and visible processors in its
raw runtime response. The observed allocatable heap can differ from configured
`-Xmx`, especially with Serial GC. Heap is not a total RSS cap: the JVM also
uses native memory. Independent REA instances have independent queues. Whole
operations have a **120-second** execution deadline after reaching the queue's
front; upstream decompilation is limited to **90 seconds**.
uses native memory. A typed JVM memory allocation failure during APK loading
reports `resource_constraint` with the observed maximum heap in bytes. Known
heap failures name `REA_JADX_HEAP_MIB` in remediation; other memory regions
remain unknown. A healthy doctor configuration check does not establish that
any particular APK fits. Failures before the bridge can report a typed result
and upstream errors from other operations retain their existing diagnostics;
error text alone does not establish memory exhaustion.
Independent REA instances have independent queues. Whole
operations have a **120-second** execution deadline by default, starting after
they reach the queue's front. For a known slow cold load, such as a large
multidex APK, select another finite deadline:
```sh
export REA_JADX_OPERATION_TIMEOUT_MS=600000
```
The value must be a decimal millisecond count from 1 to 2147483647; anything
else is refused before Java starts. REA reads it from the environment captured
at process start. A change applies on the next process start; an already
running server keeps the deadline it started with. The same deadline bounds
each upstream call. A timeout names `REA_JADX_OPERATION_TIMEOUT_MS`, the
effective milliseconds, and that range. A passing JADX configuration check in
`rea doctor` reports the effective `operation_timeout_ms`. Explicit
cancellation still ends the operation immediately, and partial observations
and owned-process cleanup are unchanged. Raising the deadline does not extend
the bridge's 30-second startup handshake or the upstream **90-second**
decompilation limit. MCP clients enforce their own tool-call timeouts, and
setup's derived [client tool timeout](client-configuration.md) does not include
this setting, so also raise the client timeout when selecting a longer
deadline.
Decoded manifest and method text have a **1 MiB** upstream byte budget and report
truncation. Class-name pages use indexed slices of up to 4,096 names without changing the
@@ -320,6 +376,14 @@ export REA_ANDROID_TEST_APK="$PWD/_reference/apk-integration/ApiDemos-debug.apk"
npm run verify:android
```
The main lane and Windows Java-selection checks validate the selected JDK and
engine archive through `validateVerifierJadxConfiguration`. This shared verifier
boundary supplies the production validator's resource owner and waits for
retained cleanup before proceeding. Failed closes retain their exact owners for
retry; prerequisite failures must not be mistaken for completed bridge tests.
Recovery checks match the typed Java or archive failure and its applicable
remediation.
The lane also uses the selected full JDK's `jlink` to create a disposable JRE
without the compiler module. It checks real Java startup failures, missing
selectors, recovery after failures, and CLI cancellation with owned cleanup.
+268
View File
@@ -0,0 +1,268 @@
# APK signature schemes
REA reads the signing of a selected local Android APK (or ZIP/AAB) statically,
so an analyst can answer **who signed this, under which schemes, and whether the
declared content digests match these bytes** before trusting or triaging it. The reader
is in-process and needs no external engine: it parses the ZIP tail and the APK
Signing Block, reads certificates with the platform X.509 parser, and
recomputes the content digest. No Android code executes, and no device or
`adb` is involved.
CLI and MCP share one workflow and return artifact-bound Evidence inline.
```sh
rea inspect-apk-signature /path/app.apk
```
## Selected facts and retained Evidence
MCP defaults to a summary with scheme presence, signer certificate fingerprints,
row counts, posture and explicit coverage. `detail: "complete"` returns the full
report. A selected page can answer a narrower question in the acquisition call:
```json
{
"path": "/path/app.apk",
"view": {
"collection": "signers",
"schemes": ["v3"],
"fields": ["certificate_sha256"],
"limit": 32
}
}
```
The returned `parent_evidence_id` identifies the retained complete report.
Subsequent reads use `inspect_analysis_view`, including after the APK is deleted:
```json
{
"source": {
"kind": "retained-evidence",
"evidence_id": "<returned parent_evidence_id>"
},
"view": {
"kind": "apk-signature",
"collection": "digests",
"signer_ids": ["<returned signer id>"],
"fields": ["signature_algorithm_id", "algorithm", "matches_apk"],
"offset": 0,
"limit": 32
}
}
```
Collections are `signers`, `digests` and declared `lineage` nodes. `ids`,
`signer_ids` and `schemes` filters intersect; omitted filters select all available
records and `[]` selects none. Omitted `fields` returns every collection column;
`fields: []` retains identity and relationship links. IDs include the complete
APK digest, scheme and source ordinal; child records retain `signer_id` and
`signer_index`, and declared certificates retain their raw-DER SHA-256 identity.
Lineage pages expose `previous_id` when selected. Certificate fingerprints can
be composed across versions without treating a declaration as verified identity.
Page coverage describes examined declared records. Exhausting a page does not
establish certificate authenticity, platform acceptance, v1 entry coverage or a
verified rotation. Partial decoding and its reason remain visible even when a
lineage page contains no rows. A weak cache reuses validated immutable report
facts; each read checks its parent subject, and new file inspections read again.
CLI defaults to complete delivery. A compact one-shot response requires a complete
Evidence export:
```sh
rea inspect-apk-signature /path/app.apk --detail summary \
--view '{"collection":"signers","fields":["certificate_sha256"]}' \
--evidence_output /path/app-signing-evidence.json --json
```
The export contains the same complete parent used by MCP. Historical CLI pages
use `inspect-analysis-view` with an `evidence-file` source record. The CLI and MCP
share the application workflow, projection, retention and publication owners.
## What it reports
- **Schemes present:** v1 (JAR signing, from the central directory), and the
APK Signing Block's v2, v3 and v3.1. v1 follows apksig's selection: entries
under `META-INF/` at any depth ending in `.SF`, or in `.RSA`/`.DSA`/`.EC`
(case-sensitive). v1 is `present` only when a signature block has its
matching `.SF` file (`META-INF/x/CERT.RSA` pairs with `META-INF/x/CERT.SF`);
`signature_files` and `block_files` list every candidate either way.
- **Per v1 signer** (each block paired with its `.SF`): REA inflates the block
and reads its PKCS#7 `SignedData`. `certificate` is the certificate the
block's one `SignerInfo` names by `issuerAndSerialNumber` or
`subjectKeyIdentifier` (not by position), with the same facts and raw-DER
SHA-256 as a v2/v3 signer, so a v1-only APK can be fingerprinted and
clustered with v2/v3-signed ones. apksig resolves only
`issuerAndSerialNumber`, so its v1 verification fails for a block whose
`SignerInfo` uses `subjectKeyIdentifier`: REA still reports the declared
certificate (`identified_by: subject-key-identifier`), adds a limitation,
and treats that signer as unknown in `v1_signers_match_block_signers`. `certificates` lists every certificate the
block carries (SHA-256 and subject) and `signer_infos` gives each
`SignerInfo`'s certificate and digest/signature algorithm OIDs. The PKCS#7
`SignedData` envelope is read as BER, including constructed signature OCTET
STRINGs; each embedded certificate is still interpreted as DER by the X.509
owner.
`certificate` is `null` when the block has more than one `SignerInfo` (with
nothing verified there is no single signer) or names a certificate it does
not carry. A block that cannot be read (malformed ASN.1, an unsupported
compression method, Zip64 sizes, a local header that disagrees with the
central directory on the name, data-descriptor flag, CRC-32 or a size, as
apksig refuses it, or over a bound) keeps its names and carries
`decode_error`; it is never reported as an absent signer. apksig ignores
the ZIP encryption flag (bit 0), so a block that sets it is read as stored:
the PKCS#7 decoding decides (bytes that really are encrypted fail to
decode), and a limitation names the block.
apksig rejects the v1 signature of an archive whose central directory
holds any name more than once ("Duplicate entry"). REA checks every entry:
a limitation names the first few duplicates (JSON-quoted; a name over 256
bytes is cut there and its stored length given) and counts the rest, and a
signer whose block, `.SF` or `META-INF/MANIFEST.MF` name is duplicated
(one signer per block name) carries `decode_error`
`duplicate entry: <name>`, so a duplicated manifest marks every signer.
A duplicate outside those names leaves the signers as declared.
Names are compared as apksig compares them, as Java strings decoded from
UTF-8: malformed bytes become U+FFFD, so `dup\xff` and `dup\xfe` are one
name (apksigner: `Duplicate entry: dup�`). REA reproduces Java's
replacement rather than WHATWG `TextDecoder`'s; the two differ for encoded
surrogates (`ED A0..BF`), which Java replaces as one unit. This was checked
against OpenJDK 25's `new String(bytes, UTF_8)` on about 300,000 malformed
and well-formed sequences, including a leading byte-order mark, which Java
keeps; other Java runtimes are not checked. The same
decoding names v1 entries and compares a local header's name with the
central directory's; as in apksig, a local name longer in bytes than the
directory's is refused before decoding. apksig takes the record count from the classic
end-of-central-directory record's 16-bit field and ignores a Zip64 count,
so it reads only the first 65,535 records of a larger directory: the
limitation then says so, counts as duplicates for apksig only names whose
second copy lies within those records, and names separately those
duplicated only past them. REA still reads v1 signing entries, and flags
duplicated signing names, from every record, so past record 65,535 it
reports signers apksig would not see.
Bytes after the block's `ContentInfo` are ignored, as apksig's BER parser
ignores them, and a limitation names the block and their count.
- **Per signer:** the certificate's **SHA-256 fingerprint over the raw embedded
DER** (the same value `apksigner --print-certs` prints), plus subject, issuer,
serial, ISO 8601 validity and public-key algorithm when the X.509 parser can
read them (each is `null` otherwise); raw signature algorithm IDs, declared
digest hex and digest algorithms, signature lengths and public-key length; and for
v3/v3.1 the signer's declared min/max SDK.
- **Key rotation (v3/v3.1):** the declared proof-of-rotation lineage, oldest
first. Each node carries its certificate and the capability flags **retained
by that node's own certificate** after rotation (installed data, shared UID,
permission, rollback, auth). A lineage attribute that cannot be decoded is
reported with `decode_error`, never silently emptied.
- **Source stamp:** whether a source-stamp block is present and the stamp
certificate it declares (the V2 stamp block is preferred over V1, as apksig
does). `certificate` is `null` when no stamp is present, or when the block
could not be decoded and `decode_error` says why. Stamp signatures are not
verified.
- **Declared digest consistency:** for SHA-256/SHA-512 digests, REA recomputes
the v2/v3 content digest and reports `matches_apk` per digest and
`posture.self_consistent`. This only shows that the **declared** digest
equals the digest of the current bytes. It is **not a signature
verification**: anyone who changes the bytes can recompute the digest and
write it into the signed data, and REA still reports `true` while
`apksigner verify` rejects the APK. A changed byte with an unchanged
declared digest reads `false`.
- **Posture** (derived from the facts above):
- `v1_only`: v1 is present and no v2/v3/v3.1 block is. The v1
`X-Android-APK-Signed` marker is not read, so this cannot distinguish an
APK signed only with v1 from one whose v2/v3 block was removed.
- `schemes_agree_on_signer` (observed): every present block scheme presents
the same _set_ of signer certificates. An extra signer in one scheme is
disagreement; a signer without a certificate makes it `null` (unknown).
Build-tools 33+ rotate through v3.1 by default, so a rotated app usually
reads `false` here and `true` below.
- `signer_change_declared_by_rotation` (declared): the signers differ, but
each appears in a lineage the APK declares. Read the two together: a
rotated APK shows `false` / `true`; signers that differ with no declared
lineage containing them show `false` / `false`. It is `null` when a
signer's identity is unknown, or when the signers differ and a declared
lineage could not be decoded.
- `v1_signers_match_block_signers` (observed): the v1 signer certificates
equal what the lowest block scheme that has signers (v2, else v3, else
v3.1) expects v1 to carry. That is its signer set, except that a v3/v3.1
signer declaring a rotation lineage expects the lineage's first
(original) certificate, as apksig requires. `false` is notable: the JAR
signature names a different key than the block signature. A key rotation
keeps v1 (and v2) on the original key, so it reads `true` while
`schemes_agree_on_signer` reads `false`, with or without v2. `null` when
v1 or a block scheme is absent, any signer's identity is unknown (a
`decode_error`, a `null` certificate or a `subjectKeyIdentifier`
`SignerInfo`), or the lineage's original certificate cannot be
identified; a limitation then says why.
- `strongest_scheme`, `rotation_present`, `source_stamp_present`,
`self_consistent`.
## What it does not claim
This is **declared identity and digest consistency, not trust**. No signature
is cryptographically verified (including the v1 PKCS#7 signature and the
`.SF`/`MANIFEST.MF` digests, so a v1 signer is reported even where `keytool`
or `apksigner` reject the archive), certificate chains and revocation are not
evaluated, and nothing here says an app is authentic. `self_consistent: true`
means the declared digest equals the digest of the current bytes; it is not a
signature verification, and it does not prove who made the bytes. Rotation-lineage node signatures are not verified either, so a lineage is
what the APK _declares_: an attacker who re-signs an APK can declare any
lineage, which is why `signer_change_declared_by_rotation` is kept apart from
the observed agreement. SHA-256 identifies exact bytes, not authenticity.
Not yet read: v4 `.apk.idsig` (a sibling file), verity (`0x0421`/`0x0423`/`0x0425`) digests (reported with
`matches_apk: null`), and the v1 `X-Android-APK-Signed` marker.
## Bounds and errors
The archive is read once, bounded at 512 MiB, without following symlinks and
with change detection. Every size and offset in the ZIP tail and signing block
is checked against its containing section before use, including the complete
central-directory extent before ZIP end records. A process-wide queue permits
one active whole-file inspection and eight pending scans across provider
instances; pending scans retain no APK bytes and can be cancelled with their
original reason. Further requests receive a capacity error. The active slot
is held through parsing and cleanup. Signers per scheme, v1 signers and
lineage nodes are capped at 32 each as resource admission limits, not format
maxima (apksig separately accepts at most 10 v1 signers). Each v1 block is
inflated to at most 1 MiB, and its BER is read within 32 levels of nesting and
65,536 elements, with at most 32 certificates and 32 `SignerInfo`s; BER
indefinite lengths and constructed OCTET STRING fields are accepted. Embedded
X.509 certificates retain their DER requirement. The report's size is
bounded from above by those counts before anything is materialised, and the exact
serialized size of the report together with its limitations (which repeat
archive names) is then checked against an 8 MiB output budget (the exact check
is authoritative); an over-budget report is refused, not truncated.
The whole-file reader retains read chunks and a concatenated buffer; total
process memory exceeds the selected file's size. It also computes the complete
file SHA-256, so inspection is not header-only. Content digests are recomputed
at most once per SHA-256/SHA-512 algorithm during one inspection. Input,
inflation, decoded-record and serialized-output bounds keep work finite;
resource refusals are distinct from malformed format errors.
A file that is not a ZIP is refused as an unsupported target. A damaged ZIP
(local headers but no end-of-central-directory), a central directory that
cannot be walked or whose declared record count does not account for all of
its bytes, or a malformed signing block or signer is an input error naming the
scheme and reason. Signer framing includes signature records, the public-key
field and matching signed/outer v3 SDK ranges. Suffix bytes beyond known
fields are counted as uninterpreted, as Android readers may ignore extensions;
their presence does not establish malformed signing.
When the central directory is not immediately followed by
the end-of-central-directory record (a declared-size mismatch or a Zip64
record), the content digest is not recomputed and a limitation says so. A
rotation attribute that cannot be decoded still counts as `rotation_present`
(the attribute exists) and carries its `decode_error`.
Verified by the `verify:apk-signature` real lane. It builds a real minimal APK
with `aapt2`, signs it with the SDK's `apksigner` (default v2+v3, v1-only with
RSA and EC keys, v1+v2+v3 on a legacy target, a rotated key lineage with and
without v1, rotation carried by v3.1, a source stamp, and a copy
with one changed byte), and cross-checks REA against
`apksigner verify --print-certs`, `apksigner lineage --print-certs` and
`keytool -printcert -jarfile` through the CLI and MCP.
The real-toolchain CI lane uses pinned, checksum-verified Build Tools 36.1.0
and platform 36 oracle files in its job workspace. It requires the existing
host JDK and runs `npm run verify:apk-signature -- --require-tools`; absent
oracles fail rather than silently establishing provider coverage. REA itself
requires none of these external tools.
+98 -14
View File
@@ -8,7 +8,7 @@ installs Apktool or a Java runtime.
## Supply tools
Use Apktool 2.7.0 or newer (verified with 2.7.0) and a Java runtime the
Use Apktool 2.7.0 or newer (verified with 2.7.0 and 3.0.3 on Linux) and a Java runtime the
launcher can find. Select the launcher through the environment:
```sh
@@ -24,6 +24,30 @@ missing launcher reports `capability_unavailable` with the
`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.
Client inspection and decode results disclose the following reported-version
advisory windows without refusing otherwise compatible launchers:
| Advisory | Reported version window | Host scope |
| ----------------------------------------------------------------------------------------------------- | ------------------------------------------------ | ---------- |
| [CVE-2024-21633](https://github.com/iBotPeaches/Apktool/security/advisories/GHSA-2hqv-2xv4-5h5w) | Before 2.9.2 | All |
| [GHSA-vgwr-4w3p-xmjv](https://github.com/iBotPeaches/Apktool/security/advisories/GHSA-vgwr-4w3p-xmjv) | Before 2.9.3 | Windows |
| [CVE-2026-39973](https://github.com/iBotPeaches/Apktool/security/advisories/GHSA-m8mh-x359-vm8m) | 3.0.0 release line through versions before 3.0.2 | All |
The 2026 advisory names 3.0.1 and its 3.0.2 fix. Inclusion of 3.0.0 is
source-derived: [tagged 3.0.0](https://github.com/iBotPeaches/Apktool/blob/f770b39e425af4fa6ac4ca1e9ea6c4824339c6d3/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/table/ResTypeSpec.java)
passes type names through unchanged; the [3.0.2 patch](https://github.com/iBotPeaches/Apktool/commit/65dd8480dfcb63068562ffaa527f71bb0a9f772c)
validates those names at their owner before they reach the output-path producer.
Prereleases before the fixed release are conservatively disclosed; the exact
`-dirty` downstream marker is compared as a release while preserving its spelling.
This is version guidance, not verification of a particular prerelease or decoder.
A launcher version does not prove the decoder's contents, execution platform,
downstream backports, or overall safety. Windows applicability uses the selected
local host; an opaque wrapper may run another engine. A missing warning means
only that none of these version/host windows matched. Verify the selected
decoder's patches before decoding untrusted APKs; the compatibility floor above
is not a security recommendation. REA does not fetch advisories at runtime.
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,
no delayed expansion, and quotes every argument. A launcher path that
@@ -38,30 +62,75 @@ reports `capability_unavailable`.
rea inspect-apktool-client
rea decode-android-resources /targets/Example.apk
rea decode-android-resources /targets/Example.apk --locale de
rea decode-android-resources /targets/Example.apk --locales de --locales pt-BR
rea decode-android-resources /targets/Example.apk --locales de --locales pt-BR --string-tables-only
rea decode-android-resources /targets/Example.apk --no-strings
rea decode-android-resources /targets/Example.apk --decode-timeout-ms 900000
```
Equivalent MCP methods are `inspect_apktool_client` and
`decode_android_resources`. The decode request takes `path`, an optional
`locale` (project `res/values-<locale>/strings.xml` instead of the default
table), and `include_strings` (default true). Regional locales such as `pt-BR`
select Android's `values-pt-rBR`; Android qualifiers such as `pt-rBR` and
`b+zh+Hans+CN` may also be supplied directly.
primary `locale` (project `res/values-<locale>/strings.xml` instead of the default
table), `locales` (repeatable array of additional localized string tables to project
from the same single decode pass), and `include_strings` (default true). Regional locales such as `pt-BR`
select Android's `values-pt-rBR`. A three-digit region such as `es-419`
selects `values-b+es+419`; `es-r419` is rejected because Android's legacy
`-r` form requires a two-letter region. Android qualifiers such as `pt-rBR`
and `b+zh+Hans+CN` may also be supplied directly.
Set `string_tables_only: true` (CLI `--string-tables-only`) to return string
entries only inside `string_tables`, including when selecting a single table.
The top-level `strings` array is then empty by request; it does not establish
that the primary table is empty. Table status, counts and coverage still describe
the examined source. This avoids repeating primary entries in multi-locale
responses and charges the aggregate output budget for one copy per table.
Omitting the option preserves existing responses. `include_strings: false`
still suppresses all string projection, including in this mode.
## Interpret the result
- **An owned APK snapshot is digested and decoded.** `target.sha256` and
`target.bytes` bind the observation to the exact APK that was decoded.
The Evidence subject carries the same snapshot digest and caller-selected
path, including when the original file changes during decoding.
- **Metadata comes from apktool.yml as apktool printed it.** Version codes
keep their own spelling (`'7'` versus `7`); `package_name` is read from
the decoded manifest, not inferred.
Apktool 3's emitted `version: 3.x.y` header selects its single-line string
dialect: literal hashes, single quotes, empty strings, and the text `null`
stay part of the value; producer backslashes and Unicode escapes decode
once. Older/tagged documents retain YAML scalar semantics; double-quoted
values also retain YAML string semantics.
This distinction follows the released
[3.0.3 metadata writer](https://github.com/iBotPeaches/Apktool/blob/18b5e99cb56ff9451e8aa55b065dcf5bbd616975/brut.apktool/apktool-lib/src/main/java/brut/androlib/meta/VersionInfo.java#L49-L55)
and [string escape owner](https://github.com/iBotPeaches/Apktool/blob/18b5e99cb56ff9451e8aa55b065dcf5bbd616975/brut.j.yaml/src/main/java/brut/yaml/YamlStringEscapeUtils.java#L55-L116).
- **The manifest is the decoded text**, bounded at 512 KiB and reported as
truncated when larger.
- **Strings are projected verbatim.** Escaped entities stay escaped, and
entries containing nested markup (`<b>`, `<xliff:g>`) are counted as
unparsed rather than flattened — the limitation names the count. The
default table is bounded to 2000 entries with partial coverage when more
exist; `locales` lists every `values-<locale>` that has a strings table.
unparsed rather than flattened — the limitation names the examined count.
Malformed XML and a wrong document root are whole-document projection
failures: the result is partial, the failure reason is disclosed, and entry
counts remain unknown. They are not counted as one unsupported string.
The default table is bounded to 2000 entries with partial coverage when more
exist. `locales` lists observed regular `strings.xml` tables whose exact
`values-<locale>` directory is selectable by the request's locale grammar.
Locale suffixes allow up to 23 ASCII characters, derived from Apktool's
language, script, region, and variant fields; for example,
`b+sr+Latn+RS+REVISED` selects that exact table.
Other table directories, including combined qualifiers such as
`values-en-rUS-v21` and `values-mcc310-fr`, are named in limitations with
their source spelling. They are not advertised as selectable locales;
REA does not infer Android's runtime resource matching. Non-regular
strings-table paths are disclosed with partial coverage. Discovery uses
the same bounded traversal as the file count; filesystem failures remain
failures rather than being interpreted as missing tables.
When multiple tables are requested with `locales`, `string_tables` records
each projected table with its locale, status (`present`, `missing`,
`oversized`, `malformed`, or `skipped`), entry counts, and coverage,
bounded at an aggregate budget of 10,000 strings and 32 MiB XML. When `locales`
is omitted and `string_tables_only` is not true, legacy single-table payloads are preserved
without adding `string_tables`.
- **Smali is skipped by design** (`apktool d --no-src`): Java-source
recovery belongs to the JADX family. Resource smali edits and rebuild are
a follow-up lane with mutation authority.
@@ -73,17 +142,24 @@ select Android's `values-pt-rBR`; Android qualifiers such as `pt-rBR` and
## Resource and lifecycle limits
- The decode runs with a 600 s deadline and a 4 MiB output capture budget;
a stopped decode reports `provider_timeout` and exceeded budgets report
`resource_constraint`.
- The decode wall clock is `decode_timeout_ms` (CLI `--decode-timeout-ms`).
Omitted means 600000 ms. The integer range is 1 to 2147483647. A change
applies on the next decode. It does not raise the 4194304-byte (4 MiB)
output capture budget. That capture budget has no runtime override. A
stopped decode reports `provider_timeout` and names the effective
milliseconds; an exceeded capture budget reports `resource_constraint`.
- The version probe waits 20000 ms and retains 65536 bytes. This release has
no runtime override for that probe. `decode_timeout_ms` does not raise it.
- Manifest reads are bounded to 512 KiB before decoding UTF-8. Strings XML
reads are bounded to 8 MiB; larger documents are skipped with partial
coverage and an explicit limit, rather than reported as missing.
- `decoded_file_count` is capped at 100,000 files; the cap is reported in
the limitations when hit.
- Apktool's own decode failures (`AndrolibException`, directory traversal
refusals) surface as `unsupported_target` with apktool's reason preserved
verbatim.
- An ordinary nonzero decode exit surfaces as `unsupported_target` naming
the caller-selected APK, with Apktool's diagnostic and captured output.
Launcher startup, ownership preparation, signal termination and incomplete
output collection remain provider failures; they do not establish that the
selected APK is unsupported.
## Real verification
@@ -94,3 +170,11 @@ locales — launcher version, on-disk digest agreement, apktool.yml metadata
(1.2.3, SDK 24–34), manifest package agreement, default and `de` string
tables projected. Parser goldens in the test suite come from the same
decode. See `docs/testing.md` for the lane summary.
Multi-locale and table-only projection were also verified through real CLI and
stdio MCP workflows on Linux with Apktool 3.0.3/JDK 21 against NewPipe 0.29.1,
ApiDemos 6.0.18 and Appium Settings 8.2.1. Independent XML projection checks,
exact facts/Evidence parity and bounded task costs are recorded in the
[shared-decode and table-only checkpoint](https://github.com/morluto/rea/blob/42e1a17d6e6c0487f17be0bae248553c4caba7f7/experiments/analysis-efficiency/apktool-string-tables.md).
This qualifies selected resource facts, not application execution or independent
binary resource-decoder completeness.
+82 -19
View File
@@ -60,6 +60,15 @@ states that absence is unknown. Component arrays remain complete when candidate
hypotheses are omitted; component arrays still include every component from
the supplied inventory pages, and overall coverage is `partial` in that case.
Inventoried files whose bytes were not read, such as encrypted archive members,
are listed in `unavailable_components` with their original path, format,
encryption flag, limitations, and null content identity. The same application-root
and AppleDouble attribution rules apply. These files make overall coverage
`partial`; component absence is unknown even when `inventory_complete` is true.
Directories have no file bytes and symlinks remain in `symlinks`, so neither is
an unavailable file component. The additive field participates in `projection_id`,
including when empty, so Apple projection identifiers change.
## Application roots
| Inventory root | Application root |
@@ -68,6 +77,15 @@ the supplied inventory pages, and overall coverage is `partial` in that case.
| `.app` directory | `.`, the inventoried bundle itself |
| Directory, ZIP, or DMG containing apps | Each outermost `<Name>.app` followed by `Contents/` |
File and slice occurrences supply component endpoints. Directory tree identities
remain in source inventory and bundle anatomy, and links remain in `symlinks`.
Root and layout discovery use known directories and ancestors implied by deeper
inventory paths. A leaf file or symlink named `*.app`, `Contents`, or `Versions`
does not establish that directory. Directory and authenticated-file identity are
selected before applying anatomy case preferences, so a file cannot shadow a
folded directory name or a directory shadow a metadata/signature file.
ZIP archives made with `ditto -c -k --keepParent` report `<Name>.app`. A DMG
reports `<image>.dmg/<Volume>/<Name>.app`; DMG child inventory needs the native
macOS mounting adapter. When a DMG has only its root identity, the projection is
@@ -162,8 +180,21 @@ rea trace-dylib-resolution MyApp.app --root Contents/MacOS/MyApp --architecture
Load commands are parsed in TypeScript from Mach-O and FAT headers, including
the `dylib_use_command` encoding newer linkers emit for flags such as delayed
initialization. Nothing is executed and no Apple tool is required, so the tool
also works off macOS.
initialization, and `LC_LAZY_LOAD_DYLIB_INFO` (`0x3a`). That command stores its
install name in a linkedit payload rather than in the load command itself.
Its flag and chain-start image offsets must lie in the slice VM range, which
is dyld's one-page floor extended by every segment except `__LINKEDIT`,
measured from the `__TEXT` vmaddr. Payload strings must start after the 24-byte
header. The load-path NUL must occur before the payload's final byte, matching
dyld's scan. Distinct payload bytes and payload-range lookups stay within the 16 MiB
limit, and a file range already read is reused. String scanning and UTF-8
checks count every reference, including repeats and overlaps, and stop at that
same aggregate. A slice retains at most 1024 of these commands; further copies are
unsupported instead of becoming one traced edge each. Symbol references,
including repeats, stop at 65536 per slice. A larger table is unsupported
instead of caching one string per offset.
Nothing is executed and no Apple tool is required, so the tool also works off
macOS.
Resolution follows dyld:
@@ -182,30 +213,62 @@ Resolution follows dyld:
Each edge lists every candidate path tried, with its outcome: `resolved`,
`absent`, `not-mach-o`, `malformed`, `unsupported` (recognized Mach-O outside
parser coverage, such as big-endian images), `architecture-missing`,
`outside-target`, `escapes-target`, or `undetermined`. Slices follow dyld's
graded architectures, so an `x86_64h` process also loads `x86_64`. The edge's `resolution` is one of:
`not-loadable`, `platform-mismatch`, `outside-target`, `escapes-target`, or
`undetermined`. `not-loadable` means a
matching-architecture Mach-O image was found but it is not a dylib and cannot
satisfy a dylib dependency. `platform-mismatch` means a matching-architecture
dylib was found but its observed platform is incompatible with the process.
These outcomes reject that candidate; dyld search may continue and resolve a
later candidate. Candidate outcomes describe individual search paths, while
`resolution.status` describes the final edge. For each selected process-root
slice, candidate slices are matched by CPU type and dyld's subtype preference
order, independently of FAT table order. An ARMv7s root slice prefers ARMv7s,
then ARMv7; other known ARM32 subtypes use their compatibility rows from
[Apple's dyld source](https://github.com/apple-oss-distributions/dyld/blob/dyld-852.2/src/dyld2.cpp#L2811-L3105).
An `x86_64h` process also loads generic `x86_64`. Unknown ARM32 subtype policy
remains undetermined when ARM32 candidate slices exist. This is a static model
keyed to the selected root slice; it does not observe the target device's host
CPU or prove which slice the device will execute. The edge's `resolution` is
one of:
| Status | Meaning |
| -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `resolved` | An image inside the analyzed root loads, and no earlier candidate was undeterminable. |
| `conditional` | An image inside the root resolves, but an earlier candidate outside the root (for example `/usr/lib/swift`) would win if it exists on the running system. |
| `unresolved` | Every candidate is definitively absent or unusable inside the root. |
| `undetermined` | No candidate resolves inside the root, and some candidate lies outside it or depends on the environment. |
| Status | Meaning |
| -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `resolved` | An image inside the analyzed root loads, and no earlier candidate was undeterminable. |
| `conditional` | An in-root image may load, but an earlier outside candidate, unmodeled environment input, or unknown platform compatibility can change which image dyld loads. |
| `unresolved` | Every candidate is definitively absent or unusable inside the root. |
| `undetermined` | No candidate resolves inside the root, and some candidate lies outside it or depends on the environment. |
A `conditional` image is still traced, because it is the image the analyzed
root supplies. Every edge below it has `loader_conditional: true`, and a
request that reuses it by install name is `conditional` too. Findings on those
edges say that they apply only if the conditional image loads.
A `conditional` image is still traced, because it is an in-root candidate
that may load. Every edge below it has `loader_conditional: true`, and a
request that reuses it by install name is `conditional` too. An in-root dylib
with no platform load command remains unknown unless the root is an executable
with observed macOS platform and the candidate’s CPU type is x86_64, i386, or arm64. In that macOS context,
[Apple's dyld](https://github.com/apple-oss-distributions/dyld/blob/fd8d0c4d52320ebf64db34f3cb280310d905c5ae/mach_o/UnsafeHeader.cpp#L1137-L1145) supplies an implicit macOS platform to legacy dylibs on those CPU
types. The candidate records `platform_basis: "macos-dyld-implicit"`; the
reported slice fields `platform: null` and `platforms: []` remain the parsed
facts. For other process contexts the platform remains unknown, coverage is
partial, and the candidate is not treated as incompatible. If a later known
candidate resolves, it remains the traced image with conditional resolution;
the trace does not claim the unknown candidate's dependency graph. Findings on
conditional edges say that they apply only if the conditional image loads.
Absolute install names such as `/usr/lib/libSystem.B.dylib` are
`outside-target`. On macOS 11 and later most of them live in the dyld shared
cache rather than on disk, so REA does not check them against the host.
Absolute install names that name a path beneath the analyzed root are checked
within that root, with symlinks and `..` resolved by the same bounded tree
resolver. Absolute paths outside the root, such as
`/usr/lib/libSystem.B.dylib`, are `outside-target`; on macOS 11 and later most
system libraries live in the dyld shared cache rather than on disk, so REA does
not check them against the host. Mach-O path strings are not converted from
`file:` URLs or looked up through unrelated host paths.
`findings` are derived, not observed:
- `required-load-unresolved`, `weak-load-unresolved`, and `lazy-load-unresolved`.
`LC_LAZY_LOAD_DYLIB` dependencies are resolved but not traversed, because dyld
loads them on first use; an unresolved one is not a launch failure.
`LC_LAZY_LOAD_DYLIB` and `LC_LAZY_LOAD_DYLIB_INFO` dependencies are resolved
but not traversed, because dyld loads them on first use; an unresolved one is
not a launch failure. `LC_LAZY_LOAD_DYLIB_INFO` has no compatibility version,
so those fields are null. Its may-be-missing flag is reported as `weak`, and
an unresolved dependency with that flag is not a first-use failure: dyld
continues if the image is absent.
- `earlier-rpath-candidate-absent`: a compatible Mach-O placed at an earlier
modeled search path could take precedence, subject to unmodeled search inputs
and code-signing library validation. These findings do not establish which
+13 -4
View File
@@ -1,8 +1,8 @@
graph TD
subgraph "Entry Points"
CLI["src/cli.ts + src/cli/<br/>One-shot CLI adapter<br/>setup, diagnostics, analysis, decompilation"]
MAIN["src/main.ts + src/main/<br/>MCP stdio server adapter<br/>config, session, process lifetime"]
DISPATCH["scripts/rea.mjs<br/>Executable dispatcher<br/>routes mcp / one-shot"]
MAIN["src/main.ts + src/main/<br/>MCP server adapter: stdio default, streamable HTTP opt-in<br/>config, session, process lifetime"]
DISPATCH["scripts/rea.mjs<br/>Executable dispatcher<br/>routes mcp / mcp --http / one-shot"]
end
subgraph "Production Composition"
@@ -10,7 +10,7 @@ graph TD
end
subgraph "Adapters"
SERVER["src/server/<br/>MCP request translation<br/>createServer, tool registration, toolResult"]
SERVER["src/server/<br/>MCP request translation<br/>createServer, tool registration, toolResult<br/>http/: streamable HTTP listener and node:http bridge"]
HOPPER_BRIDGE["bridge/hopper_bridge.py<br/>Runs inside Hopper<br/>adapts operations to Hopper Python API"]
GHIDRA_BRIDGE["bridge/ghidra/ReaGhidraBridge.java<br/>Packaged HeadlessScript<br/>inventory, decompiler, references, CFG"]
INSTALL["src/cli/ + src/application/Setup*<br/>Host installation adapters<br/>macOS: verified vendor DMG to ~/Applications<br/>Linux: verified vendor package + apt/dnf/pacman"]
@@ -26,14 +26,16 @@ graph TD
end
subgraph "Provider Layers"
PROVIDER_OWNERS["src/providers/<br/>shared deferred client and inspection owners<br/>exact cleanup retry and attributed failures"]
PWNTOOLS["src/native/pwntools/ + src/native/pwndbg/<br/>BYO offline ELF/core adapters<br/>owned snapshots/Python and optional core-only GDB<br/>unchanged upstream parsers, ABI structures and mapping API"]
WASM["src/wasm/<br/>caller-supplied WABT; immutable artifact inspection<br/>owned process supervision and retryable workspace cleanup"]
APKSIG["src/apkSignature/<br/>in-process APK Signing Block + v1 reader<br/>bounded single read, no device or execution"]
EVM["src/evm/<br/>unchanged EVMole/WASM in bounded owned worker<br/>explicit carrier parsing and inferred interface adapter"]
HOPPER["src/hopper/<br/>Hopper launch & Unix-socket protocol<br/>BridgeLauncher, HopperClient, protocol"]
GHIDRA["src/ghidra/<br/>Ghidra discovery, profile & function analysis<br/>snapshot, Unix/Windows transport, bounded lifecycle"]
IDA["src/ida/<br/>Upstream MCP contract adaptation<br/>live GUI binding or owned headless database"]
IDA_MCP["External ida-pro-mcp<br/>GUI proxy or database supervisor<br/>upstream installation and engine lifecycle"]
AUX["src/artifacts/ + src/native/<br/>artifact inventory and extraction<br/>format readers and native capabilities"]
AUX["src/artifacts/ + src/native/<br/>artifact inventory, bounded directory triage and extraction<br/>format readers and native capabilities"]
BROWSER["src/browser/<br/>CDP / Playwright capture<br/>captured scripts and native module resolution<br/>execution/: coverage, listener and source attribution"]
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"]
@@ -100,6 +102,8 @@ graph TD
SERVER -->|narrow record ports| RECORDS
RECORDS --> DOMAIN
COMPOSITION --> WASM
COMPOSITION --> APKSIG
APKSIG --> DOMAIN
WASM --> PROCESS
WASM --> DOMAIN
COMPOSITION --> PWNTOOLS
@@ -138,6 +142,11 @@ graph TD
GHIDRA --> GHIDRA_BRIDGE
HOPPER --> PROCESS
GHIDRA --> PROCESS
GHIDRA --> PROVIDER_OWNERS
APP --> PROVIDER_OWNERS
PROVIDER_OWNERS --> PROCESS
PROVIDER_OWNERS --> AUX
PROVIDER_OWNERS --> DOMAIN
GHIDRA --> WINDOWS
PROCESS --> WINDOWS
WINDOWS --> WINDOWS_NATIVE
+177
View File
@@ -147,6 +147,29 @@ 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`. Retained stdout and stderr share an 8 MiB
budget, but every drained byte is scanned for `java.lang.OutOfMemoryError`:
`failure_output.observed_oome` records the latest confirmed token's bounded line per stream from
the failing operation, with its byte offset, even after the budget is spent. These observations own OOME detection; retained text that ends at a class-name prefix cannot confirm a complete token. `line_complete` distinguishes a known line end from an incomplete trailing context; `invalid_utf8` marks replacement-decoded context.
Incomplete operation output without an observed OOME keeps the original failure code and explains that a memory cause cannot be ruled out. Incompleteness alone does not confirm a memory constraint or justify heap-specific advice. Token context stays within the failing operation's checkpoint; a class-name prefix in a different identifier is not OOME evidence. A trailing bare token requires an observed stream EOF to establish its boundary.
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
@@ -192,3 +215,157 @@ Large Evidence envelopes may require a caller-configured MCP transport receive
buffer because envelope copies add overhead. Requests honor cancellation during
snapshot reading, directory traversal and payload hashing. The operation reads
regular files only and rejects symbolic links.
## Byte entropy triage
`inspect_byte_entropy` / `inspect-byte-entropy` profiles any local regular file
without an analysis engine, execution, extraction or network access.
```sh
rea inspect-byte-entropy ./firmware.bin --block-size 4096 --json
rea inspect-byte-entropy ./firmware.bin --offset 4096 --length 65536 --json
```
```json
{
"name": "inspect_byte_entropy",
"arguments": { "path": "/artifacts/firmware.bin", "detail": "summary" }
}
```
Complete Evidence includes `artifact` (original path, byte size and SHA-256),
`block_size`, `window`, `file_entropy`, `thresholds`, `blocks`, `runs` and
`limitations`. For a 4096-byte zero-filled file, the entropy fields are:
```json
{
"block_size": 4096,
"window": { "offset": 0, "length": 4096 },
"file_entropy": 0,
"thresholds": { "high": 7.2, "low": 1 },
"blocks": [{ "offset": 0, "length": 4096, "entropy": 0 }],
"runs": [{ "offset": 0, "length": 4096, "classification": "low" }]
}
```
Offsets and lengths are decimal byte counts. Blocks begin at the requested
`offset` (default zero); omitted `length` extends to EOF. Explicit windows past
EOF fail rather than silently clipping. The final block may be shorter.
`block_size` must be a power of two from 256 through 65536, default 4096. A
window may produce at most 32,768 block rows; larger requests fail before the
file scan with a capacity error. Choose a shorter window or larger block size.
SHA-256 and `file_entropy` always cover the entire file, including when a window
is selected. Empty files and empty windows are accepted; empty entropy is zero
by convention. The tool reads the size the file reports. A file that reports size
0, such as a Linux `/proc` entry, returns no blocks with a limitation that the
result does not show the file is empty. File identity, size and timestamps are checked across the read;
detected changes produce an error rather than Evidence.
Shannon entropy is reported in bits per byte from 0 to 8, rounded to four decimal
places. Adjacent blocks with reported entropy at least 7.2 form `high` runs;
blocks at most 1.0 form `low` runs. Other blocks interrupt runs. Entropy does not
prove encryption, and this measure cannot distinguish encrypted from compressed
data. Small blocks can underestimate entropy; the result is a triage clue.
MCP `detail: "summary"` retains complete Evidence in the session and returns
file entropy, block/run counts, thresholds, the selected window, and the ten
longest runs (ties ordered by file offset). Use its `parent_evidence_id` to
retrieve complete Evidence, or request its summary with `inspect_analysis_view`.
The CLI returns complete Evidence through the same service. Summary detail
requires a session capable of retaining Evidence.
Reads use one block-sized buffer and a 256-entry histogram. Complete output
can become very large because it includes one row per selected block, and
summary delivery still retains that full list. Use a shorter length or a
larger block size to reduce it. Full Evidence and its serialization require
memory proportional to the number of selected blocks. The 32,768-row ceiling bounds the actual retained block objects and
their serialized representation: a cap-sized run below produced 1,514,877
serialized bytes and increased V8 heap use by 21.2 MiB on the measured host.
The cap therefore bounds this output independently of file size; it is not a
portable process-memory guarantee. Narrow the window or increase block size to
reduce retained rows further.
Local service measurement (macOS arm64, Node 24.18.0, 100,000,000-byte file,
4096-byte blocks, file already in the OS cache): first process run took 1.113 s
wall / 1.148 s CPU with 145.3 MiB peak RSS; a second run in the same process took
1.015 s / 1.035 s CPU, with cumulative peak RSS 155.8 MiB. Each read 100,000,000
bytes and produced 24,415 blocks. Measurements include Evidence construction,
summary projection and JSON serialization, with no child processes. Complete
Evidence serialized to 1,122,303 bytes and its summary to 2,705 bytes. A
cap-sized 134,217,728-byte sparse file with 4096-byte blocks produced 32,768
rows and 1,514,877 serialized bytes; V8 heap use increased by 21.2 MiB and RSS
by 18.1 MiB in that process. This separate measurement used the same macOS
arm64 host and Node version. Cold disk I/O, token counts and an agent
investigation were not measured; there was no previous equivalent tool to
benchmark. These figures do not establish a portable peak-memory bound,
constant memory for retained Evidence or faster agent task completion.
## Portable native exports
`inspect_native_exports` / `inspect-native-exports` reads the export table of an
explicit PE or ELF image on any supported host, without an active target,
native engine, `objdump` or Windows SDK:
```sh
rea inspect-native-exports ./runtimes/linux-x64/native/libexample.so --json
```
```json
{
"name": "inspect_native_exports",
"arguments": { "path": "/artifacts/native/example.dll" }
}
```
The complete Evidence identifies the stable artifact snapshot by path, byte
count and SHA-256.
- PE rows reproduce the export directory: each used export-address-table slot
with its biased `ordinal` (`OrdinalBase` plus the slot index), one row per
name (aliases share an ordinal), `address`
as the preferred-base virtual address, or `forwarder` text when the slot
forwards to another DLL. Unused zero-address slots are omitted;
ordinal-only exports have a null `name`. The name-ordinal table stores an
unbiased 16-bit EAT index; reported biased ordinals may exceed 16 bits. The
directory's DLL name, ordinal base and timestamp are reported, and an image
without an export directory is a complete empty inventory.
- ELF rows list defined `.dynsym` symbols with global, weak or GNU-unique
binding and default or protected visibility. Each row keeps `value`
(`st_value` as stored; bit 0 marks Thumb code on 32-bit ARM), `size`, `type`
and `binding`. ELF string tables do not declare a text encoding. Names that
contain non-ASCII bytes are decoded as UTF-8 when those bytes form valid
UTF-8; `name_utf8_decodable` records whether the non-ASCII name bytes are
valid UTF-8 and `name_bytes_base64` retains those exact bytes (`null` for
ASCII names). When they are not valid UTF-8, `name` uses a byte-preserving
Latin-1 mapping and the raw bytes remain available. Older retained results
report null for the newly added encoding fields. `version` is null for an
unversioned symbol. A versioned
symbol reports its GNU index, a name resolved from `.gnu.version_d` or
`.gnu.version_r`, the corresponding `name_bytes_base64` and
`name_utf8_decodable` facts, and the `hidden` flag from `.gnu.version`.
Version names use the same UTF-8-when-valid, otherwise byte-preserving
Latin-1 display mapping as symbol names; ELF does not declare either
string's text encoding. Older retained results report null for the added
version-name encoding fields. `"unknown"`
means the symbol-version table could not be read. A hidden version is not
the default version that a by-name `dlsym` lookup selects.
Definition names take precedence when both tables name the same index.
Unreadable names, out-of-range name ordinals and unreadable forwarders are
listed in `issues`, their entries are not reported, and coverage is `partial`.
PE export names are bounded null-terminated byte strings and may continue
across adjacent file-backed section mappings when the virtual bytes are
unambiguous. Gaps, zero-fill and conflicting mappings remain unreadable names.
Unresolved symbol versions keep their rows: the version name is null when no
definition or requirement names the index, and `version` is
`"unknown"` when the version table cannot be read; coverage is likewise
`partial`. A dynamic symbol table without its null entry is malformed. An ELF
without section headers is `unsupported`:
enumerating dynamic symbols from the dynamic segment is not
implemented. Mach-O exports are reported by `inspect_macho`.
`max_file_bytes` defaults to 256 MiB (maximum 512 MiB; CLI `--max-file-bytes`).
Export directories in PE headers are read like those in sections. An export or
dynamic symbol table above 262144 entries, more than 262144 rows, or more than
16 MiB of retained names fails without partial success. Export rows do not establish that a symbol is reachable, safe to call
or loaded at runtime.
+204 -36
View File
@@ -18,9 +18,32 @@ 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. 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`.
- `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 or registration stacks 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.
For a compact route query, select `detail: "summary"` and a
`view: {kind: "javascript-routes", collection: "routes", limit: 20}`. The
shared [route inventory](javascript-route-inventory.md) preserves source IDs,
precise locations, partial coverage, and the complete parent Evidence for
subsequent filtered pages. Static candidates never establish complete application
coverage or reachability.
For `analyze_web_bundle`, each `webmcp_declarations` entry's
`schema_property_names` is a source-derived projection of statically known own
property keys, not schema validation. `schema_property_names_coverage` reports
how complete that projection is: `complete` means a literal properties object
was fully enumerated (including an empty literal); `partial` means a literal
object was available but some computed or spread membership was unresolved,
and the name array retains keys that were known; `unknown` means the schema or
properties owner could not be enumerated as an object literal, including
explicit non-object values; `not_present` means no effective schema or own
`properties` member was present after the `inputSchema ?? input_schema` alias
selection. `not_present` describes this projection only: it does not validate a
schema or rule out properties expressed through other schema constructs. Older
observations may omit the coverage field; treat omission as `unknown` rather
than inferring completeness from the array.
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,
@@ -31,6 +54,26 @@ from a protocol omission. Rejected DOM facts also mark derived URL and agent-hin
metadata incomplete, while valid facts from other nodes and documents remain
available in the capture.
Passive comparison keeps lookup identity, cross-capture correspondence, content
evidence, and inventory coverage separate:
| Observation | Comparison outcome |
| ------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------- |
| Renamed WebMCP tool keys/frame IDs with one matching name, owner origin, and frame URL | Compare the registration content; local IDs do not prove change |
| Duplicate WebMCP owners with the same name, origin, and frame URL | Unknown correspondence; complete group count differences still prove change |
| Matching script inventory keys with retained source digests | Compare the retained SHA-256 values even without a producer hash |
| One corresponding script source is unavailable, or neither source nor producer hash is available | Source equality remains unknown; inventory changes and other proven differences survive |
| A partial HTTP response group is a subset of the complete peer | Unknown; omitted repeated responses can explain the difference |
| An observed HTTP response fingerprint is absent from the complete peer group | Changed; omissions cannot explain that retained response |
| A WebSocket connection or frame inventory is incomplete | Do not align retained payloads by position |
Storage fingerprints decode supported CDP primitive/JSON values before hashing.
Debugger descriptions, previews, and remote references do not represent storage
content. Actual JSON properties with those names remain content. Reference-only
objects, unsupported values, and opaque cookie partition identities remain
incomplete. Previously exported fingerprints are not recomputed; recapture both
sides when comparing captures produced across this decoder change.
- `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.
@@ -66,14 +109,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).
@@ -98,14 +152,11 @@ bundle, for example
`"/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`.
REA attaches through the browser's HTTP discovery endpoint on a loopback port,
or, for some passive tools, through a
[browser socket](#browsers-without-http-discovery). Connection failures report
`endpoint_unreachable`; a reachable HTTP service that returns a non-200 or
non-JSON discovery response reports `invalid_endpoint_response`.
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
@@ -157,7 +208,9 @@ file for you.
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.
automation channel has neither endpoint, and REA cannot attach to it. To
inspect such a page, reproduce it in a Chromium-family browser started with
`--remote-debugging-port`.
## Request boundary
@@ -190,6 +243,11 @@ rea inspect-web-page http://127.0.0.1:9222 TARGET_ID \
--json
```
Passive observation honors the requested duration using cancellable waits within
the runtime's timer range. Long windows stay pending across successive waits;
caller cancellation, target termination or leaving the allowed origins ends
collection early. `window_elapsed` identifies completion of the requested wait.
Static bundle analysis and a user-driven observation window are separate operations:
```bash
@@ -272,21 +330,54 @@ Call `list_browser_targets` first because target IDs are browser-instance-specif
REA establishes a main-frame origin boundary before enabling Runtime, Debugger, or Network observation, rechecks it before document capture, and validates it again afterward. If the main frame navigates during final capture, REA discards the mixed result and returns `target_changed`.
The web-session timeline uses CDP monotonic seconds where the event producer
provides a comparable timestamp; absent or invalid timestamps are `null` rather
than a synthetic zero. Events buffered while REA samples the window boundary are
retained only when their timestamp places them after that boundary; older events
are excluded. A buffered event without a comparable timestamp may remain as
contextual evidence with `timestamp: null`, accompanied by a limitation stating
that it does not establish activity in the selected window. Main-frame scope
enforcement still applies to contextual navigation events. Network redirect
events retain their paired prior-hop response context when the timestamped
next-request event falls inside the window. The pre-arm timeline buffer is bounded
by original CDP message bytes. If clock sampling completes after that budget is
exhausted, the operation returns `payload_limit` instead of a partial window.
Cancellation and clock-request failures retain their own outcomes.
## Data minimization
The Link projection parses repeated CDP header field lines independently and
retains their declarations in order. Each link keeps its target in `href` and
its relationship context in `context_url` with a separate `context_scope`;
absent `anchor` uses the response URL, while a relative `anchor` resolves
against that URL. Anchored links remain in the projection, but a `link_rel`
agent hint is emitted only when the resolved context matches the response URL
before userinfo credentials are removed. Following [RFC 8288](https://www.rfc-editor.org/rfc/rfc8288.html),
the `rel`, `anchor`, and `type` parameters use their first occurrence, and
quoted-pair escapes are decoded as specified by [RFC 9110 §5.6.4](https://www.rfc-editor.org/rfc/rfc9110.html#section-5.6.4).
The context fields are optional when reading older saved captures. Commas
inside URI targets or quoted parameters remain part of those values.
Well-known-resource hints require an exact supported pathname; path case is
preserved because a case variant may identify a different HTTP resource.
COOP and COEP projections retain the exact declared token after validating the
complete Structured Field Item, including parameters such as `report-to`.
These are declarations, not an assessment of the document's effective policy;
absent, malformed or non-token items project as `null`.
REA retains local observation data and removes only URL userinfo credentials, explicitly declared scenario secrets, and values from structured credential/storage fields:
- URL observations preserve the complete local URL, including query values, fragments, duplicate parameters, and their order. Only username/password userinfo is removed; malformed original URL strings remain available with `origin: null`.
- DOM snapshots retain node types, node names, value lengths, and attribute names, but not text or attribute values.
- Accessibility structure and roles are retained by default, while names and descriptions require `include_accessibility_text: true`.
- Supported DOM URL attributes are projected separately as resolved destinations. Empty `action` on HTML or XHTML forms and empty `formaction` on HTML or XHTML submit controls resolve to the document URL; other relative URL attributes resolve against the captured document base.
- Accessibility structure and reported roles are retained by default, while names and descriptions require `include_accessibility_text: true`. The browser protocol may omit an individual role; that role remains unknown for comparison. Other observed accessibility differences can still establish a change.
- 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.
- 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. A JSON-typed request body that CDP reports through `hasPostData` but omits from `postData`, or that cannot be parsed as JSON, makes `json_body_shapes` unavailable. That request's `body_shapes.status` is `partial`, not `included`, when only its response shape was captured. 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
cannot prove a missing WebSocket connection. A missing request in a limited inventory cannot prove absence; a retained
request absent from a complete peer can prove a change. 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,
@@ -301,15 +392,19 @@ REA retains local observation data and removes only URL userinfo credentials, ex
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.
Repeated HTTP samples retain multiplicity. A partial group that is contained
in a complete peer cannot prove modification, even when the omitted response
had different content. A retained sample absent from the complete peer can
prove modification. Two partial groups cannot establish response
correspondence from their different retained samples alone.
WebSocket stream inventories and connection counts likewise require complete
WebSocket coverage. Redirect resource types use only shared known metadata:
WebSocket coverage. Retained frame positions are compared only with complete
connection/frame inventories; matching direction/opcode sequences alone do
not prove alignment after drops. 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`.
- Console observations with a stack source retain call type, argument types, the CDP `Runtime.Timestamp` in milliseconds since Unix epoch, and source location. An absent or invalid timestamp is `null` and makes `console_events` incomplete; an observed zero remains zero. Argument types are listed by CDP argument position. An absent or non-array argument inventory is `null`, and a malformed argument keeps its position as `unknown`; either makes `console_events` incomplete, while an observed empty inventory remains `[]`. A malformed stack or top stack frame is not replaced by its caller: the console event is excluded, and a request initiator location or WebMCP `registration_source` is `null`; each is reported as `invalid_protocol_value`. This epoch-millisecond clock is separate from the monotonic-second timestamps in `observe-web-session`. When console text capture is selected, already-delivered primitive values are retained verbatim with their original `argument_index`; objects, getters, and remote properties are never expanded. Text capture is `unavailable` when the argument inventory is unknown.
- WebSocket observations retain direction, opcode, and payload byte length. A missing or malformed CDP opcode is returned as `null`, with opcode-dependent payload byte length and shape also `null`, and marks `websocket_frames` incomplete. Missing or wrong-typed payload data is not treated as empty; its byte length is `null`, and a text payload shape is unknown. Numeric opcode `0` and an observed empty payload remain distinct from missing data. Opcode is nullable in the public inspection schema, so clients must handle unknown protocol values. When shape capture is selected, text messages are classified as text or complete value-free JSON shape; binary bytes, hashes, prefixes, and raw payloads 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 fingerprints hash all decoded response bytes, including bodies above 64 KiB, subject to the [64 MiB per-message CDP limit](#request-boundary). Missing or undecodable cache bodies 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.
@@ -331,6 +426,57 @@ validation and resource bounds. Omitted script metadata makes its affected
section explicitly truncated.
Disallowed-origin entries are filtered before retention.
Script metadata and selected source reads use one fixed admitted-script snapshot.
New in-scope script IDs still retained when the final CDP read completes are
counted in `scripts.total` and `completeness.dropped_events.scripts`, with
`scripts` marked truncated; they do not extend the source-read pass. Repeated
late events for the same ID count once. The original snapshot is not replaced
or reduced by later updates to an existing ID. These omissions do not imply
that a source byte or time budget was exhausted.
Selected script-source collection shares a 32 MiB aggregate UTF-8 retention
budget and the `command_timeout_ms` deadline (default 20000 ms) across the
capture. The byte budget has no runtime override. An oversized source is
omitted while later smaller sources may still fit. When the deadline expires,
remaining source reads are skipped. Script metadata, per-item omission reasons,
and `script_sources` completeness remain available. A single CDP response can
still be materialized up to the separate 64 MiB transport limit before source
admission; the retention budget does not bound that transient allocation.
Bundle parsing and semantic inspection run sequentially in an owned child,
without executing captured code. These are separate phases. `max_heap_mb`
defaults to 1024 MiB V8 old-space (range 128..16384). `analysis_timeout_ms`
defaults to 30000 per script, including startup and transfer.
`total_analysis_timeout_ms` defaults to 300000 across script analysis. A
per-script deadline above the effective total is rejected. A failed or
timed-out script becomes an explicit unknown while completed findings and later
scripts remain available. Total-deadline expiry returns a typed timeout with
partial Evidence and identifies unstarted scripts. `source_map_fetch_timeout_ms`
defaults to 30000 and is an item limitation, not that analysis timeout. These
controls are also available as CLI flags with hyphenated names. Worker protocol
and result byte budgets have no runtime override.
One web-bundle capture/analysis is admitted per REA process at a time. Completed
script results share an 8 MiB serialized transfer budget, with host heap
reservation checked before decoding and validating another result.
When the host reservation is what reaches zero, the failure names that heap
headroom and the remaining fixed budget. When the 8 MiB allowance itself is
exhausted, the failure names that fixed budget and says it has no runtime
override. Either stop ends further script launches and returns a typed
resource failure with completed facts and explicit unstarted-script unknowns.
Worker control frames are limited to 64 KiB. Cleanup must confirm child termination
before releasing its private files; failed cleanup retains its exact owner and
blocks replacement until retry succeeds. Cancellation retains completed bundle
facts, including cancellation during final cleanup.
The worker heap setting is not a process-family RSS limit. Node enforces V8
old-space flags; Bun ignores them. Windows Bun instead uses an owned process
commit limit with a 512 MiB runtime reserve; other Bun hosts have no enforced
memory limit. An allocation failure before that Windows limit is reported as
reached still names the configured limit and says it was not reached. These
bounds do not establish an aggregate host-memory limit across other REA
operations or external processes.
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
@@ -342,13 +488,31 @@ 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.
`resource_key` remains the exact observation identity used by script
reconciliation. Capture comparison groups resources by URL, origin, type and
MIME type, then compares known sizes within a group. If a matched group contains
an unknown size, comparison reports unknown rather than treating the observation
as a resource addition or removal; complete inventory coverage still establishes
when an entire group is absent.
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,
CDP discovery has a 10-second response deadline. HTTP discovery documents
use an 8 MiB budget. Browser-socket discovery (`Browser.getVersion`,
`Target.getTargets`) uses the 64 MiB protocol message budget. Neither budget
nor the discovery deadline has a runtime override. `list_browser_targets` accepts no
`command_timeout_ms`: HTTP discovery and browser-socket discovery
(`Browser.getVersion`, `Target.getTargets`) stay on that fixed deadline, and
the socket closes without a later command. Other CDP tools use
`command_timeout_ms` (default 20000) for each WebSocket handshake and command
response, separate from the caller-selected observation window, discovery, and
the 5000 ms cleanup grace. A browser-level socket's handshake and the following
`Target.attachToTarget` each receive that full deadline; they do not share one
budget. A stalled handshake or command reports
`provider_timeout` and names `command_timeout_ms` only when that operation
accepts it; caller cancellation still reports cancellation. WebSocket messages
remain subject to the [64 MiB limit](#request-boundary), which has no runtime
override. 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
and malformed replies to pending commands poison the connection so later
commands fail closed. A correlated reply's session ID must match the command's
@@ -357,7 +521,7 @@ 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. 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`.
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 uses `source_map_fetch_timeout_ms` (default 30000) and a 64 MiB aggregate streamed-response budget across redirects. The byte budget has no runtime override. A fetch overrun is an item limitation, not a thrown analysis timeout. 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`.
source-map items retain the script's `declared_url` separately from the observed
`effective_url`. successful maps use the latter as their source-resolution base.
@@ -369,6 +533,10 @@ Current fetches emit a string or null; historical observations may omit
An observed URL must fit before its response body is read. If it cannot fit,
remaining map results are explicitly unknown; failed cancellation retains its
cleanup context and resource constraint.
Indexed maps whose decoded mappings cross the next section's starting position
are invalid under [ECMA-426](https://tc39.es/ecma426/2024/), rather than complete
maps with clipped mapping rows. Fetch collection and source tracing share this
validation.
## Non-goals and threat model
+80 -4
View File
@@ -23,9 +23,21 @@ 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. `capture.limits` controls retained event and network-content budgets;
it does not change those liveness deadlines.
Duration, action, and navigation timeouts remain separate from CDP command
deadlines. `command_timeout_ms` (default 20000; omit it to keep that default
and leave it out of Evidence) bounds launch startup, the connect-over-CDP
handshake, target lookup, environment commands, and storage CDP commands,
including replies from attached child targets. Set it in the scenario JSON.
`capture-browser-scenario` has no `--command-timeout-ms` option.
It does not change action `timeout_ms`, `wait_for_timeout.duration_ms`, or
navigation. Service-worker restoration keeps a fixed 5000 ms grace and does
not inherit a raised `command_timeout_ms`. CDP discovery stays on its own
fixed deadline. When service workers are blocked, the registration check
before discovery has its own fixed 10000 ms deadline; `command_timeout_ms`
does not apply because discovery has not started. The discovery deadline
covers endpoint discovery only. The following WebSocket handshake and
`Target.attachToTarget` each use `command_timeout_ms`. `capture.limits` controls retained event and network-content
budgets; it does not change those liveness deadlines.
## Minimal launch example
@@ -107,6 +119,11 @@ Through MCP, pass the same object as the `capture_browser_scenario` arguments.
The final step's DOM and accessibility artifacts retain the page state at that
capture time inline. REA still launches and cleans up the browser profile.
Action `timeout_ms` and `wait_for_timeout.duration_ms` accept integers from 1
through 2,147,483,647 milliseconds, matching the underlying timer representation.
Larger values receive an input error before browser launch. Omit `timeout_ms`
to keep an action pending until completion or request cancellation.
The click's `timeout_ms` covers the click action. A non-navigating `fetch` or
delayed render can finish later, so use `wait_for` to observe a page-specific
ready condition before final capture. A page that updates synchronously can
@@ -144,6 +161,36 @@ 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.
That cleanup requires REA to remain running. Abrupt termination, including
Windows process-tree termination or POSIX `SIGKILL`, can leave the launch
profile and Playwright artifacts behind. A profile can retain cookies, history,
cache and origin storage. Later REA runs currently do not reclaim these paths.
Playwright 1.58.2 can also leave an artifacts directory when launch preparation
fails before a browser process exists, such as for a missing executable.
POSIX wrappers can request awaited cancellation with `SIGINT` or `SIGTERM`;
Windows process termination is not an equivalent graceful-cancellation channel.
Do not identify owned artifacts by directory prefix or creation time alone.
See [#4659](https://github.com/morluto/rea/issues/4659) for the remaining recovery
and wrapper-cancellation work.
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
@@ -177,7 +224,9 @@ statements remain under the attached debugger client's control.
The result is Evidence with an initial state followed by one record per
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,
accessibility, URL, history, and storage. Actions after a failed or cancelled
action are not run and are recorded as `cancelled` with a not-executed error.
Console, page-error, network,
WebSocket, frame, worker, popup, and cancelled-download events are attributed to
steps. Retained events and requested artifacts are returned inline; omitted
events are counted by kind and identify affected network transactions.
@@ -338,6 +387,24 @@ observed) and excludes the sequence reference from normalization.
Playwright scenario capture does not supply initiator stacks; passive CDP
observation retains its own initiator evidence.
## Storage value fingerprints
New storage snapshots include `fingerprint_version: 2`. Non-secret, well-formed
JavaScript strings retain their SHA-256 over UTF-8. Strings with unpaired UTF-16
surrogates use SHA-256 over a raw `0xff` byte followed by their exact UTF-16LE
code units; this prefix cannot occur in UTF-8. Declared secrets remain
`redacted-secret` with a null digest, without a second or legacy digest.
A missing version identifies legacy UTF-8 fingerprints, which replaced unpaired
surrogates and cannot establish value identity. Storage containing any legacy
hashed value is conservatively compared as `unknown`, including old-to-old
comparisons; recapture both sides to compare reliably. This also conservatively
withholds some otherwise provable legacy differences. Empty or entirely
secret-redacted storage keeps its existing comparison semantics. The version is
checked before normalization and excluded from content comparison: changing
encoding metadata alone does not establish a website change. Existing captures
and their completeness records are not rewritten.
## Scenario comparison
`compare_web_captures` and `compare-web-captures` also accept two complete
@@ -370,6 +437,15 @@ sorted canonically before application, making the same policy reproducible
regardless of input order. Rules operate only on already-redacted durable
capture fields and are preserved in the result.
Cookies, local storage, and session storage are compared as unordered inventories
after caller literal rules apply. Enumeration order alone does not establish a
storage change. Canonical ordering uses the complete entry, preserving cookie
scope/flags, value fingerprints, and duplicate entries even when redaction or
literal rules make names collide. Captured storage arrays retain their original
order. The `compare_storage_entries_as_unordered_collection` built-in rule
participates in the normalization commitment; new storage comparison digests
therefore differ from earlier order-sensitive comparisons.
Each aligned step reports changed, unchanged, or unknown. Changed and unknown
action, screenshot, DOM, accessibility, URL, history, storage, and event
artifacts carry their normalized before/after digests and capture states.
+90
View File
@@ -0,0 +1,90 @@
# Static analysis in GitHub Actions
The [minimal workflow](https://github.com/morluto/rea/blob/main/examples/github-actions/rea-analysis.yml) builds a small
JavaScript application, analyzes its files without executing them, and uploads
machine-readable results. It uses the existing CLI, not a separate GitHub Action
or an MCP client. It does not promise support for every REA provider.
## Run the example
1. Copy the example to `.github/workflows/rea-analysis.yml` in your repository and
commit it to the default branch. No existing application or package manifest is
needed for the self-contained demonstration.
2. Enable GitHub Actions if necessary. Select **REA static analysis example** in
the Actions tab and choose **Run workflow**.
3. Download the `rea-analysis` artifact after the run. Its files are:
| File | Meaning |
| ----------------- | -------------------------------------------------------------------------------------------------- |
| `analysis.json` | Original CLI JSON, including complete Evidence on success or the error report when one is emitted. |
| `analysis.stderr` | Original diagnostics/progress; an empty file is normal when there are none. |
| `exit-code.txt` | Original analysis process exit code. |
The example pins Node.js 24.22.1, REA 6.7.0, and action commit revisions. It installs
REA into the runner's temporary directory rather than changing the application's
dependencies. This pins the tool version, not every transitive npm dependency;
use a reviewed, committed package lock and `npm ci` if your integration requires
that stronger dependency-reproducibility guarantee.
## Prerequisites and trust
The runner is GitHub-hosted Ubuntu 24.04 with Bash, npm, and `ps` from `procps`.
Other Linux images must supply `procps`; REA's supervised process controls use it.
This static JavaScript operation needs no Hopper, Ghidra, IDA, Java, browser,
Docker, GPU, external API key, or user-supplied GitHub secret. The workflow only
requests `contents: read`; artifact upload uses the runner's normal Actions
credentials.
Replace **Build a known artifact** with your reviewed build, and set
`ARTIFACT_PATH` to its application directory (relative to the checkout) or an ASAR.
The demonstration creates its own tiny application; it deliberately throws if
executed. REA reads it statically. Your replacement build itself can execute code:
review it separately and do not add untrusted pull-request builds to a privileged
`pull_request_target` workflow. The example is manually triggered, not an
untrusted-PR execution policy.
Reports can contain paths, URLs, source-derived facts, and caller-selected data.
Only analyze artifacts and publish reports you are authorized to share. Do not
rely on REA inferring secrets from their names. Review artifact access and
retention for your repository; the example retains reports for seven days.
## Failure, partial results, and unsupported capabilities
The analysis step captures stdout and stderr separately, records `$?`, and exits
with that same status. There is no `continue-on-error`, `|| true`, output filter,
or pipeline that could turn a failed analysis green. Before checkout or other
fallible steps, the workflow creates a fresh report directory under `RUNNER_TEMP`.
Analysis and upload both use that directory, never a checked-in `reports/rea` or
other workspace files. The upload step uses `always()` when that directory was
prepared, so available diagnostics survive an analysis failure. If installation
or the build failed before analysis began, it is empty and upload warns rather
than publishing stale results or inventing a successful result. Runner loss or
cancellation can still prevent upload.
`--json --full-output` selects a machine-readable envelope; it does **not** mean
that analysis was complete. A successful operation can retain partial analysis,
unknowns, and limitations. Exit zero is not a security verdict, proof of complete
coverage, or a policy gate for those findings. Inspect the saved Evidence and
coverage before making such a decision. See [output and exit status](cli.md#output-and-exit-status).
A nonzero exit can represent invalid input, unavailable tooling, an unsupported
host/capability, or another operation failure. Keep the original JSON error (when
emitted), stderr, and exit code, and use their category/message to diagnose it.
An early startup failure may leave `analysis.json` empty; do not parse that as
successful analysis. Select a supported operation or explicitly provision its
[documented prerequisites](installation.md); do not silently switch providers,
execute the artifact, or ignore the failure to obtain a green job.
To exercise the failure path, temporarily set `ARTIFACT_PATH` to
`build/missing-artifact` without changing the demo build. Analysis must fail,
`exit-code.txt` must be nonzero, and the available reports must still upload.
To exercise unsupported input, set `ARTIFACT_PATH` to `build/rea-ci-demo/index.js`:
this operation accepts an application directory or ASAR, not a single source file.
The retained JSON error has category `unsupported_target`; the step exits nonzero.
Restore the correct path before using the example normally.
To exercise partial analysis, append `export const broken = ;` to the demo's
JavaScript. REA 6.7.0 retains `data.normalized_result.statistics.parse_failures`
and an incomplete-coverage limitation while the operation can still exit zero.
Inspect those facts instead of treating process success as full coverage. The
report JSON and diagnostics remain unfiltered in all three cases.
+472 -7
View File
@@ -3,6 +3,8 @@
Use the CLI for a direct inspection or a script. It uses the same application
workflows and evidence contracts as REA's MCP server. Each CLI invocation is a
separate process; save results when you want to use them in a later command.
For a pinned, noninteractive workflow with retained reports, see
[static analysis in GitHub Actions](ci-analysis.md).
## Run a command
@@ -30,6 +32,13 @@ jq -c '{source: {kind: "inline", evidence: .}, view: {kind: "summary"}}' app-evi
rea inspect-analysis-view app-view.json
```
The request can also name the saved file instead of embedding it:
`{"source": {"kind": "evidence-file", "path": "app-evidence.json"}, "view": {"kind": "summary"}}`.
The CLI resolves a relative path against the working directory; MCP clients
pass an absolute path. REA reads and authenticates the file, then projects it
like retained Evidence, so a large file is bounded by the file reader rather
than charged as inline input.
For example, a 334 MB Obsidian application Evidence file yields a summary view
of about 10 KB. Replace the view with
`{"kind": "page", "collection": "modules", "offset": 0, "limit": 32}` for a
@@ -37,6 +46,119 @@ module page. See
[JavaScript application workflows](javascript-application-workflows.md) for
views, feature traces and comparisons that take the same saved Evidence.
## JSON Lines record output
Use `--format jsonl` to consume row-oriented results one record per line:
```bash
cat > observations-view.json <<'JSON'
{"source":{"kind":"evidence-file","path":"app-evidence.json"},"view":{"kind":"javascript-facts","property_keys":[],"offset":0,"limit":32}}
JSON
rea inspect-analysis-view observations-view.json --format jsonl > observations.jsonl
```
This request selects up to 32 observation identities and their provenance,
without observation property values.
This changes JSONL object-collection framing. Scripts that need one complete
report or a scalar field should use `--format json`. For a full application
graph, first project a supported analysis view rather than selecting an
arbitrary nested graph array.
A top-level array emits its elements in source order. An object must have one
clear `rows`, `items`, `records`, or `occurrences` array, directly or under
`page`. Evidence uses the same boundaries inside `normalized_result`.
An arbitrary report or a scalar selection is refused with an actionable error;
use `--format json` for a complete document. If several collections exist,
select one with `--filter-output`, for example `normalized_result.rows`.
Object collections finish with a control record:
```json
{
"$rea": "jsonl/v1",
"type": "metadata",
"records": {
"path": ["normalized_result", "rows"],
"emitted": 2,
"source": 10,
"filter": "normalized_result.rows[0,2]"
},
"context": {
"evidence_id": "ev_…",
"normalized_result": {
"coverage": {
"status": "page",
"examined": 10,
"total": 100,
"next_offset": 10,
"exhausted": false
}
}
}
}
```
This abbreviated example illustrates framing. The actual `context` preserves
all non-row fields, including Evidence subject, provider, authority, confidence,
locations, links, limitations, parent/view digests, pagination and coverage.
With `--full-output`, it also retains the command envelope and its metadata.
A data filter selects row fields/slices while this context remains available;
excluded peer record collections and unselected Evidence `raw_result` are not
restored. An explicitly selected `raw_result` remains selected. Retain the
original Evidence for complete observations.
`records.emitted` and `records.source` count the selected output and its source
array; they do not assert analysis completeness. Source coverage and unknowns
retain their original meaning. Existing row identities and indices remain
unchanged unless your field selection excludes them. Empty object collections
still emit metadata; an empty top-level array emits no lines. Bare top-level
arrays have no synthetic coverage record.
Command and output-validation failures emit `{"$rea":"jsonl/v1","type":"error","ok":false,"result":…}`
and retain the original diagnostic payload. SDK validation/sentinel errors use
the SDK's exit status rather than guessing from data fields. `$rea` is reserved
at the top level of a data record, so control records are unambiguous; collisions
are refused before rows are written. Nested `$rea` values are ordinary data.
A missing final metadata record or nonzero exit can indicate an interrupted
export; do not treat already emitted rows as a complete result.
`--token-count` estimates this exact representation, including metadata, one
line at a time. Errors remain error records even with token counting enabled.
Token windows cannot preserve JSON Lines and are rejected. JSON encoding and
stdout writes are incremental, including large escaped strings; this is output
streaming over a completed result, not incremental analysis. The analysis still
needs its normal resources. A single record must fit the runtime string limit
for token estimation; remove `--token-count` to stream a larger record.
Standalone version and MCP doctor diagnostics remain single JSON documents
in JSON Lines mode. Incur's streaming generator events and successful sentinel
responses retain their SDK protocol. MCP application results and Evidence
contracts are unchanged.
## Shell completion
The CLI includes optional shell completion. Run `rea completions --help` to
see the available shells and their setup commands. For Bash, load the hook
into the current shell:
```bash
eval "$(rea completions bash)"
```
The current Bash hook requires support for `complete -o nosort`; macOS's
system Bash 3.2 cannot load it. Use a newer Bash for this hook.
For future interactive non-login Bash sessions, add the command to
`~/.bashrc`. Interactive login Bash sessions read the first available file in
`~/.bash_profile`, `~/.bash_login`, and `~/.profile`; add the command to the
file your shell reads, or have that file source `~/.bashrc`. Remove the command
from the startup file to stop loading completion, and run `complete -r rea` to
remove the registration from the current shell. These are manual, opt-in
steps.
Completion is a discovery aid; use `rea COMMAND --help` to confirm the
available options and subcommands.
## Managed assemblies (.NET)
For a .NET PE/CLI assembly, inspect its identity and deployment form before
@@ -56,10 +178,22 @@ or repeat it to select several types.
Selection limits decoding to the selected types; member references remain an
assembly-wide inventory, so the output can still be large.
Select `--detail summary --evidence-bundle /absolute/path/members.json` to
receive identity, selection, coverage, limitations and collection counts while
saving the complete original Evidence in a new bundle. Summary requires that
explicit destination; existing files are preserved. Later
`inspect-analysis-view` calls can page the exact parent from the saved bundle
without reopening the assembly. See
[CLI summary and saved member rows](managed-code-analysis.md#cli-summary-and-saved-member-rows)
for the complete command sequence. Omitted or `--detail complete` retains the
full inline response.
Choose the workflow from the artifact's contents rather than its `.exe` or
`.dll` extension. The managed parser requires PE/CLI metadata; it does not
unpack .NET single-file hosts or decode IL2CPP metadata. NativeAOT method bodies
require native analysis. See the [managed-code guide](managed-code-analysis.md)
unpack .NET single-file hosts or decode IL2CPP. For the image, type and method
names in a Unity `global-metadata.dat`, use
[IL2CPP metadata](il2cpp-metadata.md). NativeAOT method bodies require native
analysis. See the [managed-code guide](managed-code-analysis.md)
for supported deployment forms, selection semantics, and coverage limits.
## SQLite snapshots
@@ -83,6 +217,47 @@ 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.
Inspect a Godot PCK without running the game:
```sh
rea inspect-artifact game.pck --detail summary --evidence_output evidence.json --json
rea extract-artifact game.pck --json
```
Formats 2, 3 and 4 retain header, directory and integrity facts. Compact delivery
keeps complete Evidence in the requested bundle. See [Godot PCK](godot-pck.md)
for embedded packs, batch queries and unknown encrypted coverage.
Inspect local model declarations without loading weights:
```bash
rea inspect-model-artifact /absolute/path/model.gguf --json
rea inspect-model-artifact /absolute/path/model.safetensors --hash full --json
```
Default hashing reads only headers and leaves full-file identity unknown.
`--hash full` streams the complete file for SHA-256. Both return typed metadata,
original tensor declarations and derived totals. MCP summaries retain complete
Evidence for filtered metadata, tensor and type-total pages; CLI compact delivery
uses `--detail summary --evidence_output FILE`. See [model files](model-artifacts.md).
Inspect Unity IL2CPP metadata names without Unity or a disassembler:
```bash
rea inspect-il2cpp-metadata /absolute/path/global-metadata.dat --json
```
The command decodes a version 24, 27, 29, 31 or 39 `global-metadata.dat` and reports assembly
images, type, method and member names, string literals, index ranges, header
sections and SHA-256. With `--binary PATH` it also reads the matching
`GameAssembly.dylib`, `GameAssembly.dll` or `libil2cpp.so` (never runs it) and
links each method to its code address; `--ghidra-seeds PATH` then writes Ghidra
function seeds. It does not decrypt metadata. MCP defaults to compact counts
with retained Evidence; query indexed rows with filters, batch indices and
selected fields. The CLI defaults to complete Evidence;
`--detail summary --evidence_output FILE` exports the full result to a new file
and returns a compact overview. See [IL2CPP metadata](il2cpp-metadata.md).
Inspect an embedded cargo-auditable Rust dependency list without installing
Rust or configuring a disassembler:
@@ -95,6 +270,49 @@ 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).
Inspect a PE Authenticode certificate table without executing the image or
verifying signatures:
```bash
rea inspect-pe-certificates /absolute/path/to/application.exe --json
```
The workflow reports `WIN_CERTIFICATE` entries and, for PKCS#7 signedData,
embedded X.509 serial, issuer, subject, and validity fields. A missing security
directory is observed absence of that table. See
[PE Authenticode certificate tables](pe-certificates.md).
Inspect ELF PT_NOTE and SHT_NOTE tables without executing the image:
```bash
rea inspect-elf-notes /absolute/path/to/program --json
```
The workflow reports note owners, types, descriptor hashes, and GNU build-id
or ABI-tag fields when those descriptors match. A missing note table is
observed absence of those declarations. See [ELF note tables](elf-notes.md).
Inspect a CPython 3.7–3.13 `.pyc` header and nested marshal code objects
without executing bytecode or installing Python:
```bash
rea inspect-python-pyc /absolute/path/to/module.pyc --json
```
The workflow reports the pinned CPython version for the magic, PEP 552 hash or
mtime/size fields, nested function names, and bytecode SHA-256. Bytecode is
not decompiled. See [CPython pyc inspection](python-pyc.md).
Inspect an Erlang BEAM file without executing bytecode:
```bash
rea inspect-erlang-beam /absolute/path/to/app.beam --json
```
The workflow reports IFF chunk names, atom tables, exports, imports, lambdas,
and the Code chunk header. Missing optional tables stay null. See
[Erlang BEAM files](erlang-beam.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
@@ -154,7 +372,7 @@ 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`, follow `message`, `remediation.action`, and the original
reasons in `details.rejections`. For Hopper, Ghidra, IDA, and JADX, run
reasons in `details.rejections`. For Hopper, Ghidra, IDA, JADX, and Kuna, 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
@@ -168,6 +386,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
@@ -175,7 +399,11 @@ an exact result when the target bytes, operation, parameters, provider and
settings match. Mutations and cursor-dependent calls are excluded from the
cache. Snapshot files are local and use owner-only permissions.
Dispatching a metadata write invalidates the active session's snapshot cache,
Importing an Evidence bundle refreshes retained subject display names and paths.
It preserves cached analysis results, including observations for other targets.
Snapshot bindings that use a replaced record retain its updated display path.
Dispatching a provider metadata write invalidates the active session's snapshot cache,
even if its reply fails: the provider may already have applied the write.
Close and reopen the target before importing or saving another snapshot.
@@ -223,7 +451,12 @@ Exports preserve an existing destination unless `--overwrite` is explicit.
For application traces and version comparisons, see
[JavaScript application workflows](javascript-application-workflows.md).
MCP [retained Evidence references](mcp-contracts.md#retained-application-evidence-inputs)
belong to one connection. Separate CLI calls consume full saved records.
belong to one connection. Separate CLI calls can consume full saved records.
`inspect-analysis-view REQUEST.json --evidence-bundle BUNDLE.json` resolves
the request's `retained-evidence` source ID from that authenticated bundle,
including bundles exported from an MCP session. The same view JSON works for
every supported parent kind; no target is reopened and no record is selected
by file order. Omit `--evidence-bundle` when the request supplies inline Evidence.
## Import historical source
@@ -280,9 +513,37 @@ reported separately as configured-secret exclusions.
Re-import saved graphs whose pattern-based exclusions lack `pattern` before
using them in source-to-bundle comparisons.
Entries that change during an import retain an unavailable-entry diagnostic
alongside readable siblings. If enumeration of an already admitted root fails,
the graph keeps its observed children and records the root failure as a
top-level limitation. That partial inventory does not establish absence among
unexamined children. POSIX directory-entry bytes that are not valid UTF-8 cannot
be represented as graph paths; the containing directory (or root) is marked
unsupported, and REA does not access the entry through a replacement-character
name. Symlink targets are read as bytes too: a target that is not valid UTF-8
is retained as an unreadable symlink with its bytes reported in the limitation,
and is never resolved through a replacement-character path. Readable siblings
remain inventoried.
JavaScript and TypeScript import parsing requires valid UTF-8. Malformed source
bytes retain their original hashes and sizes with a decoding diagnostic; REA
does not infer module targets from replacement characters.
does not infer module targets from replacement characters. Exact dynamic-import
strings, expression-free templates and runtime-transparent TypeScript wrappers
retain their targets; computed imports remain unknown. TypeScript import-equals
dependencies remain `requires` relationships whether or not they are exported.
REA derives historical CommonJS paths only when the selected loader and its
base are proven: this includes the exact
`createRequire(new URL("./file.cjs", import.meta.url))` form and
`require.resolve` with `{ paths: [__dirname] }`. Other custom bases and search
options remain partial with unknown resolution and the literal requested
specifier. Overwritten resolver methods retain that uncertainty; they are not
attributed to Node. `require.main` is a module object, so calling it does not
establish a loader reference. These relationships do not establish execution;
Node's [`require.resolve`](https://nodejs.org/docs/v24.22.1/api/modules.html#requireresolverequest-options)
performs lookup without loading the target. An excluded `package.json`
also leaves package-directory selection unknown rather than treating the
manifest as absent and applying an `index` fallback.
Historical-source import requires safe no-follow file opens on Linux or
macOS. Native Windows returns `unsupported_host`; use Linux REA inside WSL
@@ -304,6 +565,158 @@ Choose the guide for the target and intended interaction:
Runtime requests name the target and actions. Launched targets run with your
user permissions; consult the chosen guide for host requirements and effects.
## Serve MCP over streamable HTTP
`rea mcp` speaks stdio to the client that launched it. To serve the same MCP
catalog over streamable HTTP for one local client, such as an agent environment
runtime, start the dispatcher entry with `--http`:
```sh
rea mcp --http --port 8765
```
`--host` and `--port` default to `MCP_HOST` and `MCP_PORT` from the environment,
then to `127.0.0.1` and an ephemeral port. The bound URL is written to stderr as
one JSON log line (`"msg":"MCP transport listening"`) whatever `REA_LOG_LEVEL`
is set to; nothing is written to stdout. `rea mcp --http --help` lists the accepted options. The listener serves
one client at a time: a session ends with the client's `DELETE`, and a client
that connects while another session is open takes it over, so a client that
exited without ending its session needs no restart. The listener runs until
SIGINT or SIGTERM and does not stop when stdin closes. See
[transports](mcp-contracts.md#transports) for the session, `Host` header and
shutdown contract. Client registrations written by setup and the MCP registry
entry keep stdio.
## Serve an environment manifest
`rea environment-serve <manifest.json> --evidence <capture.json>
[--replay-only] [--host H] [--port N]` serves an environment manifest
projected by `inspect-analysis-view` as an agent environment: an MCP server at
`/mcp`, the environment card at `/.well-known/agent-env.json`, and a
`/agentenv` data plane. The manifest's `parent_evidence_id` must equal the
evidence file's `evidence_id`; otherwise the command refuses with
`invalid_request` before binding. Before binding it derives the
`environment-state` view of the capture with the manifest's `template_rules`,
as `inspect-analysis-view` would, and serves from it: read tools whose views
are state-backed answer from a live state, write tools are served only where
the capture's later reads confirmed their effect, and
`/agentenv` serves `data/reset`, `data/add` and `data/get`. When the state
cannot be derived, or its catalog exceeds the discovery reservation, it logs a
warning with the error code and message and serves replay; `--replay-only` serves replay without deriving. In replay every
manifest tool replays the retained capture records it cites and `/agentenv`
answers every method with JSON-RPC `-32601`. The stopped document's `state`
member is `{"served": true, "evidence_id": …}`,
`{"served": false, "reason": "replay-only"}` or
`{"served": false, "reason": "unavailable", "code": …, "message": …}`; see
[serve from the state](agent-environments.md#serve-from-the-state).
```sh
rea environment-serve manifest.json --evidence capture.json --port 8765
```
`--host` and `--port` resolve like `rea mcp --http`: flags, then `MCP_HOST` and
`MCP_PORT`, then `127.0.0.1` and an ephemeral port. `ENVIRONMENT_NAME` renames
the served environment; set but blank, it is refused as `configuration_invalid`
like a blank `MCP_HOST`. The bound URL is written to stderr as one JSON log line
(`"msg":"MCP transport listening"`). See
[agent environments](agent-environments.md) for what the state and replay
return and do not establish.
## Export an environment bundle
`rea environment-export <manifest.json> --evidence <capture.json> <dir>
[--package-tarball <path>] [--name <override>]` writes the same manifest and
capture pair as a bundle folder the agentenv framework runs with
`agent-env run <dir>`. It reads each file once, authenticates and cross-checks
the pair with the same checks as `environment-serve`, checks the bundle name,
then prepares the state under that name as the bundle's server will at startup
to predict what the bundle serves (replay when the state is unavailable),
copies the bytes it authenticated, refuses to write into a directory that
exists and is not empty, and never overwrites a file.
```sh
rea environment-export manifest.json --evidence capture.json ./grafana-example --json
```
The folder holds a `README.md` with the bundle's provenance, an env folder
`envs/<name>/` with a `Dockerfile` that installs this exact `rea-agents`
version and serves the copied `manifest.json` and `evidence.json` (written
with mode `0600` on POSIX; Windows retains inherited ACLs) with
`environment-serve` (from the state by default), a Python verifier under
`artifacts/check_<name>/` that checks the served tool count and one served
answer, and the smoke task `tasks/smoke.json`. `--package-tarball` copies a packed `rea-agents`
tarball into the env folder and installs it instead of the registry package,
which is how the hosted lane proves a checkout rather than a release. `--name`
replaces the manifest's kebab-case name as the env folder and env id; a device
name Windows reserves (`con`, `nul`, `com1` and the like) is refused. The
command prints one JSON document listing the written files with their sizes,
the pinned package source, the served `tool_count`, and `state` in the shape
of the serve command's stopped document. See
[agent environments](agent-environments.md#export-a-bundle) for the folder
layout and what the smoke task checks.
## Compare an environment with its application
`rea environment-compare <manifest.json> --evidence <capture.json> --probe <probe.json> [--probe <probe.json> ...]`
compares the environment the pair serves with probe captures of the application it came from. Each probe is capture
Evidence of one call sequence recorded against the application after a reset; the environment's state is reset before
each probe, every request to the application's origin is answered as the served environment answers it, and the
answer is compared with the application's after ids are mapped and clocks and generated values are normalised. The command reads files only and
makes no network call. It prints the report and exits `0`, or prints a structured `invalid_request` error and exits `1`
for the same pair refusals as `environment-serve`, a probe that is not capture Evidence, or a browser probe captured
without response bodies, or without the request bodies its writes need. See
[agent environments](agent-environments.md#measure-fidelity-against-the-application) for the report.
```sh
rea environment-compare manifest.json --evidence capture.json --probe probe.json --json
```
## Report version and runtime
`rea --version` prints the package version. Add an output format to get the
version with the runtime and platform context that a support request needs:
Version reports do not initialize analysis providers. Invalid global settings
or output options still report their usual errors.
```bash
rea --version --json
```
```json
{
"name": "rea-agents",
"version": "6.7.0",
"runtime": { "id": "node", "version": "24.22.1" },
"platform": "linux",
"architecture": "x64",
"supported_runtime": {
"range": "^22.23.4 || ^24.22.1 || >=26.12.1",
"satisfied": true
}
}
```
| Field | Meaning |
| ------------------------------------ | -------------------------------------------------------------------------------------------------------------- |
| `name`, `version` | Published package name and version; `version` matches plain `--version`. |
| `runtime.id` | `node` or `bun`. |
| `runtime.version` | Version of that runtime. |
| `runtime.node_compatibility_version` | Bun only: the Node.js version it reports compatibility with. |
| `platform`, `architecture` | Node.js `process.platform` and `process.arch`, such as `linux` and `x64`. |
| `supported_runtime.range` | Supported Node.js range. |
| `supported_runtime.satisfied` | Whether this runtime is supported, judged as `rea doctor` judges it; Bun by its Node.js compatibility version. |
The field names are stable. The report contains no paths, environment values,
user or host names, and it starts no provider and reads no target. `--format`
selects any CLI output format, such as `--format yaml`. JSON follows the usual
rule: indented on a terminal, compact when piped or redirected. `--help` still
takes precedence. With other output controls, `--version` keeps its existing
behavior: valid controls such as `--filter-output` or `--full-output` print the
plain version, and invalid combinations report the usual output-flag error,
such as `UNSUPPORTED_OUTPUT_COMBINATION` for `--json` with `--token-limit`. Use
`rea doctor` for provider and host readiness.
## Output and exit status
`rea mcp doctor --json` checks production MCP startup and reports child logs in
@@ -322,6 +735,25 @@ 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.
`--format md` escapes backslashes and pipes in table headers and cells, and
renders cell line breaks as `<br>`. These are presentation changes; retained
Evidence and JSON output keep the original values. Table widths are measured
iteratively, so an admitted row page is not limited by the JavaScript function
argument count.
`--filter-output` selects fields from successful command data. Classified failures
retain their complete typed diagnostic projection even when the selector matches
a field such as `code` or `message`, or names a missing field. `--full-output`
controls whether the outer `{ok, error, meta}` envelope is included.
Selected own JSON members, including `__proto__`, remain ordinary output data.
Sibling array slices that select different fields are combined by original row
index and emitted in source order, regardless of selector order. Selecting a
whole parent row keeps that row intact and takes precedence over narrower fields
selected from the same row. For example, `rows[0,2].name,rows[1,3].version`
combines both fields for overlapping rows, while `rows[0,2],rows[1,3].version`
retains rows 0 and 1 whole.
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
@@ -338,6 +770,30 @@ output formatting.
| `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. |
`rea mcp --http` is long-running and prints no structured result. It exits
`130` on SIGINT or `143` on SIGTERM after closing the listener, the session and
its providers, and `1` when that cleanup fails.
`rea environment-serve` is also long-running: it serves until SIGINT or
SIGTERM, closes the listener, then prints one structured
`{"status":"stopped", …}` document naming the URL, environment and tool count
and exits `130` or `143` through the shared cancellation handler below.
Refusals before binding (unreadable files, non-Evidence JSON, capture Evidence
in a format other than HAR, a manifest or state whose parent is not the
supplied evidence, an unbindable port) print a structured
error and exit `1`, as does a listener that fails after binding.
`rea environment-export` is one-shot: it prints the written files and exits
`0`, or prints a structured `invalid_request` error and exits `1` for the same
pair refusals, a non-empty or file-occupied directory, a missing package
tarball or a name that is not a kebab-case label or is a Windows device name,
writing nothing in each case.
A write that fails after the first file (a permission, disk-space or
pathname-length failure on a later path) propagates as the host's failure,
naming its cause and, when it failed on one bundle file or the package
tarball, that file. A failure while syncing the finished folder names only its
cause. Either way the partial folder stays in place for inspection; remove it
before retrying, since the next attempt refuses the non-empty directory.
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
@@ -350,11 +806,20 @@ browser, web and Electron commands (including `analyze-web-bundle`,
`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
command, objdump, Rizin and GDB, and the agent environment server
(`environment-serve`). 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.
Commands with `--evidence_output` select their delivered view, then publish the
complete Evidence bundle atomically. Cancellation before publication leaves no
new file. A publication that has committed can win a later cancellation; the
`cancelled` result then keeps the written path and byte count under
`details.diagnostics.evidence_bundle`. If staging cleanup stays incomplete
after commit, the `cleanup_incomplete` error reports the same file as
`details.published_path` and `details.bytes`.
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
+72 -11
View File
@@ -14,6 +14,12 @@ preserving your REA client entry. See the
## GitHub Copilot CLI
Setup preserves an existing `tools` selection in the REA registration, including
an empty list. New registrations default to `["*"]`. A restricted selection is
aligned configuration; doctor does not widen it to expose every REA tool. See
[Copilot's MCP configuration](https://docs.github.com/en/copilot/how-tos/copilot-cli/customize-copilot/add-mcp-servers)
for tool-selection settings.
For GitHub Copilot CLI 1.0.95, `gpt-4.1` model metadata can block ordinary
chat with REA's full input-schema profile before sending a model request
(`compaction_static_context_blocked`; [#1554](https://github.com/morluto/rea/issues/1554)).
@@ -52,10 +58,16 @@ in the file the installed app reads:
`%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
another publisher, an `AnthropicPBC.Claude_<publisher ID>` package, 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.
The reported `AnthropicPBC.Claude_fnn82j28hfe8t` build can use the `Claude-3p`
deployment directory. REA recognizes that package as an unresolved location;
it does not assume that it reads the legacy AppData file or automatically
register in `Claude-3p`. See [#4432](https://github.com/morluto/rea/issues/4432)
for the reported build and manual registration evidence.
Quit Claude Desktop fully before approving setup; closing its window is not
enough. A running Claude Desktop can rewrite its configuration from memory and
@@ -67,12 +79,17 @@ 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.
both paths: first one from the package's private `Claude` folder, and when
that folder has none (some installations keep writing the real
`%APPDATA%\Claude`), one from the package's other private roaming folders,
which the package redirects in the same way. 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 keeps no private roaming file at all, setup and
uninstall cannot rule that out and also refuse. With Claude Desktop fully
quit, add REA's entry by hand to the file setup names, which is the one the
app reads, or remove it from both the private copy and the `%APPDATA%` file.
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
@@ -147,11 +164,14 @@ 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
An explicit `REA_JEB_MCP_TIMEOUT_MS` deadline, shared by catalog discovery and
each tools/call, 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.
profile into the registration; unrelated process values are omitted. An
explicit `REA_JADX_OPERATION_TIMEOUT_MS` is forwarded with the JADX
registration but does not contribute to the derived tool timeout; raise
`toolTimeoutMs` to cover a longer selected JADX deadline.
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
@@ -234,17 +254,58 @@ standalone CLI check.
## Codex and Devin
REA manages its launcher and selected provider settings in the client
registration. Codex's server and per-tool approval policies are user choices.
Setup patches REA's connection fields in the selected file and retains other
server settings, including `default_tools_approval_mode`, nested
`[mcp_servers.rea.tools.<name>]` tables, tool allow/deny lists, and unrecognized
client settings. Doctor accepts Codex's per-tool policy tables. Known malformed
policy values block setup before it writes the configuration or a backup, and
the diagnostic identifies the setting to repair. See the
[Codex configuration reference](https://developers.openai.com/codex/config-reference/)
for the supported policy values.
| Settings | Setup ownership |
| ----------------------------------------- | --------------------------------------------------------------------- |
| Launcher, transport and activation | Refreshes the REA stdio connection and enables it. |
| Explicit server environment | Preserves configured overrides and merges selected provider settings. |
| Codex positive timeouts and client policy | Retains caller choices; adds missing recommended REA timeouts. |
| Unrelated or future server settings | Retains their parsed values. |
Dry-run applies the same policy validation and writes no files. A successful
refresh keeps an exact recovery snapshot of the original configuration;
repeating setup with the same settings performs no write. Uninstall removes
the owned REA entry, including its policy, while retaining other servers and
the recovery snapshot. A green registration check establishes known
configuration alignment; it does not prove that a particular client or fork
successfully exposes every REA tool.
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
seconds yields 2820 seconds. That 30-second Codex figure is client guidance.
REA's own phase budgets are `REA_MCP_INITIALIZE_BUDGET_MS` (default 15000),
`REA_MCP_TOOLS_LIST_BUDGET_MS` (default 5000),
`REA_MCP_FIRST_CATALOG_BUDGET_MS` (default 20000), and
`REA_MCP_DOCTOR_DEADLINE_MS` (default 30000). Each is a positive decimal
integer of milliseconds from 1 to 2147483647. Unset keeps the default.
Invalid values fail configuration and name the setting. Setup copies a
caller-selected value into the registration, and a change is read from the
environment that starts REA, so it needs a restart. Doctor and packaged
startup probes name the effective milliseconds, whether they came from the
default or the setting, the default, the floor, and the maximum. REA does
not enforce Codex's 30 seconds as the doctor deadline. The 120000 ms
tool-operation headroom and the 64 KiB doctor stderr cap stay fixed. 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.
An explicit `REA_JADX_OPERATION_TIMEOUT_MS` is forwarded with the JADX
registration but does not contribute; raise `tool_timeout_sec` to cover a
longer selected JADX deadline.
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.
+15 -4
View File
@@ -17,8 +17,8 @@ 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)
Compact is the default for the server's advertised inputs; an explicit `full`
choice is preserved. 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
@@ -457,11 +457,22 @@ disposable HOME and CODEX_HOME under `~/.cache/rea-agent-evaluations/`. Set
`REA_AGENT_EVAL_ROOT` to choose another parent directory outside the OS temporary
directory: current Codex refuses helper aliases under `/tmp`, preventing shell
commands and skill reads even if MCP works. Each owned run directory is removed
after verification unless fixture retention is requested. The lane
after verification unless fixture retention is requested. Commands share the
owned-process supervisor: native ownership preparation has its own bounded
deadline before each short command budget, and timeouts join bounded process-tree
and output cleanup before returning. If cleanup remains incomplete, the lane retries its retained
owner and preserves the run directory (including disposable auth) rather than
removing files still in use; the error retains ownership and recovery details.
Command stderr is limited to 32 KiB and combined version output to 8 KiB;
exceeding these diagnostic budgets fails explicitly. Scenario JSON events stream
to the evaluator without retaining a second raw stdout copy. Cleanup attempts the
remaining run tree even if auth removal fails, and reports unresolved cleanup
together with the original verification failure. The lane
installs the packaged skill and MCP registration through public `rea setup`,
rather than copying a skill into a project that also inherits the caller's
skills and configuration. Only authentication is copied, when present, and it
is removed even with `REA_AGENT_EVAL_KEEP_FIXTURES=true`. No user configuration,
is removed after confirmed process cleanup even with
`REA_AGENT_EVAL_KEEP_FIXTURES=true`. No user configuration,
rules, plugins or history is copied; shell snapshots and additional agents are
disabled. Existing API-key environment authentication remains available.
The native missing-provider scenario preapproves only `open_binary` and
+124
View File
@@ -0,0 +1,124 @@
# Admission limits and recovery
How to add a REA-owned admission ceiling, budget, or deadline, and what a caller
does when one is hit. This is a design rule, not an inventory. Ceilings that
should become settings are tracked in
[#6176](https://github.com/morluto/rea/issues/6176). Naming honesty for limit
errors is [#5910](https://github.com/morluto/rea/issues/5910).
## Name a default, then a setting when operators need headroom
Put the number in a named constant with a unit. Do not leave it as a magic
number at the call site.
When a workload may need more room, add a setting whose unset value is that
constant. One module under
[`src/config/`](https://github.com/morluto/rea/blob/main/src/config) owns the
default, any floor, the maximum, the schema, the parser, and the recovery
text. Register that schema on `environmentSchema` in
[`environment.ts`](https://github.com/morluto/rea/blob/main/src/config/environment.ts).
`parseConfig` calls `parseEnvironment`. Doctor uses that same parser through
`nativeAnalysisConfigurationError`, so startup and `rea doctor` accept the
same values. Add the name to `SELECTED_REGISTRATION_SETTINGS` so `rea setup`
copies it into the client registration. Unset keeps today's behavior. An
invalid value is a `ConfigurationError` and projects as `configuration_invalid`.
Do not fall back to the default.
[`REA_REGEX_MATCHING_DEADLINE_MS`](https://github.com/morluto/rea/blob/main/src/config/regexMatchingDeadline.ts)
([#6267](https://github.com/morluto/rea/pull/6267)) is the reference. Default
5000 ms, floor 1 ms, maximum 2147483647 ms, because Node schedules a longer
`setTimeout` delay as 1 ms. Setup copies the setting. A hit says:
> The effective REA_REGEX_MATCHING_DEADLINE_MS value is 5000 ms (source default; default 5000 ms, floor 1 ms, maximum 2147483647 ms). Set REA_REGEX_MATCHING_DEADLINE_MS in the environment that starts REA to a positive decimal integer from 1 to 2147483647, then restart REA. The worker old-generation heap does not raise this deadline.
[`REA_MCP_MAX_RESPONSE_BYTES`](https://github.com/morluto/rea/blob/main/src/config/mcpResponseBudget.ts)
is the byte-budget shape: a decimal safe integer with a 10 MiB floor
(`MINIMUM_RESPONSE_BYTES`), the same shared schema, and no silent fallback. A
change needs a REA restart and a matching client receive buffer. It does not
raise REA's reserved string limit or Node's string ceiling. The setup
allowlist does not copy this setting. A new headroom setting should, following
the regex deadline.
A per-call budget is not an environment setting. JavaScript `max_heap_mb`
(CLI `--max-heap-mb`) is an integer from 128 to 16384 MiB, default 1024. It
applies on the next analysis, which starts a new worker, and does not restart
REA. It does not raise the parent result budget, transfer ceilings, or fixed
parser limits.
Host memory and `NODE_OPTIONS` do not raise a REA admission unless that
admission's recovery says so. The JavaScript parent result budget is one that
does: its recovery says to set `NODE_OPTIONS` (for example
`--max-old-space-size=<MiB>`) on the process that starts REA, then restart
that process. `max_heap_mb` does not raise it.
## Keep a fixed ceiling honest
If the ceiling must stay fixed, say so in the failure. Name the limit and the
effective value, say this release has no runtime override, and name what does
not raise it. Do not invent a setting.
JSON values stop at `MAX_JSON_DEPTH` (1000) in
[`jsonValue.ts`](https://github.com/morluto/rea/blob/main/src/domain/jsonValue.ts):
"JSON value exceeds maximum nesting depth of 1000. This release has no runtime
override."
## Recovery text
The string the caller sees includes:
- the limit or setting name
- the effective value and unit
- the source (`default` or `setting`)
- the default, the maximum, and the floor when the setting has one
- the next action, including where to set it ("in the environment that starts REA")
- whether a restart is required
- what the change does not raise
Invalid settings never take this path. They are `configuration_invalid`.
Doctor reports `runtime-configuration:<setting>` and says to correct it in the
shell or the MCP registration, then rerun.
## What agents do
Follow `remediation.action`. Change a request field or a `REA_*` setting only
when that text recommends the change. Restart only when it says the value is
read at startup. A sentence that a control does not raise the limit is not a
request to change it.
- Dropped MCP connection: retry the call once only when it is safe to repeat
or known not to have started. If it may already have changed the target, or
it fails again, tell the user and ask them to restart the MCP client. Do not
start with `rea doctor`
([#6353](https://github.com/morluto/rea/pull/6353)).
- REA could not start its connection: retry once. If it fails again, tell the
user and ask them to restart the MCP client, then run `rea doctor`. Do not
retry a startup that returns the same unchanged result: tell the user REA
could not start, ask them to restart the MCP client, then run `rea doctor`.
- Unexpected process-command failure: do not repeat the command. Tell the user
to check the input files, then run `rea doctor`.
- `cleanup_incomplete`: the operation is not retryable. Retry that exact
cleanup owner, named in `details.resources`, and wait until it confirms
release before admitting new work on those resources
([#6357](https://github.com/morluto/rea/pull/6357)).
- `resource_constraint` for capacity, when `retryable` is true: wait for the
active admission to finish, then retry within the reported limits. Do not
restart REA to free the slot.
- `configuration_invalid`: correct the named setting in the environment that
starts REA, then start REA again. Run `rea doctor` for the
`runtime-configuration:` check. Re-run `rea setup` for that client when the
setting is on the registration allowlist, then reconnect so the new process
sees it.
- `invalid_request`: correct the reported fields from the current schema or
CLI help. Do not repeat the unchanged call.
A restart is required for an environment setting and for a client still
running the previous registration. It is not the first step for a transient
MCP drop, a capacity wait, or retained cleanup.
## Tests
Lock the unset default, one accepted override, rejected floor, maximum, and
syntax, and the recovery sentence through `parseEnvironment` and `parseConfig`.
[`regexMatchingDeadline.test.ts`](https://github.com/morluto/rea/blob/main/src/config/regexMatchingDeadline.test.ts)
is the pattern. Project the failure and assert `configuration_invalid` for a
bad value and the recovery wording for a hit.
+151
View File
@@ -0,0 +1,151 @@
# Desktop installation directories
Use `inspect_desktop_application` when an installation directory is the target
and its runtime is unknown. It reads directory entries, selected headers and
bounded metadata, then reports runtime candidates, supporting files, component
path markers and concrete follow-up operations. No native provider or active
binary session is needed. It does not run the application, extract archives,
follow child symlinks, contact services or change target files.
## MCP
```json
{ "path": "/artifacts/MyApplication" }
```
Default `detail: "summary"` retains complete source Evidence in the session and
returns candidates with representative supporting paths, counts, coverage,
limitations and grouped next probes. `normalized_result.parent_evidence_id`
identifies the complete record. A server without Evidence retention refuses
summary delivery before reading the target; select `detail: "complete"` to
receive every retained fact inline.
Read exact files in one request:
```json
{
"source": {
"kind": "retained-evidence",
"evidence_id": "ev_<parent-sha256>"
},
"view": {
"kind": "desktop-application",
"collection": "files",
"paths": ["Game_Data/Managed/Assembly-CSharp.dll", "GameAssembly.dll"],
"fields": ["path", "read_status", "ranges", "facts", "issues"],
"offset": 0,
"limit": 16
}
}
```
Collections are `files`, `omissions`, `candidates`, `components` and `next_probes`.
Files and omissions accept exact `paths` and batched `file_ids` / `omission_ids`.
Files also accept `has_issues: true` to select probe diagnostics directly, or
`false` to select rows without reported issues. This filter uses the retained
issue facts; it does not re-read targets or infer that uninspected files are healthy.
When both are supplied, all filters apply; an empty selection returns no rows.
Omission records retain the observed path, kind and stable `omission_id`; their
payloads are not read, and other special-entry metadata remains unavailable.
`candidates` accepts exact `families`.
Omitted selectors match all retained rows; an empty selector matches none.
Every collection supports `fields`, `offset` and `limit`; its stable row ID is
always included. Selection changes delivery, not source inspection. Pages keep
source coverage and parent Evidence links. Page exhaustion does not establish
absence in a partial source. Complete Evidence remains accessible through
`get_evidence` and Evidence bundle export.
The summary's `probe_issues` counts files with reported issues and their retained
issue entries. It previews at most two such files, including stable `file_id`,
read status and the first issue. Path/reason previews use at most 256 UTF-16 units
each without splitting surrogate pairs; `text_truncated` marks shortened text,
and `preview_truncated` marks additional files. These are display prefixes;
full caller paths and all reasons remain in Evidence. Counts describe retained
observations, while source coverage still identifies partial inspection.
Use `inspect_analysis_view` with `collection: "files", has_issues: true` and
`fields: ["path", "read_status", "issues"]` for exact reasons, with the usual
paging and parent links. It intersects with exact paths and batched file IDs.
An ASAR inventory probe first selects its exact path with `open_binary`; then
call `inspect_artifact` on that active target. Directory inspection itself does
not select or replace the session target. The JavaScript application workflow
accepts its own selected path directly.
## CLI
The one-shot CLI returns complete Evidence by default. For compact output,
explicitly retain it in a bundle:
```bash
rea inspect-desktop-application /artifacts/MyApplication \
--detail summary --evidence_output /reports/application.evidence.json --json
```
This returns `summary` Evidence and an `evidence_bundle` publication receipt.
Existing output files are never overwritten. Summary delivery requires
`--evidence_output`, checked before analysis. `inspect-evidence-bundle` selects
saved records; an exact saved record can also feed `inspect-analysis-view`.
`--detail complete` returns source Evidence inline and can optionally write the
same bundle. Export writes only the caller-selected output file.
## Facts and uncertainty
| Family | Evidence used |
| ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| .NET | A PE optional header declares a nonempty CLI directory. |
| Unity Mono | A managed assembly below `*_Data/Managed` or `Contents/Resources/Data/Managed` has an exact UnityEngine assembly reference. |
| Unity IL2CPP | A recognized IL2CPP metadata magic (with the raw version when complete) coexists with a conventionally named native `GameAssembly.dll`, `GameAssembly.so`, `GameAssembly.dylib` or `libil2cpp.so` image. Build correspondence remains unknown. |
| Godot | A `GDPC` header at file start or a validated executable trailer offset. |
| GameMaker | A bounded `FORM` container starts with a `GEN8` chunk. The scan probes conventional `data.win`, `game.unx`, and `game.ios` data files for that header. |
| Unreal | `Content/Paks` path conventions or an IoStore TOC signature; arbitrary `.pak` filenames are insufficient. |
| Electron | A `resources/app.asar` or `resources/default_app.asar` path convention; archive validity is unverified. |
When the four-byte metadata magic is present but version bytes are incomplete,
the fact retains `raw_version: null`; it does not offer an IL2CPP metadata
inspection probe. A complete magic/version prefix with supported raw version
24, 27, 29, 31 or 39 offers an `inspect_il2cpp_metadata` follow-up at its
admitted file path, including when no native library was found. This probe does
not establish full metadata validity or correspondence to a native binary;
version 24 inspection still applies only to supported header layouts. See
[IL2CPP metadata](il2cpp-metadata.md).
Candidates are derived or inferred from these facts and do not prove loading or
execution. BepInEx, MelonLoader, Easy Anti-Cheat and BattlEye labels identify
path conventions only; runtime presence stays unknown. No bypass is provided.
`file_id` includes the admitted root namespace, recorded path and available filesystem identity. The caller-selected root path and admitted canonical path/root identity are both retained; `local_path` identifies each observed file for follow-up operations. The selected root may be a symlink; child symlinks are not followed. Exact
metadata integers remain decimal strings. Read ranges have offsets, byte counts
and SHA-256 digests; typed facts expose CLI RVAs, metadata/pack versions and
assembly tokens. Directory and symlink identities unavailable from traversal
remain null.
The directory's `snapshot_sha256` commits to retained paths, omission kinds,
available metadata and range digests, composed through the admitted root,
entry and omission identities. Caller ceilings, coverage counters, diagnostic
wording and interpretation facts remain in Evidence, outside source identity.
The same observations under different generous limits share a snapshot digest;
Evidence IDs still preserve request parameters and reported coverage. Different
retained entries or read ranges change the source snapshot.
Omission records share the entry and representation budgets and can be fetched
independently through their view.
**It is not a digest of all file contents.** These are sequential stable entry
observations, not an atomic directory snapshot. Unread bytes remain unknown.
## Resource bounds and reuse
Defaults are 20,000 examined entries and 8 MiB of total probe reads. Callers may
select up to 100,000 entries and 64 MiB. A separate 32 MiB conservative
representation budget bounds retained paths and facts before growth. Limits
and failed probes produce partial coverage. Traversal and reads are serial.
Each reader admits one inspection at a time; overlapping requests receive a
retryable capacity refusal without cancelling the admitted caller.
Unity reference decoding stops after a witness in each `Data/Managed` directory;
other assemblies' references remain unexamined, while their headers are still
checked. AssemblyRef facts decode names and tokens only; they do not decode or
hash unrelated key/culture/hash payloads or establish runtime binding. Pack contents, encrypted data, metadata/address correspondence and
section-embedded PCKs without a trailer are separate capabilities.
Filesystem results are not cached across calls. Retained views reuse validated
immutable metadata without rescanning files or retaining another inventory.
+126 -7
View File
@@ -2,6 +2,12 @@
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
@@ -27,6 +33,12 @@ encoded path separators, ASAR directories or links, and nonexistent paths are
rejected. The endpoint exposes every eligible target and its local path
metadata.
Cancellation stops further file authorization after an already-started filesystem
operation settles. ASAR descriptor-close failures remain typed incomplete cleanup
with an exact retryable owner; they are not treated as a missing or unauthorized
page. A later authorization and provider shutdown retry retained descriptors
before reporting their release.
## Workflow
`list_electron_targets` returns every eligible target served by the supplied endpoint in one inline array.
@@ -50,12 +62,27 @@ rea inspect-electron-page http://127.0.0.1:9223 TARGET_ID \
The normalized result contains canonical local paths, frame and DOM
structure, resource metadata, stable script/resource identities, explicit
completeness, and content-addressed script artifacts. Script metadata
retains its execution-context frame ID when CDP supplies one. The capture also
completeness, and content-addressed script artifacts. Resource sizes are unknown
when omitted or invalid; supplied invalid sizes also produce an explicit
completeness diagnostic.
DOM node indices retain their snapshot positions. Invalid rows are omitted with
completeness diagnostics, so `total_nodes` can exceed the retained node count
and a parent index can refer to an omitted source node.
Script metadata retains its execution-context frame ID when CDP supplies one and
the observed frame generation is authorized. Frames describe the current inventory;
script observations can retain earlier authorized frames that have since detached.
The capture also
inventories worker, service-worker, and shared-worker targets with
validated opener-target and parent-frame IDs. Worker discovery uses passive
target metadata; REA does not attach to or execute code in those targets.
Collection counts and aggregate script-source bytes are not capped. Like every
Script-source collection retains at most 32 MiB of UTF-8 text across scripts
and shares a 20-second collection deadline. Omitted sources retain their script
metadata and an explicit reason; later smaller sources may still fit after an
oversized source is omitted. The separate 64 MiB CDP response limit bounds one
wire message before retention admission, rather than total transient memory.
The source budget adds no script-count cap. Like every
target, frame, script, and resource, a worker URL must resolve to a local
file before it is retained. Relationship IDs improve
attribution but do not prove which static module started a worker or that its
@@ -92,7 +119,9 @@ The CLI and MCP surfaces accept the same schema. For a JSON request file:
rea capture-electron-scenario scenario.json --json
```
The result records action status and targets, correlated app/window/WebContents,
The result records one status and target per declared action; actions after a
failed or cancelled action are not run and are recorded as `cancelled` with a
not-executed error. It also records correlated app/window/WebContents,
preload, session, navigation, shell, permission, popup, download, protocol,
native-addon, process, and IPC timeline events. IPC channels and argument-shape
metadata retain their observed lengths; payload values are never retained.
@@ -101,8 +130,18 @@ scenario controls; their attempted and observed outcomes remain in the
timeline. The active hook blocks and records external shell/navigation,
permission, download, popup, updater, and OS-integration effects. The timeline
is explicitly partial when attachment starts after application activity. The
startup has a 60-second deadline. Actions wait until completion or request
cancellation; a click whose target never appears remains pending until cancelled.
startup, including the initial state snapshot, has a 60-second deadline.
Actions wait until completion or request cancellation; a click whose target
never appears, or a `wait` without `window_index` in an app that opens no
window, remains pending until cancelled. After cancellation, the final state
read has 5 seconds before REA keeps the initial snapshot instead; the
cancellation keeps the caller's reason and records that expiry in its
diagnostics.
Blocked synchronous shell APIs keep their return contracts:
`showItemInFolder` returns `undefined` and `writeShortcutLink` returns `false`.
Their `shell-attempt` events record the blocked phase and argument shapes;
neither result means the requested host operation occurred. The existing
`openExternal` and `openPath` blocks continue to reject their promises.
CLI SIGINT/SIGTERM requests cancellation and awaits owned-process cleanup.
Window IDs, URLs, and cached titles come from one main-process Electron snapshot,
so a crashed renderer does not require a successful renderer-side title query.
@@ -116,6 +155,80 @@ 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.
Main-process IPC recording keeps original-function removal for `on` and `once`
registrations and records a fired `once` callback once. Native Node.js owns
`once` registration, firing and callable dispatch as well as removal order and
traversal, including listeners registered by removal observers;
the hook translates only its own recording wrappers at the removal boundary.
Wrapper metadata uses weak keys and does not retain removed registrations.
Wrapper metadata operations, private collection methods and traversal constructors are captured
before target startup; later replacement of their global bindings does not
change listener registration or removal.
Native runtimes differ in whether `once` consults callback `.call` or `.apply`.
The hook observes actual callback entry without imposing another runtime's
property reads or argument-list representation. Application methods selected by
native dispatch keep their original receiver and argument-list object; opaque
methods produce a `custom-call` or `custom-apply` observation gap when callback
execution cannot be established.
If native wrapper creation or registration-method lookup could expose private
instrumentation through an application getter, setter or Proxy, registration
stays native and unwrapped. The `once-metadata` observation gap identifies that
unobserved registration attempt; it does not prove registration or delivery. The original callback,
metadata writes and native method receivers are preserved, including when
registration fails. Later registrations resume normal observation. Non-callable
`call`/`apply` properties produce failed observation gaps when native dispatch reads
them; callback entry is not fabricated.
An `on` listener's application-defined `.apply` receives the original argument-list
object unchanged. Borrowing the exposed method preserves its dynamic receiver.
Because that method owns argument interpretation and may not
invoke the callback, the hook records a bounded `ipc-observation-gap` timeline
entry with the registered channel rather than an IPC invocation. Its empty
`argument_shapes` means unobserved arguments, not a zero-argument callback;
callback execution and result remain unknown. Coverage and retention include
these gaps even when their individual records are dropped. Normal native
dispatch continues to record actual invocation frames. Evidence snapshots copy
retained arrays and counters without invoking application iterators or Array
species.
Listener-chain recognition distinguishes owned, unowned and unknown metadata.
Data links may be inherited; absence is established only after completing the
bounded prototype lookup. Exhausting either walk, encountering a getter or seeing a Proxy leaves
ownership unknown. The hook preserves native registration without adding another
recording wrapper and retains a `listener-chain` observation gap after successful
registration. This gap does not prove IPC delivery. An existing inner recording
wrapper can still report its actual invocation; otherwise callback activity
remains unobserved. Setter-only properties have a known undefined read.
Data-descriptor parsing is shared with removal normalization.
This does not establish complete JavaScript function introspection equivalence.
Caller-defined `.listener` properties can change the identity exposed by
`listeners()` and meta-events; accessors and Proxy metadata are not followed for
wrapper recognition. Re-registering a raw recording wrapper on another channel
retains the original registration's channel attribution.
Mutating `.listener` on a raw recording wrapper can also prevent removal through
application decorators that bypass the hook's removal methods. When a removal
request reaches the hook's methods and its registration order is known, private
callback ownership preserves removal by the original function and the newest
matching registration when public metadata is changed or deleted. Native removal
still determines getter execution, exceptions and removal meta-events.
If registration or removal methods differ from the Node methods captured at hook
initialization, the hook leaves listener callbacks unwrapped and records a
`listener-registration` observation gap. The application keeps its original
callback and receiver; callback execution remains unobserved. This comparison
assumes Node's EventEmitter prototype has not itself been globally decorated
before hook initialization. The provider loads the hook before the application
and excludes `NODE_OPTIONS` from the child environment.
An opaque later listener or unsupported emitter state can prevent proof of
removal order. The hook then delegates removal
to the native method and records a `listener-removal` gap rather than claiming
which registration was removed. These gaps are distinct from dropped records
and do not establish IPC delivery or successful removal.
### 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.
@@ -135,4 +248,10 @@ application path and capture outcome to the static graph, while frames, scripts,
workers, and execution claims remain unavailable until a separate passive runtime
capture provides them.
ASAR observation reads the header through one open file handle, validates its declared lengths against the archive size, and limits encoded JSON headers to 16 MiB before parsing. The header cache also retains at most 16 MiB of encoded headers across eight archives. Oversized headers produce a scope refusal.
ASAR observation reads the header through one open file handle and validates its
declared lengths against the archive size. Header/index admission is the same
budget used by static ASAR reads: 16 MiB / 16777216 bytes by default, or
`REA_ASAR_MAX_HEADER_BYTES` (at most 256 MiB) when set. An oversized header is
not parsed; selecting a member path does not bypass admission, and the page
stays unauthorized. The header cache separately retains at most that many bytes
of already-admitted encoded headers across eight archives. Evicting that cache does not raise admission.
+70
View File
@@ -0,0 +1,70 @@
# ELF note tables
`inspect_elf_notes` / `inspect-elf-notes` answers whether one selected local
ELF32/ELF64 image declares `PT_NOTE` program headers or `SHT_NOTE` sections,
which note owners and types those tables store, and which GNU build-id or
ABI-tag fields those notes carry. It reads the file without executing it and
does not require a disassembler, debug package, or package catalog.
```bash
rea inspect-elf-notes /absolute/path/to/program --json
```
The CLI resolves relative paths against the caller's working directory. MCP
requires an absolute filesystem path and preserves that exact selection:
```json
{
"name": "inspect_elf_notes",
"arguments": { "path": "/artifacts/program" }
}
```
## Observations
The inline Evidence result contains the artifact path, SHA-256, and byte length.
`tables` lists each declared note program header or section, including the
section name when a string table is present. Zlib-compressed section-name
tables are decompressed within the selected file-size budget. An unsupported
compression type leaves section names unknown and adds a limitation; note bytes
remain independently examined. Each `entries[]` item reports owner, type,
descriptor SHA-256, and a hex preview for descriptors of 64 bytes or less.
For GNU notes, `gnu.build_id_hex`, `gnu.abi_tag`, or `gnu.gold_version` are
filled when the descriptor matches that type. Other owners keep `gnu` null.
`PT_NOTE` and `SHT_NOTE` are examined independently. The same file range may
appear twice when both tables declare it.
A missing table list is observed absence of those declarations. It is not proof
the image never carried notes in another container or overlay.
## Limits and failures
The selected file is limited to 64 MiB by default (512 MiB maximum). At most
32 note entries are examined by default across every table. Retained note
names, section names, and GNU descriptor text share a scan budget based on
the source bytes plus any decoded section-name table, capped at
`max_file_bytes`. The raw source and decoded table must also fit together
within that ceiling. If admission fails, the error reports the required
`max_file_bytes`; raising it can admit the decoded table up to the operation's
512 MiB maximum. Once admitted, raising that setting does not increase the
scan budget for the same source and decoded names. Repeated table ranges count again. Each descriptor range is hashed once,
and each GNU version's trailing NUL run is scanned once; neither walk may
exceed that byte ceiling. Malformed ELF headers, truncated header
tables, declared note tables that extend past the file, and truncated
note entries fail without partial success. ELF extended
numbering (`PN_XNUM`, including a section count stored when `e_shnum` is 0) is
unsupported and returns an unsupported-target outcome rather than invalid-input
recovery.
`SHF_COMPRESSED` note sections return `unsupported_target` before their payload
is interpreted or decompressed. Their compression header is storage metadata,
not a note header. The current result requires physical file offsets for each
note and descriptor, which decoded compressed notes cannot provide. This refusal
does not label compressed note storage as malformed or allocate its declared
decoded size. Compressed section-name tables remain supported within the limits
above.
This workflow does not execute the image or verify GNU build-id uniqueness
against package catalogs.
+67
View File
@@ -0,0 +1,67 @@
# Erlang BEAM files
`inspect_erlang_beam` / `inspect-erlang-beam` answers whether one selected
local file is a `FOR1`/`BEAM` IFF container, which chunks that form stores, and
which atom, export, import, lambda, and Code-header fields those chunks carry.
It reads the file without executing bytecode and does not require an Erlang
installation or BEAM emulator.
```bash
rea inspect-erlang-beam /absolute/path/to/app.beam --json
```
The CLI resolves relative paths against the caller's working directory. MCP
requires an absolute filesystem path and preserves that exact selection:
```json
{
"name": "inspect_erlang_beam",
"arguments": { "path": "/artifacts/app.beam" }
}
```
## Observations
The inline Evidence result contains the artifact path, SHA-256, and byte length.
`chunks` lists every IFF chunk name, file range, and payload SHA-256.
Repeated names retain separate chunk occurrences. Decoded tables and the Code
header use the last occurrence of each name, matching OTP's loader; the chunk
inventory still exposes earlier payloads and their source ranges.
The BEAM form size bounds the parsed chunks. Bytes after the declared form are
ignored by the parser but remain part of the selected artifact's byte length
and SHA-256.
When a selected `AtU8` or `Atom` table is decoded, `atoms` is the 1-based name table and
`module` is atom 1. Both the pre-OTP 28 table and the OTP 28+ compact table
(negated count, compact-encoded name lengths) are read. `exports`, `imports`, and `lambdas` resolve atom indices to
those names when the table is present; unresolved indices keep `function` or
`module` null.
`code` reports the Code chunk instruction-set, opcode maximum, label count,
function count, and the instruction stream's file range. Missing optional
tables are null, not empty-everywhere.
A zero-byte `AtU8` falls back to an available legacy `Atom` table, following
[OTP 20.3's atom selection](https://github.com/erlang/otp/blob/a113f6117fd696ea6f84ed754055b4ec97a7ccb2/erts/emulator/beam/beam_load.c).
A nonempty `AtU8` retains UTF-8 precedence, including an explicit zero atom
count; malformed nonempty data fails instead of falling back. An empty atom
chunk without an alternate table still fails its required count header.
Absent and zero-byte optional `FunT` chunks produce `lambdas: null`, while a
four-byte zero row count produces `lambdas: []`. This follows
[OTP 29's positive-size lambda parsing rule](https://github.com/erlang/otp/blob/550d7b7898706c7822362c42e7b93120c1d1f29a/erts/emulator/beam/beam_file.c).
Empty payloads retain their ranges and SHA-256 in the chunk inventory. These
rules apply to the last occurrence of each chunk name; an empty final occurrence
does not revive an earlier payload of that name. Nonempty truncated tables remain
format failures.
## Limits and failures
The selected file is limited to 64 MiB by default (512 MiB maximum). At most
64 chunks and 4096 atoms are examined by default, and at most 65536 export,
import, and lambda rows together. Malformed `FOR1`/`BEAM` headers, truncated
chunks, and `AtU8` names that are not valid UTF-8 fail without partial success.
This workflow does not execute bytecode, inflate `LitT` literals, or decode
`Attr`/`CInf` external-term payloads. Complete coverage describes this static
chunk and table report; it does not establish that an Erlang runtime can load
or execute the module.
+587 -17
View File
@@ -28,6 +28,33 @@
"required": ["action"],
"additionalProperties": false
},
"recovery": {
"oneOf": [
{
"type": "object",
"properties": {
"kind": { "type": "string", "const": "inspect_input_contract" },
"operation": { "type": "string", "minLength": 1 }
},
"required": ["kind", "operation"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"kind": { "type": "string", "const": "inspect_timeout_state" },
"operation": { "type": "string", "minLength": 1 },
"timeout_ms": {
"type": "integer",
"exclusiveMinimum": 0,
"maximum": 9007199254740991
}
},
"required": ["kind", "operation", "timeout_ms"],
"additionalProperties": false
}
]
},
"details": {
"type": "object",
"propertyNames": { "type": "string" },
@@ -64,6 +91,33 @@
"required": ["action"],
"additionalProperties": false
},
"recovery": {
"oneOf": [
{
"type": "object",
"properties": {
"kind": { "type": "string", "const": "inspect_input_contract" },
"operation": { "type": "string", "minLength": 1 }
},
"required": ["kind", "operation"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"kind": { "type": "string", "const": "inspect_timeout_state" },
"operation": { "type": "string", "minLength": 1 },
"timeout_ms": {
"type": "integer",
"exclusiveMinimum": 0,
"maximum": 9007199254740991
}
},
"required": ["kind", "operation", "timeout_ms"],
"additionalProperties": false
}
]
},
"details": {
"type": "object",
"propertyNames": { "type": "string" },
@@ -100,6 +154,33 @@
"required": ["action"],
"additionalProperties": false
},
"recovery": {
"oneOf": [
{
"type": "object",
"properties": {
"kind": { "type": "string", "const": "inspect_input_contract" },
"operation": { "type": "string", "minLength": 1 }
},
"required": ["kind", "operation"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"kind": { "type": "string", "const": "inspect_timeout_state" },
"operation": { "type": "string", "minLength": 1 },
"timeout_ms": {
"type": "integer",
"exclusiveMinimum": 0,
"maximum": 9007199254740991
}
},
"required": ["kind", "operation", "timeout_ms"],
"additionalProperties": false
}
]
},
"details": {
"type": "object",
"propertyNames": { "type": "string" },
@@ -136,6 +217,33 @@
"required": ["action"],
"additionalProperties": false
},
"recovery": {
"oneOf": [
{
"type": "object",
"properties": {
"kind": { "type": "string", "const": "inspect_input_contract" },
"operation": { "type": "string", "minLength": 1 }
},
"required": ["kind", "operation"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"kind": { "type": "string", "const": "inspect_timeout_state" },
"operation": { "type": "string", "minLength": 1 },
"timeout_ms": {
"type": "integer",
"exclusiveMinimum": 0,
"maximum": 9007199254740991
}
},
"required": ["kind", "operation", "timeout_ms"],
"additionalProperties": false
}
]
},
"details": {
"type": "object",
"propertyNames": { "type": "string" },
@@ -172,6 +280,33 @@
"required": ["action"],
"additionalProperties": false
},
"recovery": {
"oneOf": [
{
"type": "object",
"properties": {
"kind": { "type": "string", "const": "inspect_input_contract" },
"operation": { "type": "string", "minLength": 1 }
},
"required": ["kind", "operation"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"kind": { "type": "string", "const": "inspect_timeout_state" },
"operation": { "type": "string", "minLength": 1 },
"timeout_ms": {
"type": "integer",
"exclusiveMinimum": 0,
"maximum": 9007199254740991
}
},
"required": ["kind", "operation", "timeout_ms"],
"additionalProperties": false
}
]
},
"details": {
"type": "object",
"propertyNames": { "type": "string" },
@@ -208,6 +343,33 @@
"required": ["action"],
"additionalProperties": false
},
"recovery": {
"oneOf": [
{
"type": "object",
"properties": {
"kind": { "type": "string", "const": "inspect_input_contract" },
"operation": { "type": "string", "minLength": 1 }
},
"required": ["kind", "operation"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"kind": { "type": "string", "const": "inspect_timeout_state" },
"operation": { "type": "string", "minLength": 1 },
"timeout_ms": {
"type": "integer",
"exclusiveMinimum": 0,
"maximum": 9007199254740991
}
},
"required": ["kind", "operation", "timeout_ms"],
"additionalProperties": false
}
]
},
"details": {
"type": "object",
"propertyNames": { "type": "string" },
@@ -244,6 +406,33 @@
"required": ["action"],
"additionalProperties": false
},
"recovery": {
"oneOf": [
{
"type": "object",
"properties": {
"kind": { "type": "string", "const": "inspect_input_contract" },
"operation": { "type": "string", "minLength": 1 }
},
"required": ["kind", "operation"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"kind": { "type": "string", "const": "inspect_timeout_state" },
"operation": { "type": "string", "minLength": 1 },
"timeout_ms": {
"type": "integer",
"exclusiveMinimum": 0,
"maximum": 9007199254740991
}
},
"required": ["kind", "operation", "timeout_ms"],
"additionalProperties": false
}
]
},
"details": {
"type": "object",
"propertyNames": { "type": "string" },
@@ -280,6 +469,33 @@
"required": ["action"],
"additionalProperties": false
},
"recovery": {
"oneOf": [
{
"type": "object",
"properties": {
"kind": { "type": "string", "const": "inspect_input_contract" },
"operation": { "type": "string", "minLength": 1 }
},
"required": ["kind", "operation"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"kind": { "type": "string", "const": "inspect_timeout_state" },
"operation": { "type": "string", "minLength": 1 },
"timeout_ms": {
"type": "integer",
"exclusiveMinimum": 0,
"maximum": 9007199254740991
}
},
"required": ["kind", "operation", "timeout_ms"],
"additionalProperties": false
}
]
},
"details": {
"type": "object",
"propertyNames": { "type": "string" },
@@ -316,6 +532,33 @@
"required": ["action"],
"additionalProperties": false
},
"recovery": {
"oneOf": [
{
"type": "object",
"properties": {
"kind": { "type": "string", "const": "inspect_input_contract" },
"operation": { "type": "string", "minLength": 1 }
},
"required": ["kind", "operation"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"kind": { "type": "string", "const": "inspect_timeout_state" },
"operation": { "type": "string", "minLength": 1 },
"timeout_ms": {
"type": "integer",
"exclusiveMinimum": 0,
"maximum": 9007199254740991
}
},
"required": ["kind", "operation", "timeout_ms"],
"additionalProperties": false
}
]
},
"details": {
"type": "object",
"propertyNames": { "type": "string" },
@@ -352,6 +595,33 @@
"required": ["action"],
"additionalProperties": false
},
"recovery": {
"oneOf": [
{
"type": "object",
"properties": {
"kind": { "type": "string", "const": "inspect_input_contract" },
"operation": { "type": "string", "minLength": 1 }
},
"required": ["kind", "operation"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"kind": { "type": "string", "const": "inspect_timeout_state" },
"operation": { "type": "string", "minLength": 1 },
"timeout_ms": {
"type": "integer",
"exclusiveMinimum": 0,
"maximum": 9007199254740991
}
},
"required": ["kind", "operation", "timeout_ms"],
"additionalProperties": false
}
]
},
"details": {
"type": "object",
"propertyNames": { "type": "string" },
@@ -388,25 +658,82 @@
"required": ["action"],
"additionalProperties": false
},
"recovery": {
"oneOf": [
{
"type": "object",
"properties": {
"kind": { "type": "string", "const": "inspect_input_contract" },
"operation": { "type": "string", "minLength": 1 }
},
"required": ["kind", "operation"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"kind": { "type": "string", "const": "inspect_timeout_state" },
"operation": { "type": "string", "minLength": 1 },
"timeout_ms": {
"type": "integer",
"exclusiveMinimum": 0,
"maximum": 9007199254740991
}
},
"required": ["kind", "operation", "timeout_ms"],
"additionalProperties": false
}
]
},
"details": {
"type": "object",
"properties": {
"logical_path": { "type": "string" },
"declared_sha256": {
"anyOf": [{ "type": "string" }, { "type": "null" }]
"anyOf": [
{
"type": "object",
"properties": {
"logical_path": { "type": "string" },
"declared_sha256": {
"anyOf": [{ "type": "string" }, { "type": "null" }]
},
"calculated_sha256": {
"anyOf": [{ "type": "string" }, { "type": "null" }]
},
"unpacked": { "type": "boolean" }
},
"required": [
"logical_path",
"declared_sha256",
"calculated_sha256",
"unpacked"
],
"additionalProperties": false
},
"calculated_sha256": {
"anyOf": [{ "type": "string" }, { "type": "null" }]
},
"unpacked": { "type": "boolean" }
},
"required": [
"logical_path",
"declared_sha256",
"calculated_sha256",
"unpacked"
],
"additionalProperties": false
{
"type": "object",
"properties": {
"logical_path": { "type": "string" },
"algorithm": { "type": "string", "const": "md5" },
"declared_md5": {
"type": "string",
"pattern": "^[a-f0-9]{32}$"
},
"calculated_md5": {
"anyOf": [
{ "type": "string", "pattern": "^[a-f0-9]{32}$" },
{ "type": "null" }
]
},
"unpacked": { "type": "boolean" }
},
"required": [
"logical_path",
"algorithm",
"declared_md5",
"calculated_md5",
"unpacked"
],
"additionalProperties": false
}
]
}
},
"required": [
@@ -446,6 +773,33 @@
"required": ["action"],
"additionalProperties": false
},
"recovery": {
"oneOf": [
{
"type": "object",
"properties": {
"kind": { "type": "string", "const": "inspect_input_contract" },
"operation": { "type": "string", "minLength": 1 }
},
"required": ["kind", "operation"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"kind": { "type": "string", "const": "inspect_timeout_state" },
"operation": { "type": "string", "minLength": 1 },
"timeout_ms": {
"type": "integer",
"exclusiveMinimum": 0,
"maximum": 9007199254740991
}
},
"required": ["kind", "operation", "timeout_ms"],
"additionalProperties": false
}
]
},
"details": {
"type": "object",
"propertyNames": { "type": "string" },
@@ -482,6 +836,33 @@
"required": ["action"],
"additionalProperties": false
},
"recovery": {
"oneOf": [
{
"type": "object",
"properties": {
"kind": { "type": "string", "const": "inspect_input_contract" },
"operation": { "type": "string", "minLength": 1 }
},
"required": ["kind", "operation"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"kind": { "type": "string", "const": "inspect_timeout_state" },
"operation": { "type": "string", "minLength": 1 },
"timeout_ms": {
"type": "integer",
"exclusiveMinimum": 0,
"maximum": 9007199254740991
}
},
"required": ["kind", "operation", "timeout_ms"],
"additionalProperties": false
}
]
},
"details": {
"type": "object",
"propertyNames": { "type": "string" },
@@ -518,6 +899,33 @@
"required": ["action"],
"additionalProperties": false
},
"recovery": {
"oneOf": [
{
"type": "object",
"properties": {
"kind": { "type": "string", "const": "inspect_input_contract" },
"operation": { "type": "string", "minLength": 1 }
},
"required": ["kind", "operation"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"kind": { "type": "string", "const": "inspect_timeout_state" },
"operation": { "type": "string", "minLength": 1 },
"timeout_ms": {
"type": "integer",
"exclusiveMinimum": 0,
"maximum": 9007199254740991
}
},
"required": ["kind", "operation", "timeout_ms"],
"additionalProperties": false
}
]
},
"details": {
"type": "object",
"propertyNames": { "type": "string" },
@@ -554,6 +962,33 @@
"required": ["action"],
"additionalProperties": false
},
"recovery": {
"oneOf": [
{
"type": "object",
"properties": {
"kind": { "type": "string", "const": "inspect_input_contract" },
"operation": { "type": "string", "minLength": 1 }
},
"required": ["kind", "operation"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"kind": { "type": "string", "const": "inspect_timeout_state" },
"operation": { "type": "string", "minLength": 1 },
"timeout_ms": {
"type": "integer",
"exclusiveMinimum": 0,
"maximum": 9007199254740991
}
},
"required": ["kind", "operation", "timeout_ms"],
"additionalProperties": false
}
]
},
"details": {
"type": "object",
"propertyNames": { "type": "string" },
@@ -590,6 +1025,33 @@
"required": ["action"],
"additionalProperties": false
},
"recovery": {
"oneOf": [
{
"type": "object",
"properties": {
"kind": { "type": "string", "const": "inspect_input_contract" },
"operation": { "type": "string", "minLength": 1 }
},
"required": ["kind", "operation"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"kind": { "type": "string", "const": "inspect_timeout_state" },
"operation": { "type": "string", "minLength": 1 },
"timeout_ms": {
"type": "integer",
"exclusiveMinimum": 0,
"maximum": 9007199254740991
}
},
"required": ["kind", "operation", "timeout_ms"],
"additionalProperties": false
}
]
},
"details": {
"type": "object",
"propertyNames": { "type": "string" },
@@ -626,6 +1088,33 @@
"required": ["action"],
"additionalProperties": false
},
"recovery": {
"oneOf": [
{
"type": "object",
"properties": {
"kind": { "type": "string", "const": "inspect_input_contract" },
"operation": { "type": "string", "minLength": 1 }
},
"required": ["kind", "operation"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"kind": { "type": "string", "const": "inspect_timeout_state" },
"operation": { "type": "string", "minLength": 1 },
"timeout_ms": {
"type": "integer",
"exclusiveMinimum": 0,
"maximum": 9007199254740991
}
},
"required": ["kind", "operation", "timeout_ms"],
"additionalProperties": false
}
]
},
"details": {
"type": "object",
"propertyNames": { "type": "string" },
@@ -662,6 +1151,33 @@
"required": ["action"],
"additionalProperties": false
},
"recovery": {
"oneOf": [
{
"type": "object",
"properties": {
"kind": { "type": "string", "const": "inspect_input_contract" },
"operation": { "type": "string", "minLength": 1 }
},
"required": ["kind", "operation"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"kind": { "type": "string", "const": "inspect_timeout_state" },
"operation": { "type": "string", "minLength": 1 },
"timeout_ms": {
"type": "integer",
"exclusiveMinimum": 0,
"maximum": 9007199254740991
}
},
"required": ["kind", "operation", "timeout_ms"],
"additionalProperties": false
}
]
},
"details": {
"type": "object",
"propertyNames": { "type": "string" },
@@ -698,6 +1214,33 @@
"required": ["action"],
"additionalProperties": false
},
"recovery": {
"oneOf": [
{
"type": "object",
"properties": {
"kind": { "type": "string", "const": "inspect_input_contract" },
"operation": { "type": "string", "minLength": 1 }
},
"required": ["kind", "operation"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"kind": { "type": "string", "const": "inspect_timeout_state" },
"operation": { "type": "string", "minLength": 1 },
"timeout_ms": {
"type": "integer",
"exclusiveMinimum": 0,
"maximum": 9007199254740991
}
},
"required": ["kind", "operation", "timeout_ms"],
"additionalProperties": false
}
]
},
"details": {
"type": "object",
"propertyNames": { "type": "string" },
@@ -734,6 +1277,33 @@
"required": ["action"],
"additionalProperties": false
},
"recovery": {
"oneOf": [
{
"type": "object",
"properties": {
"kind": { "type": "string", "const": "inspect_input_contract" },
"operation": { "type": "string", "minLength": 1 }
},
"required": ["kind", "operation"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"kind": { "type": "string", "const": "inspect_timeout_state" },
"operation": { "type": "string", "minLength": 1 },
"timeout_ms": {
"type": "integer",
"exclusiveMinimum": 0,
"maximum": 9007199254740991
}
},
"required": ["kind", "operation", "timeout_ms"],
"additionalProperties": false
}
]
},
"details": {
"type": "object",
"propertyNames": { "type": "string" },
+13 -4
View File
@@ -19,8 +19,10 @@ rea inspect-evm-interface ./runtime.bin raw --json
Initial real verification covers flat legacy bytecode on Linux x64 and unchanged
bundled EVMole 0.9.3. EOF-style EF00 containers return `unsupported_target`,
with the selected path and format reason. Provider/configuration failures retain
their distinct unavailable-provider or capability classification.
with the selected path and format reason. Selected-path resolution errors
`ENOENT`, `ENOTDIR`, `ELOOP`, and `ENAMETOOLONG` are `invalid_request` path
issues. Permission, resource, cancellation, provider, and cleanup failures retain
their distinct classifications.
The host must already provide util-linux `prlimit`, normally `/usr/bin/prlimit`;
`REA_EVM_PRLIMIT_COMMAND` can select another absolute executable. REA installs
no system tools and changes no user configuration. [Upstream provenance and
@@ -43,7 +45,8 @@ license](https://github.com/morluto/rea/blob/main/third_party/evmole/README.md)
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
Nested containers have partial scalar coverage; map keys are still checked for
duplicates throughout nested maps and arrays. 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.
@@ -106,7 +109,13 @@ CLI keeps its existing complete result.
Inputs are limited to 4 MiB, complete replies to 16 MiB and combined diagnostic
output to 1 MiB. An owned worker has a 256 MiB JavaScript heap, 3 GiB virtual
address-space limit, 30 CPU seconds and a 30-second command deadline. Virtual
address-space limit and 30 CPU seconds. The worker command deadline is
`timeout_ms` (default 30000 ms, integer 1 through 2147483647) on the next call,
or `--timeout-ms` on `rea inspect-evm-interface`. Omission keeps 30000 ms.
Those byte, address-space, and CPU ceilings have no runtime override, and
`timeout_ms` does not raise them. An oversized selected carrier returns a
non-retryable `resource_constraint` with resource `file-size` and its fixed
input allowance in `reported_limits.maximum_input_bytes`. Virtual
address space differs from resident memory. The worker retains inherited tighter
soft/hard limits, reports its configured soft limits inline and leaves hard limits
unchanged. WASM trap reservations are disabled
+49 -2
View File
@@ -50,6 +50,27 @@ git submodule update --init third_party/binwalk third_party/unblob
Normal REA builds and npm packages do not include or build these sources or
engine binaries. See [upstream provenance](https://github.com/morluto/rea/blob/main/third_party/README.md).
### Windows submodule checkout
On Windows, the Unblob checkout stops with `error: invalid path`. Several
upstream test fixture names under `tests/integration/` contain characters,
trailing dots, or trailing spaces that NTFS does not allow; REA does not use
those fixtures. Keep the clone, exclude that directory, and retry from the
repository root in PowerShell:
```powershell
git -C third_party/unblob config core.protectNTFS false
git -C third_party/unblob sparse-checkout set --no-cone '/*' '!/tests/integration/'
git submodule update third_party/unblob
```
Both settings are required. While `core.protectNTFS` is enabled, Git for
Windows also rejects index paths that the sparse pattern excludes; the pattern
keeps the invalid names out of the worktree. The setting applies only to the
Unblob submodule. In Git Bash, prefix the `sparse-checkout` command with
`MSYS_NO_PATHCONV=1`; otherwise `!/tests/integration/` is rewritten as a
Windows path.
## CLI and MCP
```sh
@@ -153,13 +174,39 @@ tree semantics, vulnerability scanning, emulation or hardware runtime capture.
## Resource and lifecycle policy
- Nonempty regular firmware inputs: at most **128 MiB**, copied and hashed into
a private workspace before analysis. No in-place source modification.
a private workspace before analysis. Growth beyond that allowance during
copying receives the same typed input refusal as an already oversized file.
Supply a stable copy within the allowance. No in-place source modification.
- One queued heavy operation per provider instance; Binwalk one thread and
Unblob one worker. No automatic parallel extraction engines.
- **120-second** deadline; inherited **1 GiB per-process address-space** limit,
- Analysis defaults to **120 seconds**, from tool resolution and input snapshot
through inspection and its final private-workspace cleanup, or extraction
through report consumption and inventory, excluding queue waiting and retained
cleanup admission. Inspection has no publication phase. Select `analysis_timeout_ms` in either MCP operation or
`--analysis-timeout-ms` in either CLI command for slower analysis. Current
results retain the actual budget; an absent historical value means unknown.
Extraction publication and its final
cleanup start a separate **120-second** budget; select
`publication_timeout_ms` in MCP or `--publication-timeout-ms` in CLI for a slow
destination. Valid budgets are integers from 1 through 2,147,483,647 ms (the
timer representation limit). Publication timeouts identify their phase and
verified file progress; verified writes before atomic commit are not a claim
that output remains published after rollback. Caller cancellation applies to
both phases and final cleanup. Phase handoff, publication commit and final
completion also check monotonic elapsed time, so delayed timer callbacks
cannot revive an expired budget.
- Analysis timeout recovery names the selected budget and the analysis control,
not the later publication control. Keep the client call timeout above analysis,
publication and cleanup headroom; keep the input/range/question unchanged.
Resolve any reported incomplete cleanup before retrying. Raising wall time
does not raise fixed CPU, memory, report, diagnostic or input limits.
- Inherited **1 GiB per-process address-space** limit,
120 CPU seconds and zero core-dump allowance through `prlimit`.
- JSON reports: at most **8 MiB** and valid UTF-8; malformed encoding and
oversized reports fail without repairing or truncating producer data.
Exit status 0 is not an empty result: a missing, empty, malformed or
oversized report fails with `unreadable_output`, its `report_state`, the
command, exit status and captured output. REA does not retry automatically.
Diagnostic output is bounded with exact captured byte counts.
- Default staging budget: **256 MiB** and **10,000 entries**, configurable using
`max_output_bytes` / `max_output_files` (CLI `--max-output-bytes` /
+16 -2
View File
@@ -23,7 +23,9 @@ rea inspect-dart-aot /targets/Example.apk --abi armeabi-v7a
The equivalent MCP methods are `identify_flutter_build` (with `path`) and
`inspect_dart_aot` (with `path` and an optional `abi`; the default prefers
arm64-v8a, then x86_64, then x86, then armeabi-v7a, then the remaining
ABIs alphabetically).
ABIs alphabetically). An explicit `abi` is the directory label exactly as
`identify_flutter_build` reports it, so a payload under a nonstandard label
stays selectable and keeps that label in the result.
## Optional retained AOT delivery
@@ -106,7 +108,12 @@ server; older servers keep the complete request after schema discovery.
`build_id_candidates` preserves distinct IDs. `build_id` is populated only
when all declared note ranges were examined and one ID was found;
conflicting IDs or malformed/bounded note metadata make coverage partial.
Nonempty GNU build-ID descriptors up to 64 bytes are retained as lowercase
hexadecimal, including IDs shorter than four bytes.
Matching bytes outside declared note ranges cannot supply a build ID.
Compressed `SHT_NOTE` sections are skipped with an unsupported-compression
limitation and partial coverage; IDs from other note ranges remain candidates.
Encoded note storage is never interpreted as note headers or inflated.
- Labeled strings must fit their complete grammar. `dart_version_capped`
and `toolchain_capped` independently report labels omitted by grammar or
projection bounds; either makes identification coverage partial. Values
@@ -150,9 +157,16 @@ server; older servers keep the complete request after schema discovery.
- **Selected ABI.** AOT inspection applies the library budget to the selected
ABI after selection; an oversized unselected library does not block it.
Undefined dynamic-symbol imports are not treated as snapshot exports.
A retained section directory without `SHT_DYNSYM` uses the same bounded
`PT_DYNAMIC` symbol description as a section-stripped image. Malformed
present symbol-table sections remain failures.
Section-stripped images require a bounded symbol count from `DT_HASH` or
`DT_GNU_HASH`; layouts without either are refused. Symbol names and their
terminators must fit their linked string table.
terminators must fit their linked string table. Only dynamic entries used for
symbol discovery are interpreted as numeric bounds. ABI-ignored payloads,
including [`DT_BIND_NOW`](https://gabi.xinuos.com/elf/08-dynamic.html#dynamic-section),
do not change discovery; their stored bytes still contribute to the selected
artifact's digest.
- REA performs **no hash-to-SDK lookup**: mapping the snapshot hash to a
Dart SDK release requires an external dataset and is deliberately out of
scope for this family.
+19 -1
View File
@@ -6,8 +6,26 @@ 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.
`instrument-with-frida --json` returns the Evidence record directly, matching MCP. Consumers of the earlier `{evidence, cleanup_error}` CLI wrapper should read the record at the JSON root and the session cleanup observation at `normalized_result.cleanup_error`. If final provider cleanup fails or cancellation arrives during it, the typed error retains completed Evidence in `details.partial_observation`. Discovery commands retain their device or process report in the same field.
For state and capture-coverage checks, pass `{"session_id":"SESSION_ID","detail":"summary"}` to `frida_session_status`. Each call reads the observed session state and retains the complete observation. The reply is an `inspect_analysis_view` Evidence summary with the session identity, state, scripts, `messages_retained`, and `messages_truncated`; message payloads remain in its `parent_evidence_id`. The count covers retained queued messages, not every target event. Omitted `detail` or `detail=complete` preserves the existing complete reply.
A summary's selected fields look like this (excerpt):
```json
{
"summary": {
"state": "running",
"messages_retained": 50,
"messages_truncated": false
}
}
```
Use `get_evidence_bundle` with `detail="summary"` and `filters={"evidence_id":"PARENT_ID"}` to discover parent metadata without message payloads. Filters apply only to summary delivery. Use `export_evidence_bundle` for the complete canonical bundle, then select the record whose `evidence_id` matches `normalized_result.parent_evidence_id` from the status summary. To use the existing CLI `inspect-analysis-view`, extract that record into a standalone Evidence JSON file and select it as an Evidence-file source with `view={"kind":"summary"}`. Reading retained Evidence does not refresh a live session or verify a process after exit or PID reuse.
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. 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.
Frida scripts execute inside the selected target with that process's memory, filesystem, and network authority. Local device lookup waits 1000 ms. This release has no runtime override for that lookup; another REA timeout does not raise it. Remote endpoints do not use that lookup. Message capture is bounded to 16 MiB and reports truncation. Metadata, type, and text field caps, and the JSON node cap, are also fixed. Evidence names a cap when that observation actually dropped data. This release has no runtime override for those capture caps. Script source is not stored in Evidence; its selected path and SHA-256 digest are. For a file, `source_sha256` identifies the original selected bytes. Invalid UTF-8 continues to use replacement decoding; Evidence then records that limitation and `raw_result.source_representation` with the encoding, decoding mode, and `loaded_source_sha256` of the UTF-8 string passed to Frida. Valid UTF-8 files and inline scripts do not need this additional representation fact. 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. If cancellation is observed when the native call returns, partial Evidence retains the source identity and captured messages even if that call rejects, including messages emitted during awaited cleanup. The caller's cancellation reason remains primary and a native rejection is retained as a sanitized diagnostic. A failure already observed before cleanup stays primary if cancellation arrives during cleanup. The cancellation cutoff is REA's observation of native completion, rather than a claim about unobservable native event order. `script_id` is REA's load-attempt identity. When creation rejects without returning a handle, `script_cleanup: "no_handle"` means REA acquired no handle to unload; it does not prove that the engine performed no work. 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.
+20 -6
View File
@@ -23,13 +23,19 @@ not name its entry function `entry`. MCP uses `open_binary`, `list_procedures`,
`procedure_info`, `read_function_instructions`, and `analyze_function` with the
same admitted target and provider semantics.
The classifier validates MZ page counts, initialized module length, header and
The DOS MZ parser validates page counts, initialized module length, header and
relocation-table bounds, relocation destinations, and the initialized entry
point. It distinguishes DOS load-module and relocation bytes from the Windows
new-header field. PE remains PE; NE, LE, and LX declarations are explicitly
unsupported. A damaged declared Windows header does not silently become DOS.
Inventory classification returns `unknown` when its bounded prefix cannot
establish the format; this is distinct from the complete target admission check.
point using the available structural bytes and actual file length. It reports
those DOS facts separately from Windows-header evidence. A recognized PE
signature remains PE; recognized NE, LE, and LX headers are explicitly
unsupported. A nonzero Windows-header pointer without a recognized signature
remains an unresolved candidate, even when the DOS structure validates:
automatic target selection fails closed and reports both observations.
REA does not infer DOS intent from padding or infer a damaged Windows header
from the pointer alone. Inventory classification is a bounded-prefix format
hint; it returns `unknown` for that candidate or when its prefix cannot
establish the format. That result is distinct from complete DOS validation and
does not decide target admission.
Ghidra imports a private target snapshot with `MzLoader`, language
`x86:LE:16:Real Mode`, and compiler specification `default`. These settings,
@@ -124,6 +130,10 @@ FileBytes digests, every source mapping and its memory digest, relocation record
module partitions, relocation fixups and linear coordinates rather than relying
on the imported executable SHA alone.
Expected DOS relocation records retain each fixup's pre-application bytes and
applied value. Their memory-byte windows come from the final relocated image,
including changes made by later overlapping fixups.
The result is `verified`, `mismatch`, or `unsupported`. Every check retains its
expected and observed values. A memory-digest mismatch identifies the source
range's file offset and analysis address; it does not locate the first differing
@@ -134,6 +144,10 @@ their contents and DOS runtime semantics are not verified. Independent verificat
also covers explicit [raw-binary imports](raw-binary.md); other formats return
`unsupported` with measured observations.
MZ verification retains the initialized image described by the header's page
count and streams appended overlay bytes into the complete original and modified
source digests. Overlay size does not increase the retained image allocation.
`read_bytes` returns initialized provider memory, including loader fixups; an
unmapped or uninitialized byte ends the read with an explicit returned length and
`complete: false`. `address_to_file_offset` uses the provider's source mapping;
+6
View File
@@ -69,6 +69,12 @@ 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
+26 -1
View File
@@ -65,6 +65,14 @@ PDB, or change analyzer settings.
## Loaded addresses and file offsets
Ghidra computes overview procedure and string counts from the same inventories
used by `list_procedures` and `list_strings`, without transporting those rows
solely to count them. Counts include external functions and only Ghidra-defined
strings; they do not establish discovery of unidentified functions or undefined
string bytes. Complete inventories remain available through the list tools.
Overview reuse follows the target/profile snapshot lifetime and is invalidated
by session metadata edits.
`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
@@ -139,6 +147,19 @@ 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.
Configured analysis extensions and seeds use owned file reads during profile
resolution and launch snapshots. A changed read extent or descriptor state
refuses the commitment. Failed source or snapshot descriptor closes remain
owned and block new admission until cleanup succeeds; the physical failure
and exact cancellation reason remain available. Provider shutdown joins all
profile stages before retrying their retained descriptors.
| 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. |
@@ -193,7 +214,11 @@ 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.
`-- --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.
+92 -12
View File
@@ -19,6 +19,56 @@ requires an absolute filesystem path and preserves that exact selection:
}
```
For build identity and counts, select `detail: "summary"`. MCP retains the same
complete Evidence and returns `normalized_result.parent_evidence_id`. Read
selected dependency versions without reopening the file:
```json
{
"name": "inspect_analysis_view",
"arguments": {
"source": {
"kind": "retained-evidence",
"evidence_id": "ev_<parent digest>"
},
"view": {
"kind": "go",
"collection": "dependencies",
"paths": ["golang.org/x/crypto"],
"fields": ["path", "version", "replacement"],
"offset": 0,
"limit": 10
}
}
}
```
Go views support `dependencies` (exact `paths`) and `settings` (exact `keys`),
optional original `indices`, field selection, and pagination. Filters intersect;
empty filter arrays select no rows and `fields: []` returns only row identity.
Each row keeps its original `index`, scoped to the parent Evidence and collection;
duplicate module paths and setting keys keep distinct indices. Page offsets
count matching records. Exhausting retained rows does not establish complete
runtime dependencies. Original bytes, unknown lines and all complete metadata
remain in the parent and can be exported as an Evidence bundle.
One-shot CLI summaries save complete Evidence before returning the projection:
```bash
rea inspect-go-binary ./application --detail summary --evidence_output ./go-evidence.json --json
```
`--evidence_output` is required for CLI summary delivery and never overwrites an
existing file. The default CLI/MCP reply remains complete.
[Measured workflows](https://github.com/morluto/rea/blob/42e1a17d6e6c0487f17be0bae248553c4caba7f7/experiments/analysis-efficiency/go-build-views.md) include
catalog repetition and extra calls. Identity/count questions benefit from a
single summary. In the tested Codex full-catalog configuration, a compound
question about identity and one dependency uses fewer tokens in captured request JSON with
one complete inspection than with a summary followed by a lookup. The complete
reply remains useful for that initial question; retained views support later
selected reads. Token figures are serialization estimates, not model usage.
## Observations
The inline Evidence result contains the artifact path, SHA-256, byte length,
@@ -39,8 +89,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,19 +100,43 @@ 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
inline or historical pointer build-info encoding. Source ranges refer to the
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.
The built-in TypeScript reader supports ELF, PE and thin Mach-O images, and one
selected slice of a universal Mach-O, with Go's inline or historical pointer
build-info encoding. Source ranges refer to the original stable file snapshot,
including pointer-backed strings. Legacy pointer-backed fields are decoded at
the width declared by Go's build-info record, independently of the image's ELF
class. It does not scan arbitrary byte strings as compiler metadata.
A universal (FAT) Mach-O needs `architecture` (`x86`, `x86_64`, `arm`
or `arm64`; CLI `--architecture`). REA reads its own FAT table, without `lipo`,
and validates the selected slice's range, alignment and Mach-O header CPU
against the table. The result keeps the universal file as `artifact` (its
SHA-256 and size), adds `selected_slice` with the slice offset, length, raw CPU
type/subtype and the FAT table digest, and reports every source range as a
file offset inside that slice. Without `architecture`, or when the file has no
such slice or several, the request fails on `architecture` and lists the slices
present. `architecture` on any other image is rejected rather than ignored.
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.
remain required. When ELF section names are unreadable, limitations report that
coverage gap; the fallback search policy is unchanged.
A named ELF or Mach-O build-info section selects the record's discovery range.
For inline encoding, the following version and module strings may continue past
that section into its file-backed load mapping; their bytes are still checked
against the mapping and the existing aggregate string limit.
Historical moduleless Go binaries can store their empty module-string header in
declared zero-initialized virtual memory rather than file-backed bytes. The reader
@@ -84,11 +158,17 @@ 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.
An input above 256 MiB returns `resource_constraint` with resource `file-size`
and `reported_limits.maximum_input_bytes`. Select a suitable input within that
fixed allowance; changing an output-write or transport budget does not admit it.
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. Identical search spans with the same virtual alignment are
searched once; partially overlapping spans consume the cumulative search budget
each, aggregate embedded string contents to 1 MiB (varint length prefixes are
framing and do not count toward this allowance), structural table decoding to
16 MiB and the complete normalized metadata report to 8 MiB. Inline strings must
also fit within their actual mapped source range. 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
+113
View File
@@ -0,0 +1,113 @@
# Godot PCK archives
`inspect-artifact` and `extract-artifact` read ordinary Godot PCK formats 2, 3
and 4 without running the game or requiring Godot. Official Godot 4.4.1, 4.6.1
and 4.7.1 produced these respective formats in verification; other engine
releases have not been verified. Format 1 is unsupported.
Inspection retains the original header and directory facts, including engine
version, flags, declared file count, source paths, offsets and declared MD5.
Observed plaintext bytes receive content-addressed SHA-256 identities. MD5 is a
container integrity claim, separate from the observed SHA-256. A mismatch fails
by default; `--integrity-policy record-and-continue` retains the observed bytes
and an explicit untrusted contradiction. An all-zero MD5 is an unknown claim,
not a verified digest.
## CLI
Start with a compact summary and keep the complete Evidence bundle:
```sh
rea inspect-artifact /artifacts/game.pck --detail summary \
--evidence_output /evidence/game.json --json
rea extract-artifact /artifacts/game.pck --json
```
Summary delivery requires `--evidence_output` and never overwrites that file.
The response contains `summary` Evidence and an `evidence_bundle` receipt.
Without `--detail summary`, inspection keeps its complete inline delivery.
Extraction creates a new temporary directory and returns its path. It rejects
unsafe paths and verifies container identity before creating output.
The same CLI commands locate a PCK at the beginning of a file or through its
12-byte EOF trailer in an executable carrier. They do not scan arbitrary
payloads or discover packs behind additional trailing signatures. The root
content SHA-256 covers the whole carrier; its content classification remains
native, while the inventory records the PCK role and exact pack bounds.
An EOF marker is only a candidate: an invalid size or a pointed-to location
without a `GDPC` header leaves native classification in place. Once the
trailer points to a pack header, malformed or unsupported PCK content remains
an error. This recognition boundary follows the Godot loader at pinned source
revision [06d1e7a](https://github.com/godotengine/godot/blob/06d1e7a34ccde4029eed53d018cfde77dfe771d5/core/io/file_access_pack.cpp#L2099-L2146).
Ordinary native `open_binary` detection remains unchanged.
## MCP and selected facts
For a standalone pack, call `open_binary` with its absolute path, then
`inspect_artifact` with `{"detail":"summary"}`. The complete inspection and its
nested inventory stay in the session. Use the summary's
`normalized_result.parent_evidence_id` as the source of subsequent views:
```json
{
"name": "inspect_analysis_view",
"arguments": {
"source": {
"kind": "retained-evidence",
"evidence_id": "<parent_evidence_id>"
},
"view": {
"kind": "inventory",
"collection": "occurrences",
"logical_paths": ["scripts/main.gd", "project.binary"],
"fields": [
"logical_path",
"declared_integrity",
"hash_status",
"format_metadata"
],
"offset": 0,
"limit": 32
}
}
}
```
Occurrences retain `occurrence_id`, `artifact_id` and `parent_occurrence_id`
even when `fields` is empty. Batch occurrence IDs with `occurrence_ids`, or
join returned content IDs to `collection: "nodes"`, `artifact_ids: [...]` and
`fields: ["sha256", "size"]`. Filters intersect; an omitted filter selects all
available rows and an empty array selects none. Pagination follows the filtered
canonical order. Multiple occurrences may share one content ID.
Views reuse authenticated immutable inventory facts and lazy lookup indexes;
they do not reopen the source. These are historical observations, including
after the source changes or disappears. Close the target with `close_binary`
when finished. To read an exported bundle through the CLI, select its complete
inspection record as `source: {kind: "inline", evidence: ...}` in a JSON request
file for `rea inspect-analysis-view request.json --json`.
## Coverage and limits
PCK summaries expose root format metadata, available graph counts,
`available_hash_status_counts`, and `source_coverage`. Inventory pages preserve
producer coverage alongside page coverage. `source_complete` means all declared
retained graph rows are available; it does not establish that an encrypted or
otherwise unknown directory was examined. Missing matches in unknown coverage
are not evidence of absence.
Encrypted directories retain their header and declared file count with unknown
entry coverage. Individually encrypted members retain directory metadata with
unknown content identity; REA does not decrypt them. Sparse bundles likewise
retain header facts with unknown member coverage. Extraction refuses an
unreadable directory or encrypted member. This capability does not recover
keys, decode Godot resources, decompile GDScript bytecode, analyze signals,
apply patch/removal entries or execute targets.
Readable logical member bytes, including repeated physical ranges, share the ZIP
archive expansion policy: at most the larger of 256 MiB or 500 times the PCK's
encoded byte span. Embedded carrier bytes do not enlarge that budget. The full
directory is checked before any member is yielded, hashed or extracted. Encrypted
members are not read and do not consume this plaintext-work allowance. Small
bounded aliases remain valid. This is a processing/output resource bound, separate
from the directory metadata retention budget and format validity.
+224
View File
@@ -0,0 +1,224 @@
# Static CUDA and HIP code objects
`inspect_gpu_code_objects` answers which GPU targets and kernel declarations a
local artifact carries. It reads ELF/PE sections, standalone CUDA fatbins,
CUBIN/PTX and Clang HIP bundles, including CCOB v1/v2/v3. It neither executes the
artifact nor invokes CUDA/ROCm tools. GPU execution, SASS disassembly and host
launch correlation remain outside coverage.
Start with a target inventory:
```json
{ "path": "/artifacts/library.so", "view": { "collection": "targets" } }
```
Target rows aggregate inventoried payloads and distinguish an unrequested kernel
analysis from a complete or partial one. `objects` pages expose exact object
IDs, compression declarations and encoded/header locations. Fields, exact
filters, batch IDs and stable pagination select the facts delivered to the
caller. IDs are positional within the recorded artifact SHA-256. Selecting `object_ids`
in a new inspection requires `expected_sha256` from that inventory; a changed
file is rejected before parsing selected payloads.
`target` is the inventory declaration used for selection; `payload_target` is
the independently observed inner declaration when available. Object pages keep
both values, their kind, `target_consistency` and inspection coverage even with
selected fields.
Conflicting base architectures retain both values, mark object/report/target
coverage partial and annotate the affected kernels. CUBIN flags establish a base
architecture: `base-only` explicitly leaves feature-profile agreement unknown,
including `a` and `f` variants. A standalone declaration is `single-source`, and
unexamined payloads remain `unexamined`. An exact AMDGPU target is normalized at
its note owner for comparison with a Clang bundle declaration; raw values remain
intact. Unresolved feature comparisons stay `unknown`.
A HIP target ID identifies a toolchain target, not its payload representation.
Recognized raw or wrapped LLVM bitcode stays `unsupported` for kernel metadata
inspection, with the bundle ID, range and selected payload digest retained.
Bitcode validity and inner targets remain unexamined; malformed recognized ELF
still reports `malformed`. Standalone bitcode is outside GPU container coverage.
HIP bundle and metadata note recognition accept both the legacy `amdgcn` triple
and LLVM's `amdgpu` subarchitecture triples, including declared generic processors
such as `gfx11-generic` and `gfx9-4-generic`. Notes with the empty or explicit
`unknown` environment use the same processor and feature comparison while
retaining their raw triple spelling. CUDA's legacy fatbinary compression flag is
retained as an unsupported codec with its encoded and decoded extents;
compressed bytes are not treated as PTX text. CCOB is recognized by the exact
four signature bytes `CCOB`; high-bit-corrupted bytes are not decoded as ASCII
and accepted as a bundle. CCOB v1 has no stored total size: the completed zlib
or Zstd stream determines its encoded extent. Embedded `CCOB` text is ordinary
compressed data; zero-byte alignment after the stream belongs to neither
payload. If decoding fails or its codec is unsupported, a later `CCOB` marker
is only an uncertain recovery candidate. The affected row retains that
limitation and discovery remains partial.
The declared decoded length bounds decompression. An understated length can
stop decoding before codec completion, so any subsequent recovery extent stays
unknown; the decoder does not expand its allowance to prove the bad declaration.
Interrupted observations omit unfinished v1 rows. A terminal capacity row
qualifies its retained range as the containing tail and does not infer siblings.
`cuda_target_header` retains the OS ABI, normalized raw `e_flags` and exact
header location. CUDA OS ABI `0x33` and the observed `0x41` flags layout use
different architecture fields; they are decoded at one ABI-aware owner.
Unrecognized layouts keep the header and an unknown target instead of
inventing an SM label. Header provenance is validated against its CUBIN object.
Target filters select inventory declarations. An unselected payload's inner
target remains unexamined; an empty filtered result does not establish that
no enclosed code declares that target. Discovering every inner declaration
requires decoding those payloads.
HIP bundle IDs retain the declared processor and v2-v3 `+feature` suffixes for
exact selection. If a recognized AMDGPU bundle ID does not expose a target, the same
bounded note-discovery owner used for standalone AMDGPU files establishes it
before filtering. Those directory/note reads share the inspection work budget;
successive queries can select the target returned by inventory.
Decode selected targets and return a kernel page in the same call:
```json
{
"path": "/artifacts/library.so",
"analysis": "kernels",
"targets": ["sm_100"],
"view": {
"collection": "kernels",
"names": ["kernel_name"],
"fields": [
"name",
"parameter_count",
"parameter_bytes",
"parameters",
"registers",
"shared_bytes"
]
}
}
```
Only selected payloads are decoded. Other inventory entries remain present with
`not-selected` states, and overall payload coverage remains partial. An empty
kernel page proves no matching retained fact; it does not establish that an
unexamined payload contains no kernel. Missing fields remain null.
Kernel representations have different meanings:
- PTX reports parameter declarations and virtual registers. Shared bytes cover
supported static declarations inside that entry; they do not establish module
shared memory, dynamic allocation or physical device resources.
PTX 1.0–1.3 declares entry parameters in the body; PTX 1.4 and later uses the
entry parameter list. Entry-scoped `.pragma` statements before the body do
not end the declaration or suppress its body resources. A `.target
compute_xx` spelling is retained as written;
NVIDIA PTX defines it as a synonym for `.target sm_xx` when comparing its
target identity with the fatbin declaration. `.callprototype` formal
parameters are excluded from entry virtual-register counts. Parameter names
remain available when pointer qualifiers are understood or an unsupported
qualifier prevents layout decoding. A pointer's `.align` describes the
pointed-to memory and does not change parameter layout alignment. Spaced,
joined and partly joined pointer qualifiers have the same meaning. Parameter
alignment before or after the scalar type retains its layout and name.
Parameter names and following declarations are retained when an array
extent uses a parenthesized expression or when dimensions are
multidimensional, even though those forms do not currently yield a layout.
PTX target selection uses the highest-numbered module-scope `.target`
directive, including directives after entry declarations; unsupported or
conflicting target profiles leave the effective target unknown.
Scalar register declarations count each bare or percent-prefixed name and
each parameterized range, including compact keywords such as `.reg.b32`.
Unsupported declaration forms or overflowing totals
retain an unknown count and partial decoding coverage. Unsupported parameter
layouts and unknown shared-memory declarations likewise retain their known
facts, explicit limitations and partial coverage through retained pages.
- CUBIN identifies CUDA entry symbols and reads numeric `nv.info` records,
including V2 kernel-parameter sizes, and native section declarations. Global
register declarations also cover compiler
versions that leave the older text-section register field empty.
Conflicting register declarations keep the physical count unknown, preserve the
kernel's other facts, and mark object/report/target decoding coverage partial.
- AMDGPU reads v3+ MessagePack notes. V3 processor identity comes from the
`EF_AMDGPU_MACH` field in ELF `e_flags`; known processor values are selectable
even when the note omits `amdhsa.target`. This inventory reports the processor
only; it does not interpret version-specific feature flags. Unknown processor values remain unknown. A known
processor conflict between ELF flags and note metadata leaves the processor
unresolved. Argument counts include hidden ABI arguments
when present; SGPR/VGPR counts and fixed group/private segment sizes retain
their declared meanings. Legacy YAML metadata remains unsupported.
Each selected PTX payload becomes a retained UTF-8 text artifact with a SHA-256,
byte length and source coordinates. It is omitted from compact replies. Read it
with `inspect_analysis_view` and the parent Evidence ID returned by the compact
receipt:
```json
{
"source": { "kind": "retained-evidence", "evidence_id": "ev_..." },
"view": {
"kind": "gpu-code-objects",
"collection": "ptx",
"object_id": "...",
"offset": 0,
"limit": 4096
}
}
```
Text offsets use UTF-16 code units and must fall on Unicode scalar boundaries.
Complete Evidence retains the selected text; `export_evidence_bundle` provides
its portable source record. Historical pages never reopen the input file.
A location with `container_id: null` uses original file offsets. Other locations
use that inventoried object's decoded byte coordinates. Follow its parent,
encoded location and digest to trace decompressed metadata back to a native
section and the original file. Decoded offsets are never presented as physical
file offsets. Reads from an ELF or PE GPU section stay within that declared
section extent, so neighboring native section bytes cannot complete a
truncated embedded bundle header.
The CLI uses the same service and contracts:
```sh
rea inspect-gpu-code-objects /artifacts/library.so --analysis kernels \
--targets '["sm_100"]' --detail summary \
--view '{"collection":"kernels","names":["kernel_name"]}' \
--evidence_output /analysis/library-gpu.evidence.json --json
```
A full-file SHA-256 always streams every byte. Inventory then reads bounded
native directories and bundle headers; CCOB requires decompression to discover
its inner directory. Kernel analysis reads selected payloads. `bytes_read`
includes hashing and subsequent descriptor reads, so this is not a header-only
hashing workflow.
CUDA raw LZ4 blocks and Zstd, and CCOB zlib/Zstd are supported. Unknown versions
or codecs retain declarations with explicit limitations. Lengths are checked
before decoding, and CCOB's truncated MD5 is verified against decoded bytes.
Resource limits are one active inspection, 128 MiB per input/output buffer,
512 MiB of payload input, decoded output and AMD note-read work per inspection,
and 16 MiB retained facts including selected PTX. Payload work is reserved before
reading or allocating; a capacity refusal does not reread an aliased payload.
Native tables and decoded section names each have a 16 MiB structural budget;
AMDGPU ELF note discovery admits at most 8 MiB of source note bytes, with
bounded depth and container/string allocations. Standalone AMD targets are read
from those notes before target selection; selected kernel analysis reuses the
same parsed declarations. Pages have a
1 MiB response budget. Resource refusals and interrupted observations preserve
known facts and explicit uncertainty.
CUDA ELF parsing additionally shares a 262,144 record/symbol visit limit,
128 MiB of cumulative metadata section reads, and a 16 MiB serialized-size
accounting budget for temporary index facts. These limits are checked before
index growth; they are not claims about JavaScript heap size. Equivalent scoped
metadata byte ranges and repeated equal register declarations are coalesced;
conflicting values remain unknown. Parameter sections are parsed once per ELF.
Missing CUBIN symbol tables or unresolved section names produce `partial`
object, report and target coverage even when some kernel facts survive. AMD note
failures preserve earlier successfully parsed declarations and the original
failure reason. Standalone PTX recognition tries a small header first, then
continues within the existing 16 MiB text budget when leading comments or
whitespace obscure its directives; admitted full-file probe bytes are reused.
After those comments and whitespace, `.version` must be the first PTX module
token; later PTX-looking text does not identify an arbitrary binary as PTX.
+9 -2
View File
@@ -36,8 +36,9 @@ The MCP tool `project_harmony_application_graph` takes the same
- exact component paths, artifact IDs, and hashes (manifests, Ark bytecode,
resources, native libraries, JavaScript, signing)
- a packaging-model hint: Stage is claimed only from a `module.json` entry,
FA only from `config.json`, otherwise `unknown`
- a packaging-model hint: Stage is claimed only from a `module.json` entry
outside a classified resource path, FA only from `config.json` outside a
classified resource path, otherwise `unknown`
- FA resources under `assets/<module>/resources/` as well as Stage `resources/`
- unavailable component paths with unknown IDs and hashes, including encrypted entries; these make projection coverage partial
- App Pack child package paths without recursive inventory
@@ -45,6 +46,12 @@ The MCP tool `project_harmony_application_graph` takes the same
- path-based N-API bridge hypotheses between bytecode and `libs/<abi>` native
libraries
File and slice occurrences supply component and child-package paths; directory
trees and symlinks retain their identities in the source inventory without
being classified as code files. Ark and JavaScript runtime hints summarize the
classified bytecode and JavaScript lists; supported source extensions share the
same classification rule as their component rows.
Every claim is path-presence only. Manifest semantics (module identity,
abilities, entry points, permissions, dependencies), Ark bytecode contents and
versions, resource resolution, signing verification, and observed native
+13 -2
View File
@@ -8,6 +8,8 @@ Choose one lifecycle mode in an upstream MCP JSON registration. Set `REA_IDA_MCP
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.
Startup failures identify the accepted REA analysis operation. Missing-profile and typed startup transport failures retain the original stage in `details.diagnostics.startup_operation`; existing diagnostics, timeout budgets and resource limits remain available. Startup remains shared within a session; each caller receives its own operation attribution. Cancellation retains live cleanup ownership, so a successful retry releases the originating cancellation outcome as well.
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 |
@@ -39,7 +41,16 @@ rea function /absolute/path/to/program main --json
rea decompile /absolute/path/to/program main --json
```
Open the **original input binary** in REA, not an `.idb` or `.i64` database. Its SHA-256 must match IDA's recorded input hash. REA checks the attached identity and image base around each analysis request. A mismatch or target switch fails with recovery guidance. The original input must remain readable by REA; WSL can use its mounted alias while the GUI runs on Windows.
If the registration cannot be read or validated, provider diagnostics retain its
selected `registration_path` and the configuration owner's setting/constraint
facts in `configuration_details.settings`, plus `configuration_remediation`.
`configured` means a path was selected, not that it is valid or an IDA connection
was established; failed registrations remain unavailable. Correct
`REA_IDA_MCP_CONFIG` in the next CLI invocation's environment or the MCP server's
launch environment. Restart/reconnect that server after changing its environment;
an export in another shell does not update a running server.
Open the **original input binary** in REA, not an `.idb` or `.i64` database. Its SHA-256 must match IDA's recorded input hash. REA checks the attached identity and image base around each analysis request. A mismatch or target switch fails with recovery guidance. The original input must remain readable by REA; WSL can use its mounted alias while the GUI runs on Windows. Attached profiles, including native mode, refuse universal Mach-O inputs at admission: every slice shares the recorded whole-file hash, so REA cannot verify which slice the GUI loaded. Open a thin file extracted with `lipo -thin` in both IDA and REA, or use headless mode, which analyzes the selected slice.
REA's close operation releases its MCP connection or proxy. It never saves or closes the existing GUI database. External edits remain visible because results are live and are not replayed from snapshots. These checks do not lock the GUI or prove that the database is unpatched.
@@ -131,7 +142,7 @@ 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. 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.
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 JSON named by `REA_IDA_MCP_CONFIG` has an optional `timeoutMs` integer from 1 to 2147483647. Omitted means 300000. REA applies an edited value on the next IDA connection after it re-reads that file; an already open session keeps the timeout it connected with. Changing the `REA_IDA_MCP_CONFIG` path itself is read at process start. An upstream SDK request that reaches this limit returns `provider_timeout` naming the effective milliseconds and `timeoutMs`. A shorter inner request is the time remaining inside that ceiling, and the error names both when they differ. 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. 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.
+432
View File
@@ -0,0 +1,432 @@
# IL2CPP metadata
`inspect_il2cpp_metadata` / `inspect-il2cpp-metadata` decodes one selected local
Unity IL2CPP `global-metadata.dat` file and returns its assembly images, type
definitions, methods and named members with their owners, plus managed string
literals, by stable index, namespace, range and count,
with the header sections and serialized index widths. It reads the file without
running it and requires no Unity installation, IL2CPP binary, active target,
Hopper, Ghidra or IDA.
Metadata versions 24, 27, 29, 31 and 39 are supported. For example, Unity 2019.4.41f2
wrote version 24, 2020.3.49f1 version 27, 2022.3.30f1
wrote version 29, 2022.3.62f3 wrote 31 and 6000.4.2f1 wrote 39; other Unity
releases that write these numbers have not been checked. Other versions are
rejected as unsupported. Version 24 decoding covers the 264-byte header layout
used by the cited Unity 2019.4 build; earlier 24.x layouts are not inferred from
the version number and are refused when their header or records differ. The path
must name a regular file; directories and symbolic links are rejected.
With `binary_path`, the tool also reads the matching native binary and links
each method to its code address. See [Native method addresses](#native-method-addresses).
## MCP
MCP defaults to a compact summary and retains the complete decoded Evidence in
the session. MCP requires an absolute filesystem path:
```json
{
"name": "inspect_il2cpp_metadata",
"arguments": {
"path": "/artifacts/Data/Managed/Metadata/global-metadata.dat"
}
}
```
The summary retains the complete decoded Evidence in the session and returns a
linked `inspect_analysis_view` overview: artifact identity, metadata version,
index widths and `counts` (`sections`, `images`, `types`, `methods`,
`nested_types`, `fields`, `properties`, `events`, `parameters` and
`string_literals`) without rows. Use `normalized_result.parent_evidence_id` to page
the original rows without reopening or decoding the file:
```json
{
"source": {
"kind": "retained-evidence",
"evidence_id": "<parent_evidence_id>"
},
"view": {
"kind": "il2cpp-metadata",
"collection": "types",
"offset": 0,
"limit": 64
}
}
```
Collections are `sections`, `images`, `types`, `methods`, `fields`,
`properties`, `events`, `parameters`, `string_literals`, `method_addresses`,
`return_types`, `field_types`, `parameter_types` and `code_gen_modules`. Every returned row includes
its source `index`, scoped by artifact SHA-256 and collection. All collections
accept batch `indices` and `fields`; collections whose rows have a `name` also
accept exact `names`. Types also accept `image_indices`, `namespaces` and
`declaring_types`; methods, fields, properties and events accept
`declaring_types`. Parameters accept exact `method_indices`, so a batch of
method indices retrieves their parameters without reading the entire parameter
table. `parameter_types` accepts the same `method_indices`, resolved through the
retained `parameters` owners; Evidence without `parameters` refuses that filter. Filters intersect, duplicate indices are returned once, and
rows retain metadata order. An empty filter selects no rows; `fields: []`
returns indices only. Omitted fields return the complete selected rows.
For example, after resolving a type index, retrieve selected method facts:
```json
{
"source": {
"kind": "retained-evidence",
"evidence_id": "<parent_evidence_id>"
},
"view": {
"kind": "il2cpp-metadata",
"collection": "methods",
"declaring_types": [1555],
"names": ["SendMessage"],
"fields": ["name", "declaring_type"]
}
}
```
After finding method indices, parameters can be requested directly:
```json
{
"source": {
"kind": "retained-evidence",
"evidence_id": "<parent_evidence_id>"
},
"view": {
"kind": "il2cpp-metadata",
"collection": "parameters",
"method_indices": [9785],
"fields": ["name", "method"],
"limit": 64
}
}
```
Historical Evidence that predates these member collections remains queryable.
A missing collection has an unknown (`null`) summary count, and requesting it
reports that it was not collected; it is never presented as an empty inventory. The same holds for Evidence that predates binary linking: its summary
omits `binary`, and `method_addresses`, `method_address_states`,
`return_types`, `field_types` and `parameter_types` counts are `null`.
Existing empty collections retain a zero count. Full Evidence is preserved.
`offset` counts matching rows, not source indices. `limit` defaults to 64;
`coverage.next_offset` continues that filtered page. Coverage describes retained
metadata facts only; runtime behavior and correspondence to native code remain
unknown. Each view preserves the parent Evidence link, artifact identity and
limitations. Queries work after the source file is removed. Select
`detail: "complete"` to return every row inline within the separate MCP response
budget.
## CLI
```bash
rea inspect-il2cpp-metadata /absolute/path/global-metadata.dat --json
```
The CLI defaults to complete Evidence. For compact delivery, explicitly preserve
the full Evidence in a new file:
```bash
rea inspect-il2cpp-metadata /absolute/path/global-metadata.dat \
--detail summary --evidence_output /tmp/metadata-evidence.json --json
jq -c '{source: {kind: "inline", evidence: .records[0]}, view: {kind: "il2cpp-metadata", collection: "methods", indices: [9785], fields: ["name", "declaring_type"]}}' \
/tmp/metadata-evidence.json > /tmp/metadata-query.json
rea inspect-analysis-view /tmp/metadata-query.json --json
```
Use the exported Evidence with the CLI's inline Evidence source format described
in [CLI and Evidence](cli.md#run-a-command). Compact delivery requires
`--evidence_output`; existing files are never overwritten. The full export
preserves every decoded row for subsequent queries without the original file.
`--binary PATH` links method addresses and `--architecture arm64|amd64` selects
the slice of a universal Mach-O.
## Observations
The result contains:
- `artifact`: path, SHA-256 and byte length;
- `magic`: unsigned little-endian header signature (`0xFAB11BAF`, delivered as a JSON number);
- `metadata_version`: `24`, `27`, `29`, `31` or `39`;
- `index_widths`: the serialized widths (1, 2 or 4 bytes) of
`type_definition`, `generic_container`, `parameter` and `type` indices, and
`type_width_source`. Version 39 derives the type index width from the
`interface-offsets-record` or the `type-definition-record`; versions 24, 27, 29
and 31 always use 4-byte indices (`fixed-width-version`);
- `sections`: all version-specific header sections in header order, each with `offset`, `size`
and `count`. Versions 24 to 31 record no counts, so `count` is `null` and
record counts come from the section sizes;
- `images`: `name`, `type_start` and `type_count`;
- `types`: `name`, `namespace`, `method_start`, `method_count`,
`nested_type_start`, `nested_type_count` and `declaring_type`;
- `methods`: `name` and `declaring_type`;
- `fields`, `properties` and `events`: `name` and `declaring_type`, the type
whose member range holds the row (`-1` when none does);
- `parameters`: `name` and `method`, the method whose parameter range holds it;
- `string_literals`: managed string literals as `value`, or `value_base64` with
the exact bytes when they are not valid UTF-8;
- `limitations`.
A row's position in its collection is its metadata index, so `type_start`,
`method_start` and `declaring_type` refer to positions in `types` or `methods`.
`nested_type_start` instead indexes the metadata's `nestedTypes` table, which is
not returned; each nested type's `declaring_type` carries that relationship.
A type's `declaring_type` is derived from the `nestedTypes` section and is `-1`
for top-level types. The start of an empty range can retain the original value
`-1`.
The complete inventory of a large game is several megabytes and can exceed
the MCP response budget. Summary delivery and filtered, projected pages keep
unneeded rows out of agent context without discarding the source Evidence.
## Native method addresses
`binary_path` names the native binary built with the metadata: macOS
`GameAssembly.dylib` (Mach-O), Windows `GameAssembly.dll` (64-bit PE) or
Android `libil2cpp.so` (64-bit ELF, AArch64 or x86-64). A
universal Mach-O needs `binary_architecture` (`arm64` or `amd64`); a thin binary
rejects it. The file is read, never loaded or executed.
A position-independent `libil2cpp.so` stores zero in most pointer slots and
keeps the value in `R_AARCH64_RELATIVE` or `R_X86_64_RELATIVE` relocations
(`DT_RELA`). The reader writes those values into a private copy before
searching it, so addresses are the link-time addresses and RVAs are relative to
the first loaded segment. Android packed
relocations (`DT_ANDROID_RELA`) are refused. No target code is executed.
The binary's `Il2CppCodeRegistration` points to one `Il2CppCodeGenModule` per
assembly image. Each module record starts with its module name, its method
pointer count and its method pointer array. The reader finds, for every metadata
image, the single record whose name pointer reaches that image's name string and
whose count equals the image's method count. It then requires exactly one
`codeGenModulesCount`/`codeGenModules` pair that lists exactly those records. No
symbols are needed, so stripped builds work. A method's token row id selects its
slot in the module's method pointer array. Within one image, each method must
claim a distinct MethodDef token row; duplicate rows are refused before any
linked method table or seed output is published. Row ids may repeat across
different images, and distinct rows may still resolve to the same address when
the binary uses identical-code folding.
For ELF, a repeated load mapping that resolves a complete module record to the
same virtual address and file bytes contributes one record. Distinct virtual
record addresses remain distinct candidates, and conflicting file mappings
remain malformed.
Module records establish names through their declared NUL-terminated string pointers. NUL/0xFF padding, spaces, Unicode and suffix-shared storage are supported. A pointer to a longer string does not match just its suffix; a pointer directly into a shared suffix may name it. Exact reversed UTF-8 matching avoids rescanning the file for every name and retains duplicate-name owners until module records resolve them. A contributor investigated a Linux
`GameAssembly.so` from Unity 2023.2.13f1 whose static registration was not located. When no static pair is resolved on x86-64, the reader searches executable ranges for bytes consistent with RIP-relative LEA/MOV references to module-array candidates. Exactly one referenced array candidate must remain. `code_gen_modules_field` is then `null`, and Evidence is **inferred**: instruction boundaries, the absence of a static registration elsewhere, and runtime use were not verified. The module records and slots themselves remain file observations. Static-table associations are **derived**. Otherwise the result is `code-registration-not-found`.
The binary/metadata pair is caller-selected. Name/count/token agreement is structural evidence, not a cryptographic same-build proof. File-offset locations refer to the primary metadata artifact; linked virtual addresses refer to `binary.artifact`. Both SHA-256 identities remain available in compact native pages as `artifact` and `binary_source`.
Contributor-reported verification (not all rerun by the follow-up): macOS arm64 and x86_64 slices from Unity 2021.3.45f2, 2022.3.30f1, 2022.3.62f3 and
6000.0.84f1 against dSYM symbols; Android arm64 and x86-64 from 2022.3.62f3
against `libil2cpp.sym.so`; Windows x64 from 2023.2.13f1 (metadata version 29,
the MIT-licensed game Between Two Worlds 1.0.5, `GameAssembly.dll` SHA-256
`a3ae7f6ecd1f671297b7d98d12713ef024062d93cd7a4205298102656c93b67a`) against
Il2CppDumper, where all 43,198 linked methods share an address with a
same-named Il2CppDumper method and identical code folding groups 10,448 methods
into 1,735 addresses. A macOS x86_64 build from Unity 2020.3.49f1 (metadata version 27, which Il2CppDumper reads as 27.1) links 10,555 methods, all at addresses where Il2CppDumper names the same method; its other addresses are generic instances. A build from Unity 2021.1.28f1 (also read as 27.1) gives the same result: 10,345 methods linked, every address named the same by Il2CppDumper, and its other 3,486 addresses generic instances.
These checks compare addresses with symbols or with another static tool. They
do not establish that a linked address is the code the game runs (a runtime may
patch, hook or replace it), that builds from other Unity versions, compilers or
obfuscators link the same way, or that a method without an address has no code:
generic instances, adjustor thunks and invokers are not linked, and an
`unknown` row was not looked up at all.
The result adds:
- `binary`: artifact identity, `format`, `architecture`, `image_base`,
`code_gen_modules_field` (the address of `codeGenModulesCount`, or `null` as
described above), `code_gen_modules_array` (the module pointer array) and
`code_gen_modules` (`image`, record `address`, `method_pointer_count`);
- `method_addresses`: one row per method index with `state`, `address` and
`rva` (address minus `image_base`), both hexadecimal strings. `state` is
`code` when the module slot holds an unambiguously mapped executable address, `no-body` when an observed slot is zero,
as for abstract, extern and most generic methods, and `unknown` when no type
range in any image holds the method, a symbol relocation leaves its slot
unresolved, or dyld will bind a Mach-O slot. The raw word in a dyld-bound
slot is not treated as the resolved address.
`address` and `rva` are `null` unless `state` is `code`. The summary counts
each state in `counts.method_address_states`. Ghidra seeds are written only
for `code` rows.
- `return_types`, `field_types` and `parameter_types`: one row per method,
field and parameter index with `type`, the index into the binary's
`Il2CppType` table, and `name`, rendered from that table: C# keywords for
primitives (`int`, `string`, `object`), `Namespace.Outer+Inner` for type
definitions, `T[]` for vectors, `T[*]` for rank-one multidimensional arrays,
`T[,]` for rank-two arrays and `T*`, generic instances as
`System.Collections.Generic.List<string>` and generic parameters by their
metadata name (`TKey`). Nested generic arguments stay with the component
that declares them, for example `Game.Outer<int>+Inner<string>` or
`Game.Outer<int>+Leaf` when the nested type adds no parameters. A trailing
`&` marks a by-reference type. `name` is `null` only for type index `-1`.
`binary` also records `types_field` and `types_count`: the address of
`Il2CppMetadataRegistration.typesCount` and its value. That record is found as
the single place whose `fieldOffsetsCount` and `typeDefinitionsSizesCount` both
equal the metadata type-definition count and whose type table is file mapped.
Without `binary_path`, `binary` is `null` and the four binary collections are
empty in the complete producer report to indicate unrequested observations. Their summary counts, including state counts, are `null`, and native page requests report unavailable observations. This does not establish an empty native inventory. Missing native fields in historical Evidence remain unknown too.
For metadata version 27, type names are `null` and only type indices are
reported: versions 27.0 to 27.2 share that header number but place the
Il2CppType byref flag in different bits, so a rendered name could gain or lose
`&` without warning.
The builds checked are listed after the module search rules above.
| Reason | Category | Meaning |
| --------------------------------- | ----------- | --------------------------------------------------------------------------------------------------- |
| `unsupported-container` | unsupported | Not a 64-bit little-endian Mach-O, PE or ELF image (ELF: AArch64 or x86-64 with dynamic section). |
| `slice-selection-required` | unsupported | A universal Mach-O was given without `binary_architecture`. |
| `slice-not-found` | unsupported | The requested slice is absent, or a thin binary was given an architecture. |
| `unsupported-pointer-encoding` | unsupported | Mach-O chained fixups or Android packed (APS2) ELF relocations; pointers not decoded. |
| `malformed-container` | input error | The container headers are damaged. |
| `code-gen-modules-not-found` | input error | An image has no module record; often a binary from a different build. |
| `ambiguous-code-gen-module` | input error | More than one module record matches an image. |
| `code-registration-not-found` | input error | No unique module table or executable-byte array candidate was resolved. |
| `invalid-method-token` | input error | A token is outside its module's method-pointer rows, or a MethodDef row is reused within one image. |
| `invalid-method-pointer` | input error | A slot is unavailable or its nonzero address lacks unambiguous executable file coverage. |
| `metadata-registration-not-found` | input error | No single MetadataRegistration matches the type-definition count. |
| `invalid-type` | input error | A type index or `Il2CppType` record is out of range, unmapped or malformed. |
| `unsupported-type-kind` | unsupported | A valid type kind has no implemented rendering. |
Format and selection failures name `binary_path`; capacity refusals remain typed resource errors. Failed linking preserves the metadata source identity, validated counts, selected binary path/architecture and its SHA-256 when observed, plus prior producer observations. It never publishes an incomplete native table as complete. The binary input is bounded to1GiB.
Scans yield cooperatively in bounded batches. Name-address retention and reverse-trie construction are each limited to65,536 entries/nodes and fallback module-array candidates to64 (`native-index-capacity`); exceeding capacity does not prove absence. Rendered type text has a64MiB UTF-8 cache-workspace guard, separate from the64MiB encoded-report admission, and64 nested rendering frames (`native-type-text-capacity` / `type-nesting-capacity`). Genuine cycles are malformed; unsupported type kinds are explicit coverage failures. Native row/text admission runs before allocation or retention. Unused generic parameter names are read on demand from the same stable metadata snapshot. There is no cross-inspection file cache; validated immutable Evidence is reused for session views.
ELF RELATIVE writes into zero-fill are kept as a private virtual-pointer overlay. Unresolved supported-width symbol relocations, including RELA PLT entries, remain unknown only for the bytes their ELF ABI relocation type covers; an adjacent 32-bit relocation does not make a separate 64-bit method slot unknown. Stored zeros never establish no-body. COPY, TLS descriptor, REL, packed APS2 and other unsupported relocation encodings fail closed. Input bytes are unchanged.
Native address queries batch exact64-bit hexadecimal `addresses` and `states` (`code`, `no-body`, `unknown`), intersecting indices and pagination. Numerically equal address spellings match; original row indices and every shared-body alternative survive:
```json
{
"source": {
"kind": "retained-evidence",
"evidence_id": "<parent_evidence_id>"
},
"view": {
"kind": "il2cpp-metadata",
"collection": "method_addresses",
"addresses": ["0x180c56ed0"],
"states": ["code"],
"fields": ["address", "rva"],
"offset": 0,
"limit": 64
}
}
```
## Ghidra function names
The CLI can turn a linked report into a
[`REA_GHIDRA_SEED_FILE`](installation.md) so Ghidra names every IL2CPP method
before auto-analysis:
```bash
rea inspect-il2cpp-metadata global-metadata.dat --binary libil2cpp.so \
--ghidra-seeds il2cpp-seeds.tsv --json > /dev/null
export REA_GHIDRA_SEED_FILE="$PWD/il2cpp-seeds.tsv"
```
Each linked method becomes one `function` seed named `Namespace.Type$$Method`
(nested types joined with `.`, whitespace replaced by `_`), the Il2CppDumper
convention. Seeds are at `seed-base + rva`. The default base is where Ghidra's
loaders place the binary: `0x100000` for an ELF linked at 0, and the link base
for Mach-O and PE; `--seed-base 0x...` overrides it for a different import. When
identical code folding gives several methods one address, the first method names
the function and a `# folded <address> <kept> also <names>` comment line after
the seeds lists the others, so the kept name is visibly one of several. Ghidra still names a seeded function that is a thunk after its
target. The file is atomically published with exclusive creation; an existing destination is never replaced. Failed staging retains its cleanup owner for retry. Source digest and link/seed-base comments make the mapping inspectable. Unknown slots are skipped separately from observed no-body slots. `--seed-base` requires `--ghidra-seeds`; overflowing rebased addresses and names/outputs beyond the existing Ghidra protocol limits (2,000 name characters,32MiB file,1,000,000 entries) are refused before publication. The CLI and reusable service use one formatter and owned output workflow; ordinary MCP inspection remains read-only.
## Failures
A file whose magic is not `0xFAB11BAF` fails as an unsupported target with
`unrecognized-magic`. The file may be encrypted or may not be IL2CPP metadata.
A version other than 24, 27, 29, 31 or 39 fails as an unsupported target with
`unsupported-version`.
Damaged metadata returns an input error (`invalid_format`) whose
message names one reason:
| Reason | Meaning |
| ------------------------- | ---------------------------------------------------------------------------------------------------------------- |
| `truncated-header` | The file is shorter than the header. |
| `unexpected-header-size` | `stringLiterals.offset` is not the header size: 264 bytes for version 24, 256 for 27, 29 and 31, 380 for 39. |
| `section-out-of-bounds` | A section extends beyond the file, or a non-empty section starts inside the header. |
| `section-overlap` | Two sections overlap. Zero-size sections are exempt and gaps are allowed. |
| `record-size-mismatch` | A section size does not divide into its records, or the record size differs from the one the index widths imply. |
| `type-index-width` | The record used to derive the type index width implies no 1, 2 or 4 byte width. |
| `index-out-of-range` | A serialized index or range points outside its target collection. |
| `nested-type-conflict` | A type is listed as nested in two types, in itself, or in a cycle of declaring types. |
| `declaring-type-mismatch` | A method inside a type's method range names a different type, or a method lies outside its type's range. |
| `image-range-overlap` | Two images claim the same type definitions. |
| `invalid-string` | A name index is outside the strings section, its string is unterminated, or it is not valid UTF-8. |
A version field that disagrees with the file's layout, for example a version
39 file relabeled as 29, usually surfaces as `unexpected-header-size` or
`record-size-mismatch`.
An empty range's start must lie between -1 and the collection size, which is
stricter than some runtime paths. String indices are byte offsets; a valid
offset may select a suffix of another NUL-terminated name.
An incomplete inventory is never reported as complete. Failures after a verified
snapshot retain partial source Evidence with its SHA-256, byte length and any
validated section row counts; these counts do not establish that names and
relationships were decoded. A file that changes during reading and a cancelled
request keep distinct errors. Decode checkpoints yield for cancellation, and
close waits for active work. Each reader admits one inspection at a time.
Resource guards bound the selected file to 256 MiB and the encoded complete
report to 64 MiB, checking row counts before allocation and JSON/UTF-8/base64 expansion during
decoding, before serialization. Names and identical literal spans are decoded
once within one stable file snapshot; no filesystem results are cached across
inspections. Ownership assignment also yields to cancellation. MCP also applies its
[response transport budget](mcp-contracts.md); a complete report within the
report limit can still require the summary and paging path.
## Outside this tool
- Decrypting or deobfuscating protected metadata.
- Linking Mach-O binaries that use chained fixups, ELF images with Android
packed (APS2) relocations, and 32-bit images; generic method instances,
adjustor thunks and invokers.
- Full method signatures beyond the rendered field, parameter and return type
names, generic method instances, and type names for metadata version 27.
- Running Ghidra or another disassembler. `--ghidra-seeds` only writes a seed
file for [`REA_GHIDRA_SEED_FILE`](installation.md); use the
[native workflow](native-investigation.md) for native code.
- Metadata versions other than 24, 27, 29, 31 and 39.
Names come from the metadata file only. They do not establish which methods
were compiled into the native binary or how they behave at runtime.
## Real producer verification
`npm run verify:il2cpp:metadata -- --metadata PATH` exercises a caller-supplied
`global-metadata.dat` through the public CLI and MCP. It is opt-in, like
`verify:pcap`, because no real game metadata ships with the repository. Normal
inspection does not need it.
The follow-up was checked against the public Cpp2IL
[`Simple_2022_3_35` sample](https://github.com/SamboyCoding/Cpp2IL/tree/b5ad444b82267cb1e4b88b8b373c008105bdea52/TestFiles/Simple_2022_3_35):
version 31, 1,892,268 bytes, SHA-256
`2441635a7c234a1a34deccc6a9906d6f5abcbdab078a38b2bf488a52a4962c48`.
An independent byte-layout oracle matched all 36 images, 2,468 types and 12,409
methods. Corresponding managed DLLs independently matched names and ownership
for 35 images, 2,463 types and 12,406 methods; the generated image has no matching
DLL. This does not establish real-producer coverage for versions 29 or 39.
Compact native summaries omit the `binary.code_gen_modules` table and keep its
count in `counts.code_gen_modules`. Query `collection: "code_gen_modules"`
with batch `image_indices` or source `indices`, selected `fields` and pagination.
Module row `index` is its retained collection position; `image` joins the
metadata image collection. Scalar registration facts and both source identities
remain inline. Complete Evidence retains the original full table.
+15 -12
View File
@@ -47,20 +47,23 @@ For runtime instrumentation, see the [Frida guide](./frida-instrumentation.md).
## Choose a guide
| Task | Guides |
| --------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 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), [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) |
| Capture and compare a command | [Process capture](process-capture.md) |
| Use CLI/MCP and retain evidence | [CLI](cli.md), [MCP contracts](mcp-contracts.md), [agent prompts](mcp-prompts.md), [obligation ledgers](reconstruction-obligation-ledgers.md) |
| Task | Guides |
| --------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Identify a desktop installation | [Runtime candidates, evidence and follow-up files](desktop-applications.md) |
| Inspect native functions, references, or types | [Native binaries](native-investigation.md), [Go metadata](go-binaries.md), [Rust dependencies](rust-binaries.md), [CPython pyc files](python-pyc.md), [PE certificates](pe-certificates.md), [ELF notes](elf-notes.md), [Erlang BEAM](erlang-beam.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), [Electron fuses](electron-fuses.md) |
| Inspect assemblies, packages, or firmware | [Godot PCK](godot-pck.md), [.NET](managed-code-analysis.md), [Python packages](python-packaged-applications.md), [Android](android-analysis.md), [APK signatures](apk-signature.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) |
| Serve recorded behavior as an agent environment | [Agent environments](agent-environments.md) |
| Trace captured website source | [Source locations](web-source-location.md), [module traces](website-module-trace.md), [script export](website-script-export.md) |
| Capture and compare a command | [Process capture](process-capture.md) |
| Use CLI/MCP and retain evidence | [CLI](cli.md), [MCP contracts](mcp-contracts.md), [agent prompts](mcp-prompts.md), [obligation ledgers](reconstruction-obligation-ledgers.md) |
For development, use [testing](testing.md), [client verification](client-verification.md),
[tool design](tool-design.md), and [releasing](releasing.md).
[tool design](tool-design.md), [admission limits and recovery](design/limits-and-recovery.md),
and [releasing](releasing.md).
[Reconstruction readiness](reconstruction-readiness.md) describes a development
conformance verifier. [The 6.2 migration](migration-6.2.md) records that release's
compatibility changes; [the roadmap](roadmap.md) tracks future work.
+303 -38
View File
@@ -24,6 +24,10 @@ 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).
During package resolution, npm may print a deprecation warning for `prebuild-install`
from transitive native dependencies (such as Frida). This warning originates from
the upstream dependency tree and does not affect REA or indicate setup failure.
For the latest published release, use `npx rea-agents@latest setup`.
[Package-runner and global-installation options](#package-runners-and-global-installation)
cover explicit versions, Bun, runtime requirements, and the curl wrapper.
@@ -56,17 +60,39 @@ Before applying changes, REA checks your current configuration. The plan lists:
Malformed or unsafe existing configuration blocks the whole transaction before
Hopper installation or any file write. Declining or pressing Ctrl-C makes no
changes. Agent configuration writes preserve unrelated entries and comments,
create backups, use atomic replacement, and verify their result. Setup and
uninstall retain an existing `.rea.backup` rather than replacing the first snapshot.
If that snapshot differs from the current configuration, setup previews and
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.
changes. Client configuration files and bundled skill files have separate
publication and recovery paths; the guarantees in this paragraph apply to
client configuration. See [skill destinations](#skill-destinations) for skill
rollback limits. Agent configuration writes preserve unrelated entries and comments,
create backups, stage changes in a private directory, and verify their result.
Before publication, they refuse detected changes to the selected configuration.
Replacement uses an explicit destination under the selected canonical parent;
it does not follow a newly inserted destination symlink. First-run setup uses
an exclusive hard link and fails without overwriting a newly created destination;
filesystems without hard-link support cannot publish a first-run configuration.
Setup and uninstall retain an existing `.rea.backup` rather than replacing the
first snapshot.
If that snapshot differs from the current configuration, setup and uninstall
preserve a verified `.rea.backup.<sha256>.recovery` copy of the current bytes
before editing; setup also previews it. Both snapshots remain available.
When an edit made a backup, a failed uninstall update or restore, and a failed
setup readback or rollback, name the backup path that holds the bytes from just
before that edit.
If setup cannot verify a newly created configuration, it retains the file and
reports manual recovery instead of deleting a pathname that may have changed.
For an existing configuration, setup and uninstall recovery refuse a detected
replacement or edit and name the recovery backup. Quit clients before editing their configuration;
these checks do not lock out another writer modifying the same file in place,
or exclude pathname changes between the final check and publication.
Setup also retains valid environment overrides in REA's existing server entry,
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 manages REA's launcher, selected provider settings, and supported timeout
defaults. Client approval policies remain user choices, but the current Codex
refresh drops those policies inside the REA entry; see the
[Codex preservation limitation](client-configuration.md#codex-and-devin) before
refreshing a customized registration.
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.
@@ -86,9 +112,11 @@ your investigation. Failed steps and diagnostics remain in terminal history.
Select exact clients in scripts with repeatable `--client` flags. Each explicit
client skips interactive selection. Use `--all-detected` only when you intend
to configure every detected supported client. `--skill=false` omits the
workflow, and `--dry-run` returns a read-only plan with status `planned` and
exit code 0:
to configure every detected supported client. Combining `--client` with
`--all-detected` configures both the explicitly selected and detected clients,
even when an explicit client is not detected. Each client is configured once.
`--skill=false` omits the workflow, and `--dry-run` returns a read-only plan
with status `planned` and exit code 0:
```bash
rea setup --client codex --client cursor --skill=false --dry-run
@@ -115,6 +143,15 @@ exits 1. Installing missing Hopper non-interactively additionally requires
rea setup --yes --all-detected --install-hopper --json
```
Client configuration writes detect changes to the file read by setup, including
when a matching recovery backup already exists. A detected conflict reports
`reason: "conflict"` and preserves the newer file; review it and rerun setup.
Rollback likewise requires the file identity and content installed by that
transaction. Requests for the same resolved path are serialized within one REA
process. These checks do not provide an operating-system compare-and-swap or
coordinate arbitrary external writers, so avoid editing client configuration
while setup is applying it.
### Skill destinations
The bundled skill is installed in each selected client's personal skill directory.
@@ -133,6 +170,26 @@ 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.
The skill installer preserves byte-exact backups of changed existing files,
including invalid UTF-8, BOMs, and permission modes, and verifies each published
file through its owned descriptor. Before publication it checks the selected
file identity; first installation uses an exclusive hard link and refuses a
concurrently created destination. Filesystems without hard-link support cannot
publish a first-install skill file.
Rollback restores an existing file only while its published identity, state,
and bytes still match that installation attempt. A recovery backup preserves
the original bytes; it does not authorize overwriting a later user edit or
replacement. Conflicts report the affected paths and recovery backups. If a
newly created skill file cannot be verified, setup retains it and reports manual
recovery rather than deleting by pathname. Staging cleanup failures remain
owned and retryable, and setup does not report installation success while
cleanup is unresolved.
These checks do not lock out another writer modifying the same inode or exclude
pathname changes between the last check and publication. Avoid editing managed
skill files or running another installer while setup applies them.
`rea setup --skill-client claude_code --yes --json` refreshes and verifies the
selected skill destination independently of MCP registration selection. The
final `doctor.scope.skill_clients` records which skill destinations were
@@ -179,6 +236,8 @@ as well, so it is broader than this Claude-only migration.
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
@@ -260,14 +319,14 @@ 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
claude mcp add --scope local rea -- npx -y rea-agents@6.7.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
claude mcp add --scope local rea -- cmd.exe /d /c npx -y rea-agents@6.7.0 mcp
```
<!-- x-release-please-end -->
@@ -324,6 +383,13 @@ A scoped report has `scope.mode: "explicit"`. Its `scope_checks` determine
`environment_healthy` summarizes the full audit. `--target` adds a target check,
but the report remains audit-wide unless a scope option is also supplied.
An invalid `REA_LOG_LEVEL` fails every scope, including one client or the
installed skill, because the executable rejects it before any command. A
selected client also fails for every other setting that MCP startup rejects,
including `REA_GHIDRA_STARTUP_TIMEOUT_MS` and `REA_HOPPER_STARTUP_TIMEOUT_MS`.
Those settings still fail a selected provider and the audit-wide result. A
skill-only report leaves them informational.
Follow the remediation for the failed check. If a native target has several
available providers, choose one with `--provider` on the CLI or `provider_id`
on `open_binary`. A `capability_unavailable` failure carries
@@ -360,11 +426,68 @@ Uninstall preserves Hopper, Node.js, Evidence files, captures, unrelated skills
and other MCP servers. Purging removes only REA's cache and state under
`~/.rea`. A client configuration that is malformed, unreadable, or at an unsafe
path stops the operation before anything is removed, and a client that fails
while being updated stops the remaining removals. A purge path that is a
while being updated stops the remaining removals. Uninstall reports each
candidate `configuration_path` with its outcome. A client with multiple files
keeps their individual `actions` alongside its aggregate status and detail,
including earlier removals when a later file fails. The paths name
the discovered configuration candidates, not resolved physical file identities.
For JSON clients, uninstall removes every repeated `rea` member in the effective
server table only when each registration is REA-owned. An unowned earlier
member, or an unowned OpenCode V1 entry that removing V2 would expose, stops
preflight before any
removal. Repeats inside an inactive earlier copy of the server table stay
unchanged. A purge path that is a
symbolic link is retained and reported rather than followed. Uninstall checks
REA's managed shared and Claude Code personal skill locations. See the
[CLI guide](cli.md#output-and-exit-status) for exit statuses.
### Missing dependencies in an npx installation
If REA exits with `ERR_MODULE_NOT_FOUND` before `doctor` can produce a report,
inspect the missing path in the error. A path under npm's `_npx/<key>/node_modules`
identifies a package-runner installation. In [#5259](https://github.com/morluto/rea/issues/5259),
the missing file was `@modelcontextprotocol/client/dist/index.mjs`. That file is
present in a clean Windows installation of REA 6.7.0; its absence alone does not
establish an incompatible dependency release or explain how the file went missing.
`npm cache verify` checks npm's downloaded content cache; it does not restore
missing files in an already extracted npx installation. Repeating the same npx
command can reuse that incomplete installation. To reinstall only the affected
entry:
1. Stop the REA processes and agent clients using that entry. In the same shell
environment as the failing command, identify the cache and its entries:
```powershell
npm.cmd config get cache
npm.cmd cache npx ls
npm.cmd cache npx info REA_CACHE_KEY
```
Replace `REA_CACHE_KEY` with the key from the failing `_npx` path. Check that
`info` reports that location and the expected `rea-agents` version. Its
`valid` label describes the cache entry metadata, not the presence of every
installed file. These subcommands require npm with `cache npx` support.
2. Remove that specific entry and rerun the affected version with registry
access. For the version reported in #5259:
```powershell
npm.cmd cache npx rm REA_CACHE_KEY
npx.cmd --yes rea-agents@6.7.0 doctor --json
```
Keep the key argument: this recovery does not require clearing all npm caches
or deleting REA configuration, skills, or Evidence. On Linux/macOS, use `npm`
and `npx` without `.cmd`. A JSON doctor report confirms startup recovered;
it can still exit nonzero for missing analysis engines or registrations.
Restart the affected agent after repairing its cache entry.
If a fresh installation still loses the file, retain the error, REA/Node/npm
versions and installation details in the issue. Check installation logs and,
on Windows, security software's protection history before attributing the cause.
Do not rename `index.cjs` to `index.mjs` or disable protection to make startup pass.
### Update the owning installation
`rea update` installs the exact resolved
@@ -503,8 +626,10 @@ 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 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
The automated Hopper installer supports macOS 12+ and x86_64 hosts running
Ubuntu 24.04+, Fedora 41+, Nobara 44+, Arch Linux, CachyOS, or Omarchy. The
official Linux packages are x86_64-only, so setup does not offer Hopper on other
Linux architectures; configure [Ghidra](#ghidra) there instead. 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
@@ -583,12 +708,28 @@ 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.
Hopper also embeds Python; the Python executable and standard library alone
are insufficient on Ubuntu. Approved setup checks the system interpreter
at `/usr/bin/python3` and, when necessary, installs its matching
`libpython3.X` package, then verifies that its shared library can load.
APT resolves the concrete package (for example, `libpython3.12t64` on Ubuntu
24.04). Doctor includes these fresh observations in the `embedded_python`
details of its `hopper-demo-runtime` check, alongside the private-display
diagnostic. Failed inspections remain unknown rather than proving a library
absent. Run `rea setup --yes --install-hopper` to repair a supported install.
This prerequisite check does not launch the bridge or validate a custom Python
interpreter configured inside Hopper; that interpreter needs its own matching
shared runtime. Custom installation prefixes or shared-library ABIs require
manual dependency repair.
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`.
failure. Hopper is optional, so setup still applies the approved client
registrations and skill, then reports `needs_human` with the Hopper failure. On
Arch Linux and CachyOS, the unversioned compatibility package `zlib-ng-compat`
may conflict with Hopper's versioned `zlib` dependency. Resolve the package
conflict yourself, install Hopper's official package manually, or use another
provider such as Ghidra; rerun `rea setup` after installing Hopper.
The supported Linux demo forwards additional launches into its existing
application, even across private displays. REA therefore reserves one Linux
@@ -623,7 +764,9 @@ and skips directories.
On Linux, it prefers executable `/opt/hopper/bin/Hopper`, then executable
`~/.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:
fallback. `rea doctor` checks only the current platform's candidates, in the
same order; other platforms have no automatic candidates.
`HOPPER_LAUNCHER_PATH` overrides those choices:
```bash
export HOPPER_LAUNCHER_PATH=/absolute/path/to/Hopper
@@ -635,6 +778,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
@@ -644,9 +792,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:
@@ -736,9 +887,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
@@ -797,7 +956,7 @@ 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, plus explicitly
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.
@@ -815,6 +974,16 @@ rea doctor --json
rea setup
```
On macOS, [Homebrew OpenJDK is keg-only](https://formulae.brew.sh/formula/openjdk)
and may be unavailable to the system `java` wrapper. Set `JAVA_HOME` to the
selected formula's `libexec/openjdk.jdk/Contents/Home`, for example
`/opt/homebrew/opt/openjdk/libexec/openjdk.jdk/Contents/Home` or
`/usr/local/opt/openjdk/libexec/openjdk.jdk/Contents/Home`. Use a JDK inside the
Java version bounds declared by your Ghidra installation; REA does not select
another installed JDK automatically. Doctor distinguishes an absent Java
command from a command that runs and fails, and retains bounded probe output
and any parsed properties when the selected runtime cannot be admitted.
On Windows, configure the existing installation in PowerShell:
```powershell
@@ -846,12 +1015,50 @@ It also checks that the reported JVM `os.arch` and `os.name` map to the same nat
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.
rejected at launch. `JDK_JAVA_OPTIONS` argument files (`@file`) are expanded
and inspected with that same check using the Java launcher's argfile quoting,
comments and continuation rules. The reader admits regular UTF-8 files, retains
failed descriptor cleanup, and bounds all admitted file contents and the prepared
environment to 64 KiB per launch. Unreadable, oversized or nested `@` references
are refused before the analysis JVM starts. Validated bytes are snapshotted under
the owned runtime root, so later helpers and the analysis JVM do not reopen mutable
caller files. Repeated references share that launch's snapshot; no live state is
cached across launches. `JAVA_TOOL_OPTIONS`
and `_JAVA_OPTIONS` do not expand `@file` tokens, so they remain
literal-token checks.
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,
installs, upgrades, or modifies Ghidra or Java.
Installation inspection derives the private configuration directory identity from
`application.version` and `application.release.name` in
`Ghidra/application.properties`. The directory's version spelling follows Ghidra
(for example, `12.1.0` becomes
`12.1`), while the reported provider version preserves the original value.
The launcher uses that inspected coordinate; it does not reread the properties
or guess from the extracted folder's name.
If the identity is missing, malformed, or unsafe as a single path component,
the launcher fails before preparing its home/cache/config/temp directories or
starting Java. The client may already have allocated private runtime/endpoint
resources and a target snapshot; existing startup-failure cleanup still applies.
Restore the selected release's properties instead of renaming its installation folder.
Inspection follows selected properties symlinks, admits only a regular file,
and caps the metadata payload at 1 MiB under REA's resource budget. A final
one-byte probe checks for growth. This is not a Java
Properties format limit. Reads preserve Ghidra's Latin-1 byte mapping and check
the descriptor and selected name for changes. Permission failures and resource
refusals remain explicit; failed descriptor cleanup must succeed before that
owner admits another inspection.
Native decompiler compatibility inspection follows the selected executable symlink
and reads at most the first 4096 bytes through the same descriptor owner. It
checks the full extent and selected name for changes without reading the remainder.
An unreadable or unrecognized header leaves compatibility unknown; diagnostics
retain read failures. Cancellation and incomplete cleanup propagate as failures,
and failed cleanup retains the exact descriptor for retry.
This coordinate selects only REA's session-private configuration directory, not
the installation or an existing user configuration directory.
Each verified session uses an ephemeral temporary project and isolated
home/cache/config/temp paths. REA passes `-readOnly`, `-deleteProject`, uses
Ghidra's default analysis settings, and loads its packaged Java
@@ -867,6 +1074,10 @@ ownership inspection, so retaining those wrappers would prevent verified
process-group cancellation during startup.
If ownership remains unverifiable, it reports the reason and retains the process
supervisor and private runtime instead of removing files beneath a live provider.
Startup preserves an observed launch failure when cancellation arrives during
rollback. Producer cancellation retains the caller's exact reason separately
from LaunchSupport warnings and cleanup failures. Failed rollback keeps the
original process owner for a later cleanup retry.
The experimental Windows transport uses authenticated IPv4 loopback with a
private native-owned bearer descriptor and Job Object process ownership.
@@ -878,6 +1089,9 @@ bridge, and health readiness. Large binaries can need more: set
`REA_GHIDRA_STARTUP_TIMEOUT_MS` to an integer between 1 and 2,147,483,647
milliseconds in the server environment. An absent setting uses the default;
empty, invalid or out-of-range supplied values fail configuration validation.
When the deadline elapses, the `provider_timeout` error names
`REA_GHIDRA_STARTUP_TIMEOUT_MS` with the deadline that was applied and suggests
`REA_GHIDRA_DISABLED_ANALYZERS` for analyzers that dominate the time.
REA parses the supplied configuration environment and
passes the deadline to each provider client; a startup failure is returned by the query that triggered it,
without exposing partial analysis. This deadline is separate from MCP transport
@@ -886,6 +1100,24 @@ can time out earlier even when Ghidra would complete within its startup deadline
See [Ghidra first-query deadlines and recovery](mcp-contracts.md#ghidra-first-query-deadlines-and-recovery)
for client options and the close/reopen recovery flow.
The 330-second default is configurable. Configuration is read when REA starts:
changing an environment variable in another shell, or closing and reopening a
binary in the same MCP server, does not change that server's startup allowance.
For a CLI retry with an example 30-minute allowance:
```sh
REA_GHIDRA_STARTUP_TIMEOUT_MS=1800000 rea analyze /absolute/path/to/app.exe --provider ghidra --json
```
For MCP, export useful Evidence before restarting, close the binary and resolve
any cleanup failure, update the environment in the client's REA server launch
configuration, then restart REA and reconnect before reopening the target. Allow
startup plus the query in the client's own tool-call deadline. Every retry uses
a fresh ephemeral import; smaller result requests do not reduce the prerequisite
auto-analysis. A larger time allowance does not raise the JVM heap limit or
guarantee completion. Keep [heap and host CPU controls](#ghidra-heap-and-cpu-controls)
appropriate for the machine.
One session contains exactly one imported Program; use `provider_id: "ghidra"`, `--provider ghidra`, or
`REA_ANALYSIS_PROVIDER=ghidra` when both Hopper and Ghidra support the target.
One persistent decompiler is owned by the Program, and a serial queue keeps
@@ -959,6 +1191,8 @@ export REA_GHIDRA_COMPILER_SPEC_ID="borlandcpp" # optional; requires a languag
REA passes `-processor`/`-cspec`, checks both against the installation's `.ldefs`, and records them in the analysis profile. DOS imports reject an override.
Language definitions must be well-formed XML encoded as UTF-8 or BOM-marked UTF-16, with any encoding declaration matching the bytes. Literal Unicode replacement characters (U+FFFD) are accepted; malformed source bytes are rejected.
To seed code that auto-analysis cannot reach (computed jumps, pointers in data):
```bash
@@ -983,12 +1217,22 @@ override in `analyzer_preset` (for example
open when its committed profile and the setting differ. A name starts with a
letter or digit and contains only letters, digits, spaces, periods, underscores,
and hyphens; at most 32 names are admitted, each at most 120 characters.
Names must identify analyzers registered for the selected program. Unknown names
or options that remain enabled abort the headless import before auto-analysis
and the bridge, so an unsuccessful override cannot produce a session claiming
that the analyzer was disabled. For Windows PE resource analysis, the exact name
is `WindowsResourceReference`. Disabling an analyzer skips the facts it would
have produced.
The setting applies to every import in the REA server, but Ghidra registers
format-specific analyzers, such as `Objective-C Message Analyzer` or
`Demangler Swift`, only for programs they can analyze. A name that the Ghidra
installation provides but did not register for the selected program is not
applicable: the import continues, the profile still commits the configured
names, and result limitations report the applied preset with that name marked,
for example `ghidra-default+disabled:Objective-C Message Analyzer(n/a)`. A name
that no installed analyzer provides, or an option that remains enabled, aborts
the headless import before auto-analysis and the bridge with an error naming
`REA_GHIDRA_DISABLED_ANALYZERS` and the analyzer, so a misspelled override
cannot produce a session claiming that the analyzer was disabled. Changing the
environment requires restarting the server; reopening a target alone keeps the
current setting. For Windows PE resource analysis, the exact name is
`WindowsResourceReference`. Disabling an analyzer skips the facts it would have
produced.
### Ghidra heap and CPU controls
@@ -1056,8 +1300,14 @@ 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.
`REA_SWIFTC_COMPILE_TIMEOUT_MS` to an integer between 1 and 2,147,483,647 in
the environment that starts REA when a busy host needs longer. Restart REA to
apply a change; the next ownership-helper compile uses the new value. A
timeout names that effective deadline.
On macOS, the Java prerequisite probe waits for this independently bounded
preparation before starting its five-second command deadline. Caller cancellation
still interrupts preparation, and failed helper cleanup remains owned for retry.
Linux process-table preparation remains inside the command deadline.
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
@@ -1104,7 +1354,7 @@ For a client that requires manual configuration, use:
"mcpServers": {
"rea": {
"command": "npx",
"args": ["-y", "rea-agents@6.5.0", "mcp"]
"args": ["-y", "rea-agents@6.7.0", "mcp"]
}
}
}
@@ -1149,7 +1399,7 @@ equivalent entry point. Setup run through `npx` registers
`cmd.exe /d /c` so clients can start the runner's command shim. Neither
registration points into the runner's temporary package cache.
REA supports Node.js 22.x (>=22.19), 24.x (>=24.11), and 26+. Node.js 23, 25, and prereleases are unsupported. It uses the npm already paired with that runtime and never upgrades Node.js, npm, or Homebrew.
REA supports Node.js 22.x (>=22.23.4), 24.x (>=24.22.1), and 26.12.1+. Node.js 23, 25, earlier 26.x releases, and prereleases are unsupported. It uses the npm already paired with that runtime and never upgrades Node.js, npm, or Homebrew.
When REA runs on Bun, the `rea doctor` `node` check reports the Bun version and
the Node.js compatibility version Bun declares, and passes when that
@@ -1169,6 +1419,21 @@ on the shell `PATH`. Use the setup command above for the guided setup
journey, `npx -y rea-agents@latest` for unattended one-off commands, or install globally
with `npm install --global rea-agents` for a shell-visible `rea` command.
For many concurrent agent sessions, run setup directly from a global installation
to avoid keeping a separate npm launcher process for each MCP connection:
```bash
npm install --global rea-agents
rea setup
```
Review the plan, approve the selected client changes, then restart those clients.
This registers the installed entrypoint directly; installing the global package
alone does not replace existing `npx` registrations. Each client still has its own
REA process and analysis state. Direct registrations follow the installed CLI;
use `rea update` to update it. Running setup through `npx` again selects the pinned
package-runner command. Do not register paths inside npm's temporary package cache.
The optional curl wrapper installs only the global npm package:
```bash
+13 -2
View File
@@ -68,9 +68,20 @@ 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
`exposes`, `sends`, `invokes`, `handles`, `calls`, `accesses`, `persists_to`,
`observed_as`, and `changed_from`. Provider names are deliberately absent from both
vocabularies.
For a `maps_to` edge targeting a source-map node, the edge carries
+193 -5
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,111 @@ 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.
Content-deduplicated module references match every retained observation path.
A path-filtered reference reports the caller-selected path while keeping its
shared node identity. Item selection also accepts any retained path when it
identifies one module; if several module kinds share that path, select the
reported `node_id`. Node-ID selection uses the stable representative path.
## 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.
## Retained file and native facts
Read metadata, native inventory and unavailable processing scopes directly from
retained application observations:
```json
{
"source": {
"kind": "retained-evidence",
"evidence_id": "ev_ACTUAL_PARENT_ID"
},
"view": {
"kind": "javascript-facts",
"where": { "paths": ["package.json", "build/Release/addon.node"] },
"property_keys": [
"name",
"version",
"bytes",
"availability",
"operation",
"analysis_status"
],
"offset": 0,
"limit": 32
}
}
```
Each row preserves its node identity, observation ID, source-map reference and
original Evidence, including location, authority, confidence, coverage and
limitations. `property_keys` selects exact retained property names; absent keys
stay omitted and retained null values stay null. An empty list requests only
identity/provenance. Observations are not merged into a single asserted fact.
When property names are unknown, opt into
`include_available_property_keys: true`. Each returned row then lists its
original `available_property_keys`, without unselected values; combine this with
`property_keys: []` for discovery only. The default is false, so known-field
reads do not pay for field inventories. Keep node/observation IDs paired when
combining rows; location-related observations need not belong to the same node.
`where.paths` compares exact Evidence `artifact-path` values or `source-range`
sources. Empty query strings remain valid selectors, though current location
contracts require nonempty paths/sources. It does not inspect display labels or a
property named `path`, normalize URLs, or reopen files. Omission includes unknown
locations; an empty list matches none. Optional `where.kinds` filters original
node kinds, with an empty list likewise matching none. Multiple locations of
shared content remain separate rows with the same canonical node identity.
These rows select application-graph observations. Semantic relationships and
semantic unknown records remain in complete parent Evidence and trace tools.
Rows follow retained node/observation order. The default offset/limit is 0/64;
follow `coverage.next_offset`. Page totals count matching retained observations,
while `source_coverage` preserves caller selection and application/semantic
partial or unknown states. An empty or exhausted page does not establish absence
outside examined coverage. Complete source Evidence remains available through
export. CLI `inspect-analysis-view` and portable Evidence use the same contract
and application implementation as MCP.
## Feature tracing
Select one literal seed kind: node ID, route, string, API, IPC channel, module,
@@ -55,6 +160,14 @@ 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
@@ -76,6 +189,33 @@ registrations/removals and dispatch, Node timers and cancellation handles, async
`node:child_process` creation and ownership, configuration sources and direct
defaults, request construction and response consumers, parse/coercion/
validation boundaries, and built-in resource acquisition/release.
Global timer, `fetch`, `WebSocket`, `JSON.parse`, and coercion calls receive
these built-in effects only when the file has no visible possible write to that
global, including a computed write through a global object. The check does not
evaluate order or reachability, so a later or guarded polyfill write also
removes the resolved effect.
Local callback candidates do not establish a complete target set when a
conditional write may leave a parameter, catch binding, or uninitialized
variable unchanged. These calls retain candidate edges and dynamic uncertainty;
default traces remain ambiguous with partial coverage. Opt into
`include_ambiguous_dynamic_edges` to traverse the candidates. Proven preceding
unconditional assignments, including writes dominating a read in the same
conditional block, can resolve a target; later writes do not replace an
earlier read or captured alias. Unproved ordering across branches or function
activations remains conservative. Returned-reference effects use the same
read-position facts as call resolution.
Returned-reference effects share one property-projection state per source read.
When several paths reach that read, differing keys at the same depth become
wildcards; differing depths become an unknown-depth selection. This retains a
conservative superset of the represented local targets rather than enumerating
every alias history or dropping paths at a query cap. It can add possible effects,
including for finite projection cycles. Static limitations disclose this loss of
precision; semantic graphs retain `dynamic-property` unknowns at the affected
callee coordinates, and captured web bundles identify the affected script keys.
These candidates do not prove runtime calls or mutations. Existing source-worker
and result-admission limits still apply.
Mutation tracking covers explicit member assignments, updates, deletion, and
loop assignment targets, including the supported local aliases and shared
@@ -126,8 +266,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
@@ -165,6 +310,27 @@ and the result includes its basis, candidates, changed dimensions, Evidence
links, limitations, and a `changed_from` graph containing all compared nodes,
their observations, and relationships.
A matched pair whose only differing dimension is `location` counts as
`unchanged` when both input graphs have complete coverage and its neighboring
relationship identities are resolved. This includes an
identical module shifted by an earlier bundle edit and a named IPC channel
observed at a new offset. The item still reports `dimensions: ["location"]`
and both node IDs; the graph retains both original observations and coordinates.
Content, property, and complete relationship differences still count as
`changed`. With incomplete coverage, relocation alone remains `unknown` and
retains the location and coverage dimensions. Matching confidence is unchanged:
an inferred pair is not promoted to exact identity by this classification.
Complete inventory does not resolve ambiguous or unmatched relationship targets.
A matched module with identical bytes can therefore remain `unknown` when a
neighbor, including its containing chunk, has no cross-version match. The item
retains its exact content match, coordinates, candidate inventory, and an explicit
relationship-identity limitation. `coverage` is added to its dimensions only for
incomplete inventory. Definite content, property, or complete topology differences
remain `changed` while retaining any separate relationship uncertainty.
An unknown item with no observed content, property, or relationship difference
does not emit a `changed_from` edge; its pairing and uncertainty remain in the item.
One-sided absence is `added` or `removed` only when the opposite input graph has
complete coverage. With partial, unavailable, or truncated input, the same
condition is `unknown`. The comparison retains every classified item and
@@ -208,6 +374,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
@@ -239,8 +412,23 @@ 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.
index. For these returned-reference writes, unrelated properties and known
array prefixes remain precise. Accessor applicability uses the shared object
reference owner for literal definition order, own-property shadowing, known
array elements and explicit literal prototypes. Getter invocation effects
project write and call operands through the same local alias and `this` owner
used by returned references. Reads of another selected getter continue that
effect walk. A receiver reached by these effects becomes uncertain on an
ordinary or optional read, compound assignment or update. A simple assignment
or deletion does not invoke the getter. Selected setters invalidate
the receiver on assignment, including through known array and prototype paths.
Inert getters and ordinary data slots retain their supported precision.
These are conservative source-derived effects, not execution results. The
analysis does not establish general accessor purity, model every class or
reflectively installed descriptor, or cover getter reads introduced by object
spread/destructuring and all implicit coercion or disposal invocations. Absence of a later explicit write does not establish getter purity;
unsupported invocation forms can still retain stale receiver-field facts.
Nested callable returns are not assigned to their parent callable. Projected
graph observations carry source ranges but never source text.
+195 -19
View File
@@ -50,6 +50,12 @@ The equivalent MCP input is:
`input_path` must be absolute. `format` accepts `auto`, `asar`, or `directory`.
`integrity_policy` accepts `fail` or `record-and-continue`; both CLI routes and
the MCP tool default to `fail`.
Evidence parameters retain the caller-selected `input_path`, including a
symlink alias. Both CLI routes anchor relative inputs to their working
directory without resolving symlinks. The subject and normalized result use
the resolved path for artifact identity; the selected path remains a separate
request fact. Older Evidence can omit this parameter; retained records are
validated without resolving their paths against the live filesystem.
The result retains the canonical local path, root artifact digest, artifact
manifest and graph commitments, JavaScript Application Graph, static
Electron summary, reconstruction statistics, and explicit limitations. It does
@@ -100,6 +106,18 @@ only reduces delivery; source selection reduces text acquisition and analysis.
Explicit native provider or snapshot routing rejects `--source-path` rather
than ignoring it.
ASAR header/index admission defaults to 16 MiB / 16777216 bytes. Set
`REA_ASAR_MAX_HEADER_BYTES` in the environment that starts REA to a decimal
byte count of at most 268435456 (256 MiB) to raise or lower it; restart REA
after changing it. Invalid values are configuration errors, never a silent
fallback. The archive index is read before any member can be selected, so
`source_paths`, `--source-path`, `detail: "summary"`, `max_heap_mb`, and
available host memory do not bypass or raise it. When the header exceeds the
effective allowance, the failure names it, whether it came from the default or
the setting, and the setting's limits. Alternatively, analyze an extracted
application directory, which does not admit an ASAR header. Repeating the unchanged
request fails the same way.
## Read the analysis
`analyze_javascript_application` returns the complete analysis Evidence by
@@ -166,7 +184,8 @@ for the tool-result contract.
Use MCP `detail: "summary"` for a large application, then read selected views of
the retained analysis. Summary changes delivery only. To reduce source parsing,
select exact [source paths](#select-sources-before-analysis).
select exact [source paths](#select-sources-before-analysis). Source selection
does not bypass ASAR header/index admission.
If a complete response exceeds the transport budget, use its retained Evidence
ID for a summary, module page, or item, or export the bundle. See
@@ -182,6 +201,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)
@@ -221,7 +252,8 @@ Selected bounded text is then parsed as inert data to recover:
- Electron preload paths and renderer files visible in static syntax;
- local HTML script entrypoints;
- Webpack and Rspack chunk registrations and module factories represented as
AST literals;
AST literals, plus esbuild `__commonJS` module tables; recovered esbuild
CommonJS exports use its factory's `exports`-then-`module` parameter order;
- static imports, dynamic imports, CommonJS `require` calls, workers, and
service workers (only `new Worker(new URL(path, import.meta.url))` resolves
relative to the calling module; string worker and service-worker paths use
@@ -299,13 +331,25 @@ 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-v2` structural fingerprint that ignores
ordinary identifier names while retaining syntax, literals, operators, object
keys, and member properties. Computed member identifiers retain their access
syntax without treating a local variable's spelling as a fixed property; template
literal contents remain part of the fingerprint. Version comparison keeps the
algorithm in its matching key, so retained `babel-ast-v1` fingerprints cannot match
these new fingerprints. Re-analyze both snapshots to compare their new structure.
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.
@@ -326,11 +370,35 @@ substitute version-dependent Electron defaults for omitted `webPreferences`.
Unresolved option objects, bridge keys, API objects, and IPC channel expressions
remain unknown and make coverage partial.
BrowserWindow options and `webPreferences` can follow local `const` aliases
whose initialization precedes the use. The shared lexical and mutation index
checks the selected `webPreferences` path, so an unrelated `options.icon` write
does not discard these facts. Reassignment, relevant writes, prior escapes,
ambiguous initialization, and unresolved spreads remain unknown. The binding
association is an inference: the window observation retains the constructor
location and `resolution_sources` identifies the original object literals in
the same artifact. Inline option syntax keeps its observed Evidence. This
does not execute code or establish runtime configuration; popup overrides from
`setWindowOpenHandler` are not covered by this constant-options resolution.
Recognized literal module-table bootstraps also support bounded lexical
substitution through module exports, import helper bodies, local wrapper calls,
returned closures, object spreads, and callback parameters. Recovered Electron
paths retain source ranges, module identities, literal channels and branch
conditions as static candidates. Unknown spreads, reassigned bindings, accessors,
conditions as static candidates. A channel the IPC call site itself
determines, such as a local constant, resolves that call's observation rather
than adding a second one. A channel or registration handler supplied through
a wrapper argument is a candidate beside the call's own unresolved
observation, because callers outside the traced paths may pass other values.
A wrapper call that is both its own entry and reached through another entry
is recorded once when both paths find the same facts; calls that supply
different handlers or channels stay distinct.
A contextBridge exposure reached through wrapper calls records one API per key
those invocations supply, each with its own bounded member paths. Invocations
that supply the same key to one exposure call describe one API covering each
alternative object. A traced invocation whose key, API object or world stays
unresolved or guarded keeps the call's own unresolved observation.
Unknown spreads, reassigned bindings, accessors,
dynamic channels and unsupported syntax do not become exact implementations.
This pass neither executes the bundle nor proves that runtime guards pass.
An ambiguous or unresolved handler retains its channel registration but does
@@ -339,6 +407,29 @@ conditions retain both return branches, including fallthrough. Short-circuit
selection distinguishes truthiness from nullishness; uncertain `??` paths
retain a nullish guard rather than a falsy guard.
Recognized module factories retain a separate initialization boundary for each
reachable return and for fallthrough. A conditional early return does not hide
later exports on the continuing path; exports and captured initializers after a
definite exit remain unavailable. Fallthrough candidates retain their branch
predicates. Exports shared by the complete set of proven exits also retain
unconditional facts. Their shared view excludes later overwrites and bounds
captured initializers by the earliest completion; branch-dependent facts keep
their predicates. Incomplete or additionally guarded exit sets cannot use that
proof. Completed factory control flow is reused within that analysis only.
Guarded main-process registrations remain candidate paths even with empty
callbacks; they do not become unconditional IPC summary facts.
Factory guards read export writes at their source position. Destructured and
spread snapshots retain their capture position rather than reading later writes.
An unproven namespace read boundary remains unknown.
Completed factory environments preserve their `exports` and `module.exports`
alias identity when a namespace escapes through an export or returned closure.
Synchronous helper calls retain the caller's factory activation and read point;
lexical captures and executing callers remain separate facts.
Async calls retain their truthy Promise identity until an explicit `await`;
generator calls produce iterators without evaluating their bodies. Iterator
advancement and Promise/iterator members remain explicitly unresolved. Awaited
function values retain their source origins and captured initialization bounds.
These paths appear as contextual `javascript-expression` nodes. An exact `api`
seed such as `desktop.copy` can trace an exposed member through a unique IPC
pairing to an external API call. `addListener` is normalized to `on`. Semantic
@@ -402,10 +493,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
@@ -420,6 +514,11 @@ inventory digest before parsing, and malformed ASAR operations return typed
format diagnostics that retain the local container path. Native add-ons are
represented by metadata only.
POSIX directory names are read as raw bytes and admitted as logical paths only
when they round-trip through UTF-8. Invalid names are left untouched, retained
siblings remain inventoried, and traversal coverage is marked unknown with the
first rejected basename bytes identified in hexadecimal.
## Bounds and coverage
The reconstruction keeps internal resource safeguards for artifact entries,
@@ -437,7 +536,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
@@ -511,20 +621,68 @@ The default semantic capacities are 100000 nodes across the application and
`--max-semantic-nodes-per-file`), each from 1 to 1000000. The per-file offer is
also bounded by the remaining application capacity. Raising these controls does
not bypass worker heap, execution deadline or parent result admission. Truncated
coverage reports both effective node ceilings; omitted relationships remain
unknown.
coverage lists both effective ceilings, `max_semantic_nodes` and
`max_semantic_nodes_per_file`. The limitation names only the ceiling that
stopped retention. Omitted relationships remain unknown.
Unknown records have independent defaults of 100000 across the application and
20000 per source. Select `max_semantic_unknowns` and
`max_semantic_unknowns_per_file` (CLI: `--max-semantic-unknowns` and
`--max-semantic-unknowns-per-file`), each from 1 to 1000000. The parent offers the
remaining capacity to the worker, and rejects excess records before adoption.
Unknown admission happens before canonical identity and provenance construction.
When capacity is exhausted, coverage is partial and truncated. Coverage lists
both effective unknown ceilings, `max_semantic_unknowns` and
`max_semantic_unknowns_per_file`. The limitation names only the ceiling that
stopped retention. `coverage.omitted_unknown_occurrences` counts examined
emission occurrences by family and reason, including repeated emissions. It does
not claim distinct discarded identities or count frontiers in unexamined source.
Omitted unknown provenance and IDs are not retained. Existing source, node,
execution and memory limits continue to determine examined coverage. Raising only
a node capacity does not raise the unknown capacity. Graphs without unknown
omissions omit this optional coverage field, preserving their existing identities.
These node and unknown capacities bound retained graph projection. Parsing,
static analysis and semantic fact construction precede that projection for an
admitted source. Lowering the capacities can reduce projection and retention
work, but does not bound upstream semantic-analysis time or memory. Select fewer
`source_paths` to reduce the sources analyzed; heap and deadline controls set
separate resource failure boundaries with the runtime-specific limits above.
While sources remain, the sequential worker reserves one next compact static
checkpoint at its 8 MiB bound with the same 6x retention expansion used at
admission. Earlier checkpoints are already charged to retained bytes; the
remaining inventory does not multiply the reserve. Streamed file transfers keep the default 512 MiB
allowance, and can grow with the parent's offered retention budget (8x byte
expansion), up to 2 GiB. Individual records remain bounded to 8 MiB. The parent
expansion), up to 2 GiB. Individual records remain bounded to 8 MiB. The 8 MiB
record ceiling and the 2 GiB file maximum have no runtime override, and raising
`max_heap_mb` does not raise them. The effective file ceiling follows the
parent's offered result budget. On Node, a larger CLI/MCP process heap
(`NODE_OPTIONS`, for example `--max-old-space-size=<MiB>`, after restarting that
process) can raise it up to 2 GiB. Selecting fewer `source_paths` can also
raise that effective allowance, because the reserve for remaining sources and
earlier retained results reduce the offered budget. Neither change raises the
record ceiling or the 2 GiB maximum. The parent
still independently checks actual bytes, record overhead, digests and source
identities before adopting results.
A source that exhausts the analyzer heap does not terminate the CLI/MCP
process. Completed static facts are retained, other files can still be analyzed,
process. On Node, the failure names `max_heap_mb` (CLI `--max-heap-mb`), the
effective mebibytes, the inclusive range 128 to 16384, and the 1024 MiB
default. A changed value applies on the next analysis, which starts a new
worker. It does not change a worker that is already running and does not
require restarting the REA server. On Bun, the same heap-exhaustion diagnostic
is not attributed to that V8 old-space limit, because Bun ignores the flag.
Raising `max_heap_mb` does not raise the parent result budget,
the transfer record ceiling, the 2 GiB transfer file maximum, the CLI/MCP
process heap, or fixed parser limits. The parent result budget follows that
process heap. On Node, set `NODE_OPTIONS` (for example `--max-old-space-size=<MiB>`)
in the environment that starts the CLI or MCP server, then restart that
process, to raise the parent heap and the result budget. A completed static
checkpoint that does not fit the parent's remaining heap names that parent
process heap. Increasing `max_heap_mb` does not raise it. On Node, the same
`NODE_OPTIONS` restart can raise the parent heap and admit the checkpoint.
Completed static facts are retained, other files can still be analyzed,
and unexamined semantic relationships remain explicit unknowns. The parent
also checks transfer expansion and accumulated results against its remaining
heap before accepting semantic facts. A resource-limited result has partial
@@ -611,6 +769,24 @@ URL targets before selecting a later package-local file; an unsupported bare
package or recursive alias keeps its unresolved outcome rather than selecting
a later fallback.
For a proven `createRequire(new URL(relativeFile, import.meta.url))` call,
relative `require()` edges use the selected filename's directory and retain the
source-relative `resolution_base_path` on the graph edge. This follows Node's
`createRequire(filename)` contract; the path is a loader-resolution fact and
does not change the source file or call-site Evidence identity. If the filename
cannot be proven, REA keeps the require target unresolved rather than silently
resolving it from the caller's directory.
Module-reference graph edges, including native-addon `loads` edges, retain
`properties.operation`: `load` identifies
an import or loader invocation that may execute the target; `resolve` identifies
a proven `require.resolve()` lookup that discovers a target without executing it.
Both retain the exact specifier, resolution mode, selected resolution base, and
call-site Evidence. Resolve-only edges do not propagate application ownership or
create executable-import gaps when their targets are missing. A modified
`.resolve` member remains an uncertain `load`, because its replacement may execute
code. These static facts do not establish that a call runs at runtime.
ESM relative module paths, legacy package subpaths without exports, and selected
package exports targets use URL suffix handling and one percent-decoding pass.
Bare specifiers retain their complete query/fragment text for exports key matching.
+14 -6
View File
@@ -129,12 +129,20 @@ equivalence remain unknown. No network fetching occurs.
## Resource policy and verification
Each operation uses one worker, a 1 GiB address-space ceiling and a 120-second
deadline; it accepts up to 64 MiB of total input across all selected files and
128 MiB/10000 entries of staged
output, with 8 MiB per diagnostic/report stream. Exceeding a limit is an explicit
failure with rollback. These limits apply per operation and do not claim aggregate
host containment across concurrent callers.
Each operation uses one worker, a 1 GiB address-space ceiling and a 120000 ms
deadline. That deadline has no runtime override: raising `analysis_timeout_ms`
or `max_heap_mb` does not raise it. An unchanged retry may hit the deadline
again. The operation accepts up to 64 MiB of total
input across all selected files and 128 MiB/10000 entries of staged output,
with 8 MiB per diagnostic/report stream. A `provenance.json` over that report
allowance names the fixed limit: this release has no runtime override, and
raising `max_heap_mb` or `analysis_timeout_ms` does not raise it. Malformed
reports stay distinct from that size refusal. Exceeding a limit is an explicit
failure with rollback. Selected inputs beyond the total input allowance return a
non-retryable `resource_constraint` with `resource: "file-size"` and
`reported_limits.maximum_input_bytes`/`remaining_input_bytes`; input that is not
valid UTF-8 is an `invalid_format` request issue. These limits apply per
operation and do not claim aggregate host containment across concurrent callers.
See [testing.md](testing.md#javascript-source-recovery) for the focused real
Wakaru CLI/MCP lane and its source-owned compiler fixtures. Other host platforms
+113
View File
@@ -0,0 +1,113 @@
# Static JavaScript route inventory
`analyze_javascript_application` and `analyze_web_bundle` retain a shared
`route_inventory` alongside their existing graph and endpoint observations.
The inventory answers which route patterns the supplied code suggests, where
each pattern came from, and which parts remain unknown. It never requests a
route, executes scripts, expands a sitemap, or establishes reachability.
Select a compact view in either MCP analysis call:
```json
{
"detail": "summary",
"view": {
"kind": "javascript-routes",
"collection": "routes",
"where": { "path_prefix": "/reports" },
"fields": ["path", "classification", "confidence"],
"limit": 20
}
}
```
Keep the original analysis inputs in that call. For the CLI, pass the same
selection as `--view '<JSON>' --detail summary --evidence_output routes.json`.
The complete Evidence bundle is written without overwriting existing files.
Unselected delivery retains the existing complete result. A web-bundle summary
requires an explicit route view; application summaries also support the existing
general summary when `view` is omitted.
Use the returned `normalized_result.parent_evidence_id` with
`inspect_analysis_view` on the same connection. The CLI's exported bundle can be
reopened with `inspect-evidence-bundle`; its `records` contain the complete
Evidence. To use `inspect_analysis_view`'s `evidence-file` source, extract the
selected parent record into a separate local Evidence JSON file (that source
accepts one Evidence record, not a bundle). Views do not reopen source files or
reconnect to a browser. Historical Evidence without `route_inventory` is
unmeasured, rather than an empty route set.
Collections are `summary`, `routes`, `occurrences`, `sources`, and `unknowns`.
All row collections support exact batch `where.ids`, field selection, `offset`,
and `limit`. An explicit empty filter array matches nothing. Routes also support
exact `paths`, literal `path_prefix`, `classifications`, `mechanisms`, and
`min_confidence`; occurrences support `route_ids`, `source_ids`, and `mechanisms`;
sources support `keys` and `statuses`; unknowns support `source_ids` and `kinds`.
Follow `coverage.next_offset` until `coverage.exhausted`; a page may stop early
at the 1 MiB serialized view budget. Oversized indivisible facts return a
resource error carrying the retained parent Evidence ID.
Pattern IDs bind the exact path. Source IDs bind the source key, path, original
URL, content digest and container digest. Occurrences identify a detector mechanism at a source location; one location
may support multiple mechanisms. `source_count` counts distinct sources. Lookup IDs use 128 bits of SHA-256;
full content and inventory digests remain SHA-256. Field selection always keeps
row identity and necessary provenance. The page's deduplicated `sources` table
contains the source descriptors referenced by returned rows. Retrieve all
occurrences by route ID when independent supporting evidence matters. Source
ranges use one-based lines and zero-based UTF-16 columns with exclusive ends;
filename-only discoveries have no invented AST range.
The shared parser handles literal router calls, nested route objects, typed
`_$` trees with explicitly inferred property-name fallbacks, Next App Router
page paths, and navigation targets. It follows stable lexical bindings and
keeps unresolved parents, mutations, spreads, and dynamic fields explicit.
A selected route-builder root owns its configuration even when mutation prevents
trusting its value. Ordinary wrapper keys do not become route ancestors. Missing
outer ancestors of unbound typed subtrees remain unknown rather than invented.
Generic Next runtime path constants require a top-level application segment
supported by structural or navigation candidates across the supplied sources.
This limits framework filesystem noise, while leaving unsupported candidates
unknown. The parser shares the existing AST traversal and lexical index.
[Next dynamic segments](https://nextjs.org/docs/app/api-reference/file-conventions/dynamic-routes)
map to `:id`, `:slug+`, or `:slug*`; groups and parallel slots do not contribute
URL segments. Intercepted routes remain unresolved. A prefix before `/_next/`
is an **inferred** routing base: [assetPrefix](https://nextjs.org/docs/app/api-reference/config/next-config-js/assetPrefix)
can differ from the routing base. Every affected occurrence keeps
`unprefixed_path`, `inferred_base_path`, and low confidence. `system` classification
is explicitly a path-name heuristic; `page` uses page-file or structured/typed configuration support. Name-only
`route`/`addRoute` calls remain `unknown` because they can also register server APIs.
Next page-source segments keep their filename spelling when converted to route
patterns. Next's [page-path conversion](https://github.com/vercel/next.js/blob/v16.4.2/packages/next/src/shared/lib/page-path/absolute-path-to-page.ts#L21-L39)
removes page extensions and the `index` tail without URI-decoding segments, so
ordinary percent sequences are not decoded: `%61` and `a` remain separate
candidates and a literal `%` is retained. Its [App pathname normalizer](https://github.com/vercel/next.js/blob/v16.4.2/packages/next/src/server/normalizers/built/app/app-pathname-normalizer.ts#L5-L18)
specifically applies the [underscore normalizer](https://github.com/vercel/next.js/blob/v16.4.2/packages/next/src/server/normalizers/underscore-normalizer.ts#L2-L9),
which maps `%5F` to `_`; private-folder checks apply to the source spelling
before that escape is mapped. When classifying a chunk URL, REA reads its
pathname, then applies these same Next-specific rules. This is static source-path
interpretation, not browser request-path decoding or reachability.
Known capture omissions remain inline in `summary.reported_capture_omissions`
and queryable as `unknowns` of kind `capture-omission`. Each keeps a stable gap ID,
its script-record/script-source unit, and the reported count (including `null`
for unknown). They do not invent missing script identities or source ranges.
Counts describe deduplicated patterns in the provided code sources. They are
not a denominator for complete application or capture coverage. In particular,
server-only, lazy, inaccessible, or unselected sources may be missing.
`coverage.application_routes` always remains `unknown`; parsing and candidate
enumeration have their own completeness. Static work and fact admission share
the existing 8 MiB per-source transfer ceiling, and the root inventory has an
8 MiB admission budget. Exhaustion preserves a prefix with explicit capacity;
the serialization owner also admits route facts within the actual space left by
legacy facts. `result-bytes` capacity preserves an earlier work-capacity reason;
if even that envelope cannot fit, the optional route facet remains unmeasured.
These are accounting bounds, not a claim that total process memory is 8 MiB.
Projection only reduces delivery, not analysis work. Retained immutable Evidence
can reuse validated tables through a weak cache with the snapshot's lifetime;
subject identity is checked on every query. No live state or source content is
cached as current. Use existing source selection and resource controls when
they match the investigation, keeping their partial coverage visible.
+21 -2
View File
@@ -105,11 +105,15 @@ The direct observations are:
- `Debugger.scriptParsed`, establishing that a bounded script location was
parsed in the observed execution context;
- `Debugger.scriptFailedToParse`, retained only as a bounded invalid-script
count;
count in `scripts.parse_failed_total`. A well-formed parse-failure
event is distinct from malformed protocol data and does not make loaded
script coverage unavailable. Older Evidence may omit this count;
- `Runtime.executionContextCreated`,
`Runtime.executionContextDestroyed`, and
`Runtime.executionContextsCleared`, establishing context lifecycle within
the capture window.
the capture window. A created or destroyed event whose context ID is
missing or not a safe integer adds no context record; it is retained only
as a count reported in `unknowns`.
The Inspector protocol does not directly expose require/import caller edges,
EventEmitter emissions or listener invocation, Electron IPC messages or
@@ -117,6 +121,12 @@ handlers, or script-unload events. Those facts remain explicit unknowns.
`scriptParsed` proves script presence, not the importing module, feature
execution, initialization order, or causality.
For an authorized file script, `location.file_path` is the verified filesystem
identity and optional script-item `reported_url` preserves the exact Inspector
URL. Script identity includes that reported URL, so query and fragment
variants of one file remain separate observations. Older Evidence may omit
`reported_url` and remains readable.
## Node and Electron roles
The caller declares one role: `node`, `electron-main`, `electron-preload`, or
@@ -150,6 +160,15 @@ and uncollected scripts, but scripts collected before attachment may be
missing. Absence from the observation window never proves that a script or
behavior does not occur.
Cancellation stops new target-file authorization after the current filesystem
operation settles. Cancellation diagnostics retain the examined target prefix;
unexamined targets remain unknown. Observation cancellation keeps its original
reason and any completed Evidence. Finalization waits for REA's bounded socket
close (up to two seconds for graceful and forced confirmation). A failed close
retains its exact transport owner and blocks replacement connections until
cleanup succeeds; CLI finalization and MCP shutdown also retry retained cleanup.
Closing REA's connection does not stop the caller-owned runtime.
## Static correlation
Pass the resulting observation Evidence and an
+13 -2
View File
@@ -57,7 +57,11 @@ are complete Evidence objects, not only evidence IDs:
`static_layers` requires exactly one `application` role and permits additional
`cache` and `assets` roles. A `file-root` mapping translates a canonical runtime
filesystem root to an artifact-relative prefix. A `url-prefix` mapping does the
same for one exact HTTP(S) origin and path prefix. Mappings cannot add CDP
same for one exact HTTP(S) origin and path prefix. Each URL path segment below
the prefix is percent-decoded separately: an encoded slash (`%2F`) stays inside
its segment and never names a nested artifact, so such a location, like one
with malformed or non-UTF-8 escapes, has no artifact path under that mapping.
An encoded backslash (`%5C`) remains a literal name byte. Mappings cannot add CDP
origins, broaden Electron roots, read files, or authorize source-map access;
they are caller-declared inference inputs applied only to already-authorized
locations.
@@ -106,7 +110,12 @@ nodes. REA evaluates compatible static candidates conservatively:
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.
remain unknown or truncated rather than becoming absence claims. A runtime
location with no automatic artifact-root or caller-declared mapping into
any layer, and no content match, is `unknown` with
`no-authorized-location-mapping`, with or without captured bytes; several
content matches remain `ambiguous` under rule 4. Only a mapped location can
be `unmatched` with `no-static-content-match`.
Every successful cross-layer `observed_as` edge has
`authority: cross-layer-reconciliation` and `state: inferred`. The underlying
@@ -128,6 +137,8 @@ classification:
- `unknown` covers incomplete captures, layers outside runtime scope, and
unresolved correspondence. Nodes outside a mapped artifact prefix stay
unknown even when another prefix in the same static layer was observed.
Nodes beneath a mapped artifact prefix also stay unknown when a runtime
script or worker under that mapping has no artifact path.
None of these states proves feature execution, initialization order,
reachability, or causality. In particular, `not-observed-in-capture` never means
+27 -14
View File
@@ -8,7 +8,16 @@ Start a JEB client that serves MCP and point REA at it:
- Default endpoint: `http://127.0.0.1:8425/mcp`. Override with `REA_JEB_MCP_URL` (absolute `http`/`https` URL with literal host `127.0.0.1` or `[::1]`; redirects, remote hosts, credentials, query strings, and fragments are rejected rather than silently dropped).
- In the JEB GUI client, start the MCP server from the _File_ menu (or via the VIBRE assistant).
- Headless, use JEB's documented programmatic route (`JebMcpServerInstance` from the JEB API) with the JEB client on your classpath; JEB 5.48's `-c` headless mode does not accept `--mcp` directly.
- Headless, the official Linux CE build `5.48.0.202610071539` supports `-c --mcp`. From the JEB installation directory, select JDK 21 or newer through `JAVA_HOME`, choose your target and bind explicitly to loopback:
```sh
"$JAVA_HOME/bin/java" -jar bin/app/jeb.jar -c --mcp --interface=127.0.0.1 \
--port=8425 --infile=/absolute/path/to/target
```
Complete the vendor's first-use EULA and license-key activation when prompted. The Linux audit used an isolated installation, a 1536 MiB Java heap and one active processor inside a separately bounded process family; JVM heap limits alone do not bound the whole family.
- JEB's documented programmatic route (`JebMcpServerInstance` from the JEB API) remains an alternative. The earlier licensed 5.48 headless verification used that route because its CLI did not accept `--mcp`. Check your installed build's `-c --help` and live tool catalog; the version number alone does not establish the launcher options or supported operations.
For setup registration, set `REA_JEB_MCP_URL` in the REA process environment
before running `rea setup`. The approved setup plan preserves the exact selected
@@ -19,19 +28,21 @@ JEB service is reachable or that a project is open; use the existing
MCP `binary_session` probes the live JEB service and its advertised tool catalog.
Availability is reported per operation: a reachable headless client can support
inspection and decompilation while refusing `open_jeb_project`. Catalog discovery
uses one 60-second deadline across connection and pagination; a responsive server
cannot keep discovery running indefinitely by returning more pages.
is one deadline across connection and pagination; a responsive server cannot keep
discovery running indefinitely by returning more pages.
Each upstream tool call has an absolute 60-second request deadline by default.
Set `REA_JEB_MCP_TIMEOUT_MS` to a positive decimal number of milliseconds to
select a different per-request budget, for example `REA_JEB_MCP_TIMEOUT_MS=300000`
for a five-minute decompilation. This budget starts after discovery and applies
to each individual `tools/call`; it does not extend the separate catalog deadline
or establish one end-to-end deadline across a whole CLI or MCP operation. A
request that reaches its budget returns a typed timeout, while caller
cancellation remains a cancellation.
Catalog discovery and each `tools/call` share `REA_JEB_MCP_TIMEOUT_MS`. Set it
in REA's launch environment to an integer from 1 to 2147483647 milliseconds,
for example `REA_JEB_MCP_TIMEOUT_MS=300000` for a five-minute budget. Omitted
means the pinned MCP client SDK default of 60000 ms (`@modelcontextprotocol/client`
2.3.1). Restart REA and reconnect to apply a change. Raising the setting raises
both phases. It does not make one end-to-end deadline across a whole CLI or MCP
operation: discovery and each `tools/call` each get that budget. A request that
reaches its budget returns a typed timeout naming the phase and the effective
milliseconds, while caller cancellation remains a cancellation. Allow execution,
cleanup, and delivery headroom in the outer MCP client deadline.
Verified against JEB 5.48.0 (Linux x64, licensed build) serving `jeb-mcp-server` 1.3.0 over streamable HTTP.
Earlier verification used JEB 5.48.0 (Linux x64, licensed build) serving `jeb-mcp-server` 1.3.0 over streamable HTTP. The official Linux x64 CE build `5.48.0.202610071539` was also verified through real CLI and production stdio MCP native-code workflows; see the [Linux CE audit](https://github.com/morluto/rea/blob/42e1a17d6e6c0487f17be0bae248553c4caba7f7/experiments/analysis-efficiency/jeb-linux-native.md) for measurements and limits. CE verification does not establish licensed/Demo Android decompilation or GUI behavior.
## Operations
@@ -42,16 +53,18 @@ Verified against JEB 5.48.0 (Linux x64, licensed build) serving `jeb-mcp-server`
| `list_jeb_units` / `list-jeb-units` | Page units by stable path and type (`filter`, `parent-unit-path`, `index`, `count`) |
| `decompile_jeb_item` / `decompile-jeb-item` | Decompile one type or method to engine pseudo-code |
All four are read-only with respect to the target file; only `open_jeb_project` changes engine-side project state, and Evidence records the endpoint, the caller-selected parameters, and the engine-reported digests. Mutation workflows the engine also offers (renames, comments, `save_project`, `close_project`) are intentionally not exposed yet.
All four preserve target-file bytes. `open_jeb_project` selects or creates an engine-side project; analysis queries can populate computed analysis within it. Evidence records the endpoint, caller-selected parameters, and available engine-reported digests. Mutation workflows the engine also offers (renames, comments, `save_project`, `close_project`) are intentionally not exposed yet.
**Engine coverage note (verified):** JEB 5.48.0 headless MCP instances do not advertise `open_project` — the target must be selected when the engine client starts (`--infile` on the programmatic launcher, or the GUI's file opening). REA preserves that refusal with the engine's reason instead of failing silently; `open_jeb_project` completes only against engine builds that advertise the tool, such as GUI clients.
**Engine coverage note:** The earlier licensed 5.48.0 headless verification did not advertise `open_project`; it selected the target when the engine client started. For that build, use `--infile` on the programmatic launcher or open the file in the GUI. The audited CE headless catalog also omitted `open_project`; the verified CLI route above selects its input at startup. Use `binary_session` to inspect the current engine's advertised operations. REA preserves an unavailable tool's refusal; `open_jeb_project` requires an engine that advertises `open_project`. The CE audit does not establish GUI project-opening support.
## Semantics
- Paths in `open_jeb_project` are resolved by the JEB process, not by REA; REA does not require the artifact to exist locally.
- The engine caps `list_units` pages at 100; a full page is reported as `coverage: "partial"` because more units may exist. An empty remainder is `complete` only when fewer units than requested were returned.
- When `decompile_jeb_item` omits `unit_path`, the engine implicitly selects the project's first code unit; REA reports `unit_path: null` and a limitation instead of guessing the effective unit.
- `item_address` is a JEB code-item reference, such as a value returned by the engine's `list_code_items`. Preserve that representation and select its code unit explicitly. For the audited CE SQLite unit, the exported method reference was `sqlite3_libversion_number`; using its raw ELF symbol address did not produce a successful decompilation. File offsets, ELF virtual addresses and engine item references have different semantics. REA currently exposes unit listing, while code-item discovery remains an upstream JEB operation.
- Engine refusals (`success: false` envelopes) are preserved as unsupported-target errors carrying the engine's own message, for example a nonexistent item address.
- Successfully parsed client information is reused for the lifetime of the MCP connection, including when the engine omits its version. Explicit client inspection and availability probes refresh it.
- An unreachable endpoint is a provider-availability failure with recovery guidance; REA does not retry or spawn the engine.
Real-engine verification lives in the JEB lane described in [testing.md](testing.md); mock transport tests do not establish real-engine compatibility.
+68 -16
View File
@@ -13,39 +13,53 @@ rea doctor --provider kuna
Select it explicitly with `--provider kuna`, MCP's `open_binary.provider_id: "kuna"`, or `REA_ANALYSIS_PROVIDER=kuna`. With Kuna and another provider configured for the same target, `auto` reports `ambiguous` instead of choosing one.
Ordinary readiness cancellation returns `cancelled` (CLI SIGINT/SIGTERM exits
130/143); an earlier recorded timeout or physical failure keeps its diagnostic.
Completed version, hash and script observations remain in `kuna_installation`
with `status: "partial"` and `completed_hash` when available. Typed errors carry
this in `details.diagnostics`; doctor cleanup checks carry it in their `details`.
Omitted fields remain unobserved; availability remains unknown until readiness
completes. Cleanup retries keep both the observations and the exact file owner.
| Setting | Default | Meaning |
| ----------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `REA_KUNA_COMMAND` | none (required) | Absolute path of the `kuna` executable. |
| `REA_KUNA_CACHE_DIR` | `~/Library/Caches/rea/kuna` (macOS), `$XDG_CACHE_HOME/rea/kuna` (Linux; `~/.cache/rea/kuna` when `XDG_CACHE_HOME` is unset or relative) | Absolute directory REA owns for project exports. Kuna's own spec cache also lives here, not in your Kuna cache. |
| `REA_KUNA_CACHE_MAX_BYTES` | 4 GiB | Size cap; least recently used exports are evicted above it. |
| `REA_KUNA_PROJECT_TIMEOUT_MS` | 30 minutes | Deadline for one project export. |
| `REA_KUNA_TIMEOUT_MS` | 120 s | Deadline for one single-function decompile. |
| `REA_KUNA_PROJECT_TIMEOUT_MS` | 30 minutes | Deadline for whole-program inventory/export, reference and string scans. |
| `REA_KUNA_TIMEOUT_MS` | 120 s | Deadline for one single-function decompile, and for the inventory run REA makes when the export ends without one. |
| `REA_KUNA_JOBS` | 1 | `decompile-project --jobs`. 1 is Kuna's reference answer; more jobs finish sooner but lose callee-first prototypes, and change about 40–60% of function bodies on the measured binaries. |
A single-function decompile, inventory, string, graph or xref run that exceeds its deadline fails as `provider_timeout`, naming the setting that owns the deadline and that raising it takes a restart. A project export that exceeds `REA_KUNA_PROJECT_TIMEOUT_MS` is an export failure instead: requests that need the export report `capability_unavailable` with the export's own retry guidance.
Windows is not supported yet. Supported targets are ELF and thin Mach-O executables for x86-64, AArch64 and ARM; PE and universal Mach-O are rejected with a typed reason.
## What Kuna answers
| Tool | Source |
| ----------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| `list_procedures`, `search_procedures`, `procedure_address` | Kuna's callable inventory (`kuna functions --json`), including import slots it does not decompile |
| `address_name` | Function entries, then the data labels Kuna's export assigns |
| `procedure_pseudo_code`, `batch_decompile` | The project export, or a single-function run while the export builds |
| `procedure_assembly` | The project export's labeled listing |
| `analyze_function` | Pseudocode and assembly from the export; callers, callees, references, comments, strings and CFG are declared unavailable |
| Tool | Source |
| ----------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| `list_procedures`, `search_procedures`, `procedure_address` | Kuna's callable inventory (`kuna functions --json`), including import slots it does not decompile |
| `address_name` | Function entries, then the data labels Kuna's export assigns |
| `procedure_pseudo_code`, `batch_decompile` | The project export, or a single-function run while the export builds |
| `procedure_assembly` | The project export's labeled listing |
| `procedure_callers`, `procedure_callees` | Call-kind edges from a lazy, body-free Kuna graph; synthetic import nodes are accepted by address |
| `list_strings`, `search_strings` | Typed string candidates from one cached, reference-free scan; exact-address, literal and ECMAScript regex selection |
| `xrefs` | Exact-address incoming reference sites, with forwarding aliases filtered explicitly |
| `analyze_function` | Pseudocode and assembly from the export, plus call relationships; typed references, comments, strings and CFG remain unavailable |
Cross-references, callers, callees and basic blocks are not available from this provider. Use another provider when you need them.
Typed procedure references, basic blocks, raw-memory tools, annotations and type recovery are not available from this provider. Use another provider for those operations.
## How answers are produced
The first analysis request in a session looks for this target's export in the cache. `list_documents` reads the selected path without initializing analysis. The export is keyed by the target's SHA-256, the Kuna executable's SHA-256, its version and `--jobs`.
The first text or assembly request in a session looks for this target's export in the cache. `list_documents` reads the selected path without initializing analysis. Inventory, function lookup, string queries, call relationships and xrefs do not start a C export. A data-name lookup starts it only when the address is absent from the function inventory. The export is keyed by the target's SHA-256, the Kuna executable's SHA-256, its version and `--jobs`.
- **Warm cache:** analysis reads reuse the verified export. A fresh server verifies its file digests; that cost scales with export size.
- **Warm cache:** analysis reads reuse the verified export. A fresh server verifies its file digests once, even for concurrent first requests; that cost scales with export size.
- **Cold cache:**
- REA starts `kuna decompile-project` in the background on a private copy of the target.
- Meanwhile, pseudocode comes from one `kuna decompile --addr` run, and listings from `kuna functions --json`.
- Requests for the same function share one run; it stops when its last waiting request is cancelled. Each run analyzes the whole image again in its own process, so one REA process runs at most as many at once as the host has cores beyond `REA_KUNA_JOBS` (at least one). Further requests wait in order, and a waiting request is answered from the export if it finishes first.
- Assembly and data names wait for the export; progress notifications report the wait every ten seconds.
- Meanwhile, pseudocode comes from one `kuna decompile --addr` run. Name and address lookups, and listings, use the inventory the export takes with `kuna functions --json` before it decompiles. The session runs `kuna functions` itself, under `REA_KUNA_TIMEOUT_MS`, only when no export is building (an inventory-only request does not start one) or the export ends without an inventory.
- Requests for the same function share one run; it stops when its last waiting request is cancelled. Each run analyzes the whole image again in its own process, so one REA process runs at most as many at once as the host has cores beyond `REA_KUNA_JOBS` (at least one). Further requests wait in order, and a waiting request is answered from the export if it finishes first. A run whose stop REA cannot prove keeps its slot until `close_binary` proves it stopped.
- The session keeps each completed single-function text, up to 16 MiB in all with the least recently used dropped first, and answers repeats from it until the export can. After a failed export that lasts for the session.
- Assembly and data names wait for the export; progress notifications report the wait every ten seconds. A session does not rebuild a failed export: requests that need it fail with `retryable: false`, the export's failure, its captured Kuna output and what to change before reopening the target. A setting such as `REA_KUNA_PROJECT_TIMEOUT_MS` or `REA_KUNA_JOBS` is read when REA starts, so changing it also needs a restart. Pseudocode keeps using single-function runs.
- A single-function answer says so in its limitations. It lacks the export's whole-program prototypes, so a later request can return different text for the same function.
Pseudocode, assembly, `analyze_function` and data-name answers record where their text came from in `raw_result.source`: `single-function`, or `project-export` with the cache directory. `procedure_pseudo_code` and `procedure_assembly` answers from the export also name the file, byte span and the file's SHA-256. Combined `analyze_function` answers retain both facet records in `raw_result.source.files`, each with its file, digest and byte span. Both texts come from one export; losing that project retries the complete report. Listings and procedure lookups carry no `raw_result`. Agents can open or search the export's `.c` and `.asm` there directly when that is cheaper than calling a tool per function. Text read that way is target-derived, like any decompiler output, but it bypasses the untrusted-content marker that REA's tool results carry.
@@ -54,10 +68,48 @@ The background export belongs to the session. `close_binary`, or the end of a CL
The assembly label index is scanned once per export reader. Cancelling a request stops its wait without interrupting other readers; closing the session aborts and drains the shared scan before releasing its files.
Regex procedure search uses ECMAScript Unicode matching in a cancellable worker with a five-second matching deadline. Literal matching remains the default. Cancellation or a matching deadline leaves the session available.
Regex procedure and string search uses ECMAScript Unicode matching in a cancellable worker with a matching deadline and a worker old-generation heap. Literal matching remains the default. Cancellation or a matching deadline leaves the session available. The deadline is `REA_REGEX_MATCHING_DEADLINE_MS`, a positive decimal integer from 1 to 2147483647 ms. Unset uses 5000 ms. A larger value is refused because Node schedules a delay past that signed 32-bit bound as 1 ms. Invalid values are configuration errors and do not fall back to the default. The setting is read from the environment that starts REA, so a change needs a restart. A deadline failure names the effective milliseconds, whether they came from the default or the setting, the default, and the maximum. The worker old-generation heap does not raise it. The worker heap request is `REA_REGEX_WORKER_HEAP_REQUEST_MIB`, a decimal integer from 7 to 16384 MiB. Unset uses 128 MiB. A request below 7 MiB aborts the worker during startup, and a larger value is refused. Invalid values are configuration errors and do not fall back to 128. The setting is read from the environment that starts REA, so a change needs a restart. A heap failure names the effective request, whether it came from the default or the setting, the default, the floor, and the maximum. A positive Node.js `--max-old-space-size` on the process that starts REA, including `NODE_OPTIONS`, overrides that request and sets the effective heap. `--max-old-space-size-percentage` takes precedence over that flag in either order. REA does not turn the percentage into a MiB, so the effective heap stays unestablished instead of the worker heap request or the absolute flag. The reported `NODE_OPTIONS` value is the process initial environment when that block can be read. A later edit to the environment is not the value Node applied, and an unreadable initial value leaves the effective heap unestablished. The launch Node options are the initial command line on Linux when that buffer still has a program entry, not a later edit to `process.execArgv`. A preload can clear that array after V8 has applied the flags. Setting `process.title` or `--title` overwrites the command line, so a buffer without a program entry leaves the effective heap unestablished. `--env-file` or `--env-file-if-exists` can supply `NODE_OPTIONS` when the initial environment has none. That file is not part of the initial environment block, so the effective heap stays unestablished instead of the worker heap request. An initial `NODE_OPTIONS` is still used, because Node does not let the file replace it. A positive `--max-heap-size`, including the underscore form and launch `NODE_OPTIONS` on Node versions that allow it, sets the total heap rather than the old generation and can be lower than the worker heap request. The effective old generation stays unestablished while that value is in effect. A zero or empty value clears it. `--experimental-config-file` or `--experimental-default-config-file` can set `nodeOptions`, including a heap percentage, from a file that is not read back. The effective heap stays unestablished when either flag is present, including when an absolute `--max-old-space-size` is also on the command line or in the recovered `NODE_OPTIONS`. Node accepts the config path as `=` or the next argument.
Single-function subprocess output and each cached source facet share a 16 MiB buffered-text capacity. Cached spans are checked before allocation; larger text remains in the local export and the error identifies the file and range. `analyze_function` initially returns the complete dossier, which can still be large; later retained views do not remove that first transfer. Prefer the individual text tools for a focused question.
## Strings
`list_strings` and `search_strings` share one lazy `kuna strings --json --encoding all --min-length 5 --termination any --no-xrefs` scan of the digest-checked private input. Skipping references avoids program lifting, a call graph and a C export. REA validates the input, scan policy, every row and the total before caching any facts. Encoding, character count, byte length and terminator facts must agree; supplementary UTF-8 characters count as Unicode scalars. Malformed output is an error, not an empty search. REA parses the JSON as Kuna writes it instead of buffering it, so the scan has no fixed size: Kuna writes about 290 bytes per row, 31 MB for a 20 MB Ghostty binary and 179 MB (611k rows) for an arm64 Firefox `XUL`. Rows are validated one at a time, and the first contradiction stops Kuna. Text REA does not keep is dropped unassembled; kept text and retained rows are admitted against the REA process's live heap headroom as they arrive; a refusal is a `memory` resource constraint with `retryable: false`, and closing other targets or starting REA with a larger Node.js heap makes room. String scans use the whole-program `REA_KUNA_PROJECT_TIMEOUT_MS` deadline; `REA_KUNA_TIMEOUT_MS` applies to single-function decompilation. The parsed inventory belongs to one selected target/executable profile and expires on session close; Kuna diagnostics beyond their 8 MiB buffer are retained as a failure until close, while other failures are not cached.
Results keep exact u64 virtual addresses, decoded text, native encoding, terminator status and byte length including any terminator. Kuna scans mapped sections or segments for runs of at least five characters; unterminated fragments can include instruction bytes. `all` includes ASCII, UTF-8 and the engine's Latin-range UTF-16LE reading. Other text, including short strings, non-Latin wide text and UTF-16BE, can remain undiscovered. An empty result does not prove absence.
Use `search_strings` for the matching rows or `list_strings` with an exact address for that location. The validated inventory is sorted by numeric address and encoding. Once cached, exact-address reads use binary search and return every encoding at that address without scanning the full inventory or retaining another lookup index. Literal and regex matching run in REA, preserving the existing case and ECMAScript Unicode semantics instead of substituting Kuna's different regex dialect. Procedure and string regex queries share one worker slot per session. Input projections are materialized only after admission, so queued queries retain no per-query inventory arrays. Close cancels and drains matching. Native metadata queries also share one admission slot, including references; a failed stop retains admission until cleanup is proven.
`raw_result.strings` reports the source JSON digest/size, scanned scope, extraction policy, complete inventory count and selected count without copying unrequested text into the answer. Omit the address from `list_strings` to retrieve the complete inventory as Evidence; normal session retention and bundle export apply, including transport-capacity errors for oversized inline replies. These operations preserve their complete-result contracts and do not add paging. Function-referenced string facets in `analyze_function` remain unavailable; call `xrefs` explicitly when references to one returned string are needed.
## Calls and references
The first call query runs `kuna decompile-graph --functions ''`, admitting no C or assembly bodies. One validated graph and forward/reverse indexes serve later call queries and existing `get_call_graph` / `trace_call_path` workflows. Numeric u64 addresses are parsed from their original JSON tokens. Only `call` edges become callers/callees; jumps and data references retain their distinct meaning. Graph counts, edge endpoints and ordering are validated before any relationship is returned. A forwarding address may be an instruction inside a function, so it is retained separately and never promoted to a function node or call edge. Reported indirect calls make affected relationship coverage partial; no target is invented for them.
`xrefs` runs native `xrefs --to ADDRESS --json` on demand. Kuna unifies forwarding aliases and suppresses edges internal to their class; REA filters to the requested address and records the excluded-row count. Duplicate source sites appear once. An empty result cannot establish the absence of references. Addresses are explicit u64 values or exact, unambiguous procedure names; Kuna has no cursor.
REA parses the graph as Kuna writes it, like the string scan, so the graph has no fixed size: a 53 MB Raycast binary produces 157 MB (180k functions, 773k edges), which REA retains in about 93 MB after a whole-program walk of about a minute. Unrequested bodies are refused before any of their text is assembled, and edges are indexed as they arrive, so retained rows and their indexes are admitted against live heap headroom. Xref JSON admits at most 16 MiB before parsing. Output or Kuna diagnostics beyond a buffer are reported as a `memory` resource constraint with `retryable: false`, because the same target and executable produce the same output. The session remembers such a graph failure, so later call queries fail at once instead of repeating the whole-program walk. The graph is cached for the selected target and executable profile within one session. Xref reuse is an LRU charged by source bytes plus metadata, limited to 16 MiB and 128 entries; at most eight distinct native xref queries may be pending. Reference commands run one at a time per session and share the provider's foreground budget with single-function decompiles. An unproven process stop retains its admission slots until cleanup succeeds. Concurrent callers share work, individual cancellation releases only that waiter, and the last departing waiter cancels its producer and receives any unproven cleanup. Session close aborts and drains producers, releases parsed caches and retries any retained process/file cleanup.
Evidence includes only selected addresses or dossier neighbors, the producing JSON's SHA-256/byte count, coverage and relevant function facts; unrequested program-wide JSON and instruction text are omitted. Reference caches are in memory and expire on close: a fresh CLI process or MCP server rebuilds them. A first call query therefore costs a whole-program reference walk even when a C export is warm. These operations do not establish full Ghidra/IDA feature parity.
Failures from shared installation or inventory work identify the operation the
caller requested. `diagnostics.producer_operation` retains the internal producer
name when it differs. Caller-specific failures keep the producer's typed outcome,
captured output, partial facts and live cleanup dependencies; cleanup retry
updates every projection rather than preserving an independent stale marker.
Installation acquisition is shared, with independently cancellable caller waits.
The final departing caller stops the probe and joins its cleanup; session close
also stops and joins acquisition. A cleanly cancelled acquisition can be retried,
while a retained cleanup failure blocks replacement until its owner is closed.
Shared string and reference queries preserve caller cancellation reasons and
join the final departing caller's command cleanup. A command cleanup error keeps
its typed primary failure and captured output. Its live resource list follows
both process and workspace ownership: proving process exit does not prove that
the private workspace was removed, and retry updates earlier caller errors.
If workspace setup fails, an unsuccessful rollback retains the original target
or read failure together with its cleanup owner. Replacement stays blocked until
that rollback is resolved; a clean rollback permits another setup attempt.
## Provenance and limitations
- Addresses and bytes are Kuna's observations of the image. Procedure entries and extents are its derivations: extents are upper bounds clipped at the next entry, and Kuna can split one function at an entry its own analysis infers. Pseudocode is its reconstruction, never original source.
+190 -13
View File
@@ -51,6 +51,11 @@ This keeps the complete source in the session and returns a distinct linked
evidence, metadata status, module/MVID, assembly, frameworks, returned-row counts,
coverage/issues and limitations. No assembly is loaded or executed.
ECMA-335 permits empty metadata streams to be omitted. An absent `#Blob`
stream therefore does not prevent module or assembly identity from being read
when all blob indexes are zero; a nonzero reference to that missing heap is
reported as a missing-stream issue.
Use `normalized_result.parent_evidence_id` to page original rows:
```json
@@ -85,7 +90,7 @@ rejects selected summary before analysis; complete delivery remains available.
Complete `inspect_managed_members` delivery always includes the assembly-wide
`member_refs` and `exported_types`, which `type_names` does not narrow, so even
one selected or unmatched type can return megabytes. Select
one selected or unmatched type can return megabytes. In MCP, select
`{"path":"/absolute/path/Assembly.dll","type_names":["Namespace.Type"],"detail":"summary"}`
to keep the complete source in the session and receive identity, metadata
status, the selection with its unmatched names, coverage/issues, the separate
@@ -115,13 +120,58 @@ literal pool's own coverage. Older Evidence that never examined exported types
or user strings reports a null count and refuses those pages rather than
returning an empty one.
#### CLI summary and saved member rows
The CLI accepts the same `complete` and `summary` detail choices. Omitted or
`--detail complete` returns complete Evidence. To receive a summary, choose a
new Evidence bundle destination so its original complete parent remains
available after the command exits:
```bash
rea inspect-managed-members /absolute/path/Assembly.dll --type Namespace.Type \
--detail summary --evidence-bundle /absolute/path/evidence/members.json --json
```
`--evidence-bundle` writes the complete original Evidence in REA's canonical
bundle format. Its parent directory must exist, and it never replaces an
existing path. Relative paths resolve against the command's working directory.
The option also works with complete delivery when a saved bundle is useful.
Summary without a destination is rejected before analysis. A publication failure
returns a typed error with the completed Evidence under
`details.partial_observation`; it does not report successful summary delivery.
Cancellation before publication prevents the new output. Once atomic
publication has started, it can complete despite cancellation; a cancelled
delivery then retains the committed path and byte count under
`details.diagnostics.evidence_bundle`.
Copy `normalized_result.parent_evidence_id` from the summary into the
`source.evidence_id` field of the `managed-members` request above, then save that
small request as `members-page.json` and run:
```bash
rea inspect-analysis-view /absolute/path/evidence/members-page.json \
--evidence-bundle /absolute/path/evidence/members.json --json
```
The view command authenticates the selected bundle and resolves that exact
parent ID. It does not reopen the assembly, so the original target may have
been moved or removed. Subsequent calls can select another collection or use
the returned `coverage.next_offset` to continue the same collection. An inline
source already includes its parent and cannot also select `--evidence-bundle`.
Summary changes delivery, while type selection still controls decoding and the
same inspection resource limits apply. Each separate CLI page reads and
authenticates the saved bundle; the full source is retained on disk.
## Shipped scope
The canonical parser admits PE/CLI bytes and their metadata/CIL without loading
an assembly. It reports observed implementation markers and unavailable facts;
it does not unpack .NET single-file hosts, decode IL2CPP metadata, or infer a
NativeAOT identity from an ordinary native PE. Inputs without admitted CLI
metadata do not become managed assemblies through naming or routing guesses.
it does not unpack .NET single-file hosts or infer a NativeAOT identity from an
ordinary native PE. It does not decode IL2CPP; use
[IL2CPP metadata](il2cpp-metadata.md) for the image, type and method names in a
Unity `global-metadata.dat`. Inputs without admitted CLI metadata do not
become managed assemblies through naming or routing guesses.
Inspect separately obtained components explicitly and preserve their identities.
A valid implementation marker establishes a candidate native boundary, not
@@ -132,6 +182,9 @@ declares API compatibility across multiple .NET implementations. REA preserves
that target in `target_frameworks` and leaves `runtime_family` as `unknown`
unless independent markers identify a runtime family, such as a ReadyToRun
signature.
When ReadyToRun is observed, `runtime_family` remains `modern-dotnet` even if
Unity or target-framework hints are also present; the separate hint evidence
is retained in the classification evidence list.
### Resource admission
@@ -155,8 +208,19 @@ rather than interpreted according to section order.
### Selected member inspection
When whole-assembly member decoding reaches the representation budget, select
known declaring types before their signatures and CIL bodies are decoded:
Member inspection admits type names, tokens, list ranges/counts, nesting facts,
module identity and their output representation before decoding members. If
later decoding or projection reaches the 64 MiB representation budget, the
result retains this type inventory with `coverage.state: "partial"` and the
original `resource-limit` coordinate. It does not return partially admitted
member collections: empty fields, methods, references and edges are unexamined,
not proof of absence; exported types, user strings and signature-resolution
attestation remain null. Summary and retained pages keep this partial coverage.
The capacity limit is unchanged. If metadata or the type inventory itself cannot
be admitted, the result remains unavailable.
Use the returned `types[].full_name` values to select declaring types before
their signatures and CIL bodies are decoded:
```bash
rea inspect-managed-members /absolute/path/Assembly.dll --type Example.Parser
@@ -186,7 +250,21 @@ remain an assembly-wide inventory, so output size is not guaranteed to be small.
Coverage describes the selected members. Comparisons leave members outside the
opposite observation's selection unknown, and application graphs report their
selected scope as partial. Neither makes an absence claim about the whole
assembly. Older member Evidence without `selection` remains readable.
assembly. Older member Evidence without `selection` remains readable. The
`method_rows` interval records the examined zero-based MethodDef offset, row
count and full table total before type selection. A complete observation can
cover only that interval. Comparison keeps omissions from a method page unknown:
build-local row positions cannot prove member absence across builds. Historical
Evidence without this interval also cannot establish method absence; observed
pairs and field inventory comparisons remain available.
For MCP callers, request `detail: "summary"` and page the `types` collection
using the returned parent Evidence ID. Select one or more returned full names
in a subsequent `inspect_managed_members` request. Selection is a recovery path,
not a guarantee: a single type or the assembly-wide member references can still
exhaust the representation budget. A successful command exit means the Evidence
was returned; inspect `coverage.state` and `coverage.issues` before treating any
collection as complete.
### User strings
@@ -247,6 +325,19 @@ native/runtime questions retain their own authority and coverage.
## Member comparison
Call edges and field accesses retain `il_offset` as their instruction site within
the caller's build-local method token and artifact identity. Their
`target_identity_sha256` combines the existing enclosing-type nominal digest,
member name and resolved decoded signature. Comparison joins edges to their
instruction sites rather than array positions and compares these identities;
display labels alone cannot establish equal targets. Same-named nested types
under different enclosing types and different overload signatures stay distinct.
This is nominal metadata identity, not assembly resolution or runtime binding.
Historical edges without a site or identity, unresolved/cyclic type scopes,
TypeSpec owners and unresolved MethodSpec instantiations retain unknown target
equivalence. Valid MethodSpecs combine the base member identity with the
resolved instantiation signature, preserving generic argument distinctions.
Names and semantic labels do not establish durable member identity. Comparison
preserves observed differences, structural inferences, and unknowns separately.
@@ -322,6 +413,24 @@ constants, and bounded shape context. It reports all candidates at the winning
score and remains ambiguous when the evidence does not distinguish them. The
digest does not itself remap metadata tokens.
Matched methods also compare aligned references. `call-shape` and
`field-shape` report opcode and operand-kind differences; `call-target` and
`field-target` report differences in the observed target declaring type or
name from `call_edges` and `field_accesses`. Target tokens remain build-local
and are not compared across artifacts. Missing or unaligned edges and missing
target names leave that facet unknown; equal names do not establish overload
identity or runtime dispatch. `exception-shape` compares clause flags and
ranges. `exception-target` compares the nested nominal catch-type name digest;
for resolved TypeDefs it also compares the enclosing assembly identity. A
different known name or assembly scope establishes a change. Equal TypeRef
names remain unknown because their resolution scope is not resolved here.
Exception tokens remain in the source facts but are not cross-build identity;
an unresolved catch type leaves that facet unknown. The method body retains the
display `class_type`, nested `class_type_name_sha256`, and whether its local
token identifies a TypeDef. A TypeDef digest alone does not prove identity
without matching assembly identity. Missing assembly identity and equal TypeRef
names leave the comparison unknown.
Tokens are always remapped through observed structure. A caller cannot carry
`0x06001234` into a new MVID and assume it names the same method.
@@ -352,7 +461,7 @@ install it or its toolchain. The [NativeAOT guide](ghidra-nativeaot.md) defines
these routes, their layout/host restrictions, derived-memory provenance, and
verification coverage.
REA's experimental Windows Ghidra boundary admits native x86 and x86-64 PE
REA's experimental Windows Ghidra boundary admits native x86, x86-64, ARMNT (Thumb-2) and ARM64 PE
applications and DLLs for static analysis. For x64 PE NativeAOT, the read-only
parser also runs there; the [NativeAOT guide](ghidra-nativeaot.md) lists what a
Windows host covers.
@@ -372,7 +481,20 @@ point identities with supplied native export or function evidence. Name/module
matches establish declaration links; they do not establish that a call executed.
When either side lacks a usable module identity, a matching name remains
unresolved and retains the candidate evidence; REA reports a module mismatch
only when both identities are available and differ.
only when both identities are available and their format-specific comparison
establishes a difference. ELF module names retain case, PE module names compare
case-insensitively, and a case-only match for Mach-O or an unknown format stays
unresolved because the available Evidence does not establish case behavior.
For PE imports, `.dll`, `.exe`, a trailing period, and absolute paths suppress
appending `.dll`; other names may also be tried with `.dll`. Unix loaders use
the platform suffix (`.so` or `.dylib`) and `lib` prefix variations. A suffix
already present at the end of the name or before a period still gets an
appended-suffix variation, so Linux can match `nativedep.so.6` with
`libnativedep.so.6`. The `lib` prefix is omitted when the name contains `/`, and
absolute paths are used as supplied. POSIX paths keep backslashes as literal
characters. These candidates follow [Microsoft's native library loading rules](https://learn.microsoft.com/en-us/dotnet/standard/native-interop/native-library-loading).
These filename rules compare declared and observed identities; they do not
establish which library the runtime loaded.
Metadata tokens are not native addresses. Native body mapping for C++/CLI,
ReadyToRun, NativeAOT, and IL2CPP requires separately verified format and provider
@@ -383,6 +505,32 @@ Declared import inventory supports a bounded positive claim. Its absence does
not exclude dynamic resolution, generated code, protected code, native helpers,
or server-side behavior.
## Application graph relations
`project_managed_application_graph` turns member Evidence into `contains`
edges for declarations, `imports` edges for P/Invoke declarations, and two
relation kinds read from method bodies:
- `calls` links two `managed-method` nodes when a `call`, `callvirt`, `newobj`
or `jmp` site in the caller targets a MethodDef in the inspected module.
- `accesses` links a `managed-method` node to a `managed-field` node for each
field it loads, stores or takes the address of.
Each edge stands for every site between one source and one target. Its
properties carry `site_count` and per-opcode `opcodes` counts; `accesses`
edges also list `access` as `read`, `write` and/or `address`. Edge evidence
links the member Evidence.
Some sites are counted rather than projected: calls to MemberRefs (members of
other assemblies or of generic type instantiations), generic method
instantiations (`MethodSpec`), unknown targets, `ldftn`/`ldvirtftn` function
pointer loads, recursive self-calls (the graph has no self-loops), MemberRef
fields, and sites whose method or field node is absent, for example under a
type selection. Limitations name each counted class, and graph coverage is
`partial` with `omitted_count` equal to the omitted sites when nothing else is
incomplete. A missing `calls` edge is therefore not evidence that a method is
never called.
## Evidence record shape
Member comparison, application graph projection, and managed/native verification
@@ -416,6 +564,10 @@ preserving readable member and attribute facts.
The retained inverse indexes are admitted against the existing representation
budget before allocation.
Custom-attribute `type_name` uses `Outer+Nested` when its local TypeDef or
TypeRef enclosing chain is readable. It remains `null` when the constructor's
type or required enclosing facts cannot be resolved.
`exported_types` reports ExportedType rows as declared. Facade assemblies such
as .NET `System.Runtime.dll` provide their types this way: `forwarder: true`
marks a type forwarder, and `implementation_token` names the AssemblyRef, File,
@@ -429,6 +581,19 @@ CLI `#GUID` heap values retain their exact 16-byte GUID text. REA does not impos
RFC 4122 UUID version or variant bits on MVID, EncId, or EncBaseId because
ECMA-335 metadata does not require those bit patterns. Tokens and RVAs are
build-local coordinates, not durable cross-build identities or native addresses.
Field and method signatures validate embedded TypeDefOrRefOrSpec operands
against the artifact's metadata tables. An invalid operand leaves the member
name and raw signature digest available while marking that signature malformed
and coverage partial.
A fat method header's nonzero LocalVarSigTok must name an existing StandAloneSig
row whose blob is a structurally valid LocalVarSig. REA validates its local
types and exact blob coverage but does not project their identities. An invalid
token or malformed signature leaves decoded CIL anchors intact but marks the
body malformed and coverage partial. Unsupported local type encodings retain
the readable body with partial coverage. Zero continues to mean no locals
signature. Comparison detects whether a locals declaration is present, but raw
table tokens are not cross-build identities and local type contents are not
compared.
## Decoded-CIL fingerprint
@@ -454,6 +619,10 @@ Tuple operands use these exact projections:
- signed integer and variable operands: decimal strings; floating-point
operands: JavaScript's `String` representation of the decoded IEEE value.
For `ldtoken` MemberRef operands, `operand_kind` follows the parsed member
signature (`field` or `method`); if that signature does not establish the kind,
the anchor keeps the generic `token` classification.
Opcode names retain short/long forms and prefix opcodes. This makes the value a
reproducible decoded-tuple fingerprint, not a canonical semantic CIL identity:
it does not resolve tokens across MVIDs, does not commit full control flow or
@@ -461,7 +630,8 @@ exception semantics, and does not replace the raw CIL digest. Cross-build
comparison requires equal raw CIL digests as well for exact CIL/signature
matches and reports ambiguity when structural evidence is insufficient.
For example, a decoded `nop; ret` body serializes exactly as
For example, a present body (including any valid declared locals signature)
whose IL is `nop; ret` serializes exactly as
`[["nop","none",null],["ret","none",null]]` and has digest
`5e5fad7741cb44bca3a4f045546b7449990da343f612f5f34c0ca30e9eee0636`.
This vector specifies the tuple order, `null` handling, absence of whitespace,
@@ -620,6 +790,13 @@ which managed methods executed or how the CLR resolved them.
Managed member comparisons report a `metadata` dimension when an observed
MethodDef flags or implementation flags value differs, including accessibility
or synchronization changes. This is separate from the CIL/signature matching
tiers: identical instructions do not establish unchanged method metadata. The
reported difference is static metadata evidence, not proof of runtime behavior.
or synchronization changes. Observed method-body `init_locals` and `max_stack`
differences also contribute to `metadata`, even when CIL decoding is incomplete.
For complete bodies, presence or absence of a locals signature also contributes;
the locals table token is build-local and is not compared. Local type contents
are not currently compared, so this facet does not establish equivalent locals
declarations.
An unavailable header value keeps body coverage unknown rather than establishing
equality or a difference. These comparisons are separate from the CIL/signature
matching tiers: identical instructions do not establish unchanged method metadata.
The reported difference is static metadata evidence, not proof of runtime behavior.
+462 -29
View File
@@ -1,8 +1,13 @@
# MCP runtime contracts
For pairing native functions before comparing selected dossiers, see
[native function matching](native-function-matching.md). It operates on retained
inventories, preserves aliases/unknowns and exposes selected fact pages.
Use this reference to call REA tools and interpret their replies on one MCP
connection. Start with [tool results](#tool-results), [retained Evidence](#retained-application-evidence-inputs),
and [session discovery](#identity-and-discovery). See [cancellation](#progress-and-cancellation)
and [session discovery](#identity-and-discovery). See [transports](#transports)
for the opt-in HTTP listener, [cancellation](#progress-and-cancellation)
for partial-result recovery and [first-query deadlines](#ghidra-first-query-deadlines-and-recovery)
when a cold provider exceeds the client's wait.
@@ -21,6 +26,14 @@ directly to `compare_functions`, and `inspect_artifact` Evidence to
`compare_artifacts`. Use `get_evidence_bundle` when the task needs broader
retained session history or an explicit bundle for transfer.
Ghidra inventories and function replies use `raw_result: null` when their facts
are already preserved in `normalized_result`, limitations, locations and
relationship coverage. Distinct overview details, measured load-image
observations, file-offset selection roles and database types supplemented with
derived metadata keep their upstream representation. Read function results from
`normalized_result`; older retained Evidence keeps its original representation
and identity.
Artifact inventories declare `coverage.scope` (`root` or `container-entries`)
and `coverage.status` (`complete` or `unknown`) independently of diagnostic
wording. Comparison requires matching scopes, complete graph members and complete
@@ -31,12 +44,18 @@ root deltas. Missing graph members yield `truncated`; unknown traversal coverage
or unavailable occurrence hashes retain uncertainty. Integrity mismatches remain
contradictions. Static artifact equality does not establish unchanged runtime
behavior.
Source-map relation deltas include the content identity of the mapped source,
so an unchanged map file is reported when its target source bytes change.
Exhausted traversals with deliberately omitted filesystem entries retain
`unknown` coverage and identify each omitted path/type in `limitations`; FIFO,
socket and device entries are not opened. Case-distinct archive members remain
separate observations. Their case-insensitive materialization diagnostic does
not reduce traversal coverage when both spellings were read.
PyInstaller inventory also preserves distinct table rows that share a logical
path, including decoded children beneath a colliding file/directory path.
Inventory coverage describes observed archive facts; extraction still refuses
destinations where those paths cannot coexist.
Current inventory Evidence requires scoped `coverage` with a `complete` or
`unknown` status. Consumers use this producer-owned fact and reject records
@@ -58,9 +77,21 @@ Call comparison additionally retains the Evidence relationship coverage
checks. Ghidra and Hopper declare their observed dossier facets and complete
provider-observed caller/callee sets, including empty sets. Missing decompiler
output remains unavailable; complete call coverage does not claim every runtime
target. Hopper's unavailable reference-kind metadata and reported unresolved
target. When Ghidra's decompiler fails on one function, its dossier is still
returned: `pseudocode`, `native_api` and `native_value_flow` are unavailable
with the decompiler's failure as the reason, while the listing-backed facets
are collected as usual. These failed decompiler observations and workflows
derived from them retain Evidence but skip snapshot reuse, so a later request
can observe backend recovery; healthy dossiers keep their existing cache policy.
`procedure_pseudo_code` still fails for that function. Hopper's unavailable reference-kind metadata and reported unresolved
calls still prevent equality conclusions for those dimensions. Analysis profiles
version these declarations so older snapshots are not reused as current results.
For `strings_names`, a string marked with `decoding.available: false` keeps its
provider display text in the source Evidence, but that text is not compared as
decoded content. If no independently known string, name, or literal fact differs,
the dimension remains `unknown` with the decoding reason. Known content and typed
string-object differences can establish a change; missing typed metadata remains
unknown, and `encoding_status` describes provenance rather than content.
IDA declares the facets its selected adapter cannot provide; an empty
unsupported array does not prove observed absence.
Evidence and bundle imports reject unsupported fields in their envelopes,
@@ -71,13 +102,35 @@ metadata in the supported JSON payloads (`parameters`, `raw_result`,
Tool execution failures return `isError: true` and the complete canonical
`{ "error": ... }` diagnostic as JSON in text `content`. Read that diagnostic
for the error code, target details, partial observations, and remediation.
for the error code, target details, partial observations, remediation, and the
optional bounded `recovery` next step. `recovery.kind=inspect_input_contract`
names the rejected operation: re-read that operation's advertised input schema
(call `tools/list` if the catalog may be stale), correct the listed fields, and
do not repeat the unchanged call. `recovery.kind=inspect_timeout_state` names
the operation and the REA server/provider deadline. Inspect partial Evidence
and reported cleanup state, then follow the returned remediation. Change a
request field or REA_* setting only when the returned remediation explicitly
recommends that change. Restart REA when that text says the value is read at
startup. A mention that a control does not raise the deadline is not a
recommendation to change it. When it names no setting, reduce supported work;
do not invent one. Raising only the MCP
client's deadline cannot extend this budget. These hints never include the
complete catalog or caller arguments.
Failures omit `structuredContent`, since each advertised `outputSchema`
describes successful results. Successful replies retain their schema-validated
structured data and matching text projection. Oversized errors are retained
as Evidence before delivery of a bounded diagnostic with an export reference;
if retention is unavailable or fails, the diagnostic reports that reason.
SDK-generated input validation failures are normalized into the same canonical
`invalid_request` text when the bounded diagnostic fits the response budget.
The complete field/type diagnostic is preserved when normalization fits the
response budget, including request-specific rejected fields and reasons. Oversized
SDK diagnostics keep the existing retained-Evidence recovery path. An unknown
tool remains an MCP JSON-RPC `-32602` error, but its message directs the caller
to `tools/list` and an advertised operation instead of repeating the unknown
name.
`get_call_graph` returns a `graph` keyed by traversal depth and a top-level
`limitations` array with deduplicated execution caveats. Each node keeps its
relationship coverage and read-specific `limitations`, alongside observed
@@ -92,8 +145,9 @@ unresolved relationship reads. Cancellation remains a tool error.
REA prepares complete MCP results within the pinned stdio client's 10 MiB
receive-buffer budget, including both text and structured representations and
room for the JSON-RPC envelope. Tool errors carry only their text
representation and `isError` flag, so only those count against the budget. Response budget settings are captured at startup;
room for the JSON-RPC envelope. Tool errors omit `structuredContent` from the
wire; their text, `isError`, provenance marker and negotiated protocol framing
count toward the wire budget. Response budget settings are captured at startup;
restart or recreate the server to apply changes. If a result cannot fit, REA returns
`resource_constraint` with `details.resource: "transport"` before constructing
a document-sized string. Analysis Evidence remains complete in the current
@@ -103,6 +157,52 @@ that reference for a summary, one section/module, or a stable page, or call
`export_evidence_bundle` with a destination path. Complete
bundle exports stream canonical JSON into an atomically published file.
On protocol revision `2026-07-28`, completed replies retain the SDK's
`resultType: "complete"`, including the replacement for an oversized error.
This marks completion of the MCP exchange; Evidence coverage, partial facts
and unknowns retain their separate meaning. Legacy replies keep their existing
framing.
`list_procedures`, `search_procedures`, `list_strings`, `search_strings` and
`list_names` accept `detail: "summary"`. Complete inline delivery remains the
default. Summary retains the original complete Evidence and returns its row
count, provider identity, analysis-profile digest, original query, parent Evidence
ID, limitations and explicit unknown discovery coverage. Delivery detail is excluded from provider arguments
and original Evidence identity.
Read selected rows with `inspect_analysis_view`:
```json
{
"source": {
"kind": "retained-evidence",
"evidence_id": "ev_<source digest>"
},
"view": {
"kind": "native-inventory",
"offset": 0,
"limit": 32,
"fields": ["value"]
}
}
```
Each row has `source_index` and `record`; parent Evidence ID plus source index
identifies same-address alternatives. Order is the original retained order.
Omit `fields` for complete records; a selected field list always retains
`record.address`. Selecting `value` also retains available `string` and
`decoding` qualifications, so native display text and unterminated fragments
remain distinguishable. Search tools apply their existing filters before retention.
Follow `coverage.next_offset`; exhaustion describes the retained list, not
complete discovery of all code or strings. An unbound source keeps
`artifact: null` and the derived Evidence keeps `subject: null`.
The CLI uses the same projector. For an original complete Evidence JSON file,
write `{"source":{"kind":"evidence-file","path":"/absolute/source.json"},"view":{"kind":"native-inventory","limit":32}}`
to `/absolute/view-request.json`, then run
`rea inspect-analysis-view /absolute/view-request.json --json`. This also reads
pre-summary source records without changing their identity; no live provider is started.
For oversized native function Evidence, `details.reported_limits.continuation`
contains a ready-to-call `inspect_analysis_view` request for the retained
pseudocode's first page. Use its `name` and `arguments` directly, then follow
@@ -111,6 +211,8 @@ limit is the view contract's default, not a guarantee that arbitrary source or
metadata fits; reduce the limit or export when needed. Continuations are
offered for completed `analyze_function` results only after session recording is
acknowledged; the complete result and its retention reference remain intact.
Oversized native inventories likewise include a ready-to-call first
`native-inventory` page. Reduce its limit or select fields when a row is large.
A broad follow-up or `get_evidence_bundle` can also exceed the response budget; exporting
preserves the complete session without sending it through a single MCP frame.
@@ -132,6 +234,12 @@ is null. `total` counts all matching heads; each page preserves complete records
in stable unknown ID order. Filters apply before paging. Pages reflect current
session state, so keep the unknown ledger unchanged while traversing it.
Automatically retained workflow questions include their immutable result
Evidence ID. Distinct results retain independent questions and source links;
repeating the same result does not create another unknown. Workflow Evidence
keeps the original residual question text. Explicit `record_unknown` requests
keep caller-selected identity and reject duplicate creation.
For a portable exported bundle, the CLI shares this projection:
`rea inspect-evidence-bundle '{"path":"/tmp/evidence.json","detail":"summary","filters":{"operation":"analyze_function"}}' --format json`.
The CLI validates and materializes that local bundle under its existing memory
@@ -142,11 +250,31 @@ The destination changes only when a complete export is atomically published.
Clients that explicitly configure a larger receive buffer can set the REA
server's `REA_MCP_MAX_RESPONSE_BYTES` environment variable to the same byte
count. This setting must be a safe decimal integer at least 10485760; REA
reserves 1024 bytes for the envelope. Raising it restores complete inline
delivery for responses that fit that buffer and Node's single-string limit.
It does not change the client's buffer, analysis coverage, or retained content.
Ordinary responses keep their existing complete result contract.
count. This setting must be a safe decimal integer at least 10485760 (10 MiB);
REA reserves 1024 bytes for the envelope. The value is read when the REA
process starts, so a change requires a restart and a matching client receive
buffer. Raising it restores complete inline delivery for responses that fit
that buffer. It does not raise REA's reserved single-string limit, which is
1024 UTF-16 code units below Node's single-string ceiling, and it does not
raise that Node ceiling. It does not change analysis coverage or retained
content. Ordinary responses keep their existing complete result contract.
### Error recovery and deadline ownership
Initialization instructions and the static JavaScript tool description teach
recovery before a host can discard a tool response. Invalid input calls should be corrected from the connected schema
(refresh `tools/list` only if stale), not retried unchanged. REA's returned
`provider_timeout` describes its server/provider deadline; provider-authored
remediation names the applicable controls and preserves partial Evidence and
cleanup uncertainty.
A host timeout without a REA result is different: execution and cleanup remain
unknown. Increasing an advertised `analysis_timeout_ms` cannot fix a shorter
client deadline. Configure the client's tool timeout with execution, cleanup
and delivery headroom, or report that change is needed when not authorized.
Session status does not establish that all target-free work stopped. Detailed
host settings and bounded retry guidance live in the shipped skill's
[recovery reference](https://github.com/morluto/rea/blob/main/.agents/skills/reverse-engineer-anything/references/connection-and-recovery.md#distinguish-client-and-server-deadlines).
### Untrusted target content
@@ -198,7 +326,9 @@ that complete Evidence in the session and returns the derived
`normalized_result.parent_evidence_id` and `evidence_links` name the retained
analysis, so later views and application workflows read it without repeating
analysis or moving the complete record through the response. A server without
session retention refuses summary detail with `capability_unavailable`.
session retention refuses summary detail with `capability_unavailable`;
`inspect_artifact`, `inspect_wasm_artifact`, `inspect_macho` and
`decode_interface_builder` refuse before any analysis starts.
For `inspect_artifact`, omission also keeps the original request parameters and
complete output shape. Delivery selection never reaches the inventory engine;
the complete inspection and its atomic inventory Evidence remain retained.
@@ -210,6 +340,21 @@ inferred Evidence whenever any group is templated, and derived Evidence only whe
no group was templated.
See [derived API surfaces](web-network-captures.md#derived-api-surface).
`inspect_analysis_view` also accepts `environment-manifest` for retained HAR capture
Evidence and `capture_browser_scenario` Evidence, projecting the primary origin's request lines into named tools with path
templates, parameters, observed query names, value-free body shapes and record
pointers, plus excluded groups and three fixed unknowns. Templates, parameters and
names are inferences; the Evidence is inferred whenever any tool or excluded group
is templated, and derived otherwise.
See [environment manifests](web-network-captures.md#environment-manifest).
`inspect_analysis_view` also accepts `environment-state` for the same parents,
extracting the initial state the recorded reads observed: entity types with an
identity field, entities, documents for non-entity reads, and a render
plan per read tool that reproduces its recordings from the state or names why it
cannot. Types and identity are inferences, so the Evidence is always inferred.
See [environment state](web-network-captures.md#environment-state).
`inspect_analysis_view` projects a caller-selected view of already completed
`inspect_binary_layout`, `analyze_javascript_application`, or `analyze_function`
Evidence. Native views select procedure, pseudocode, assembly, basic blocks,
@@ -228,10 +373,41 @@ application and semantic coverage. The result carries a digest of the selected
facts. Actual serialized size determines MCP transport admission: reduce the
page size or export retained Evidence if the view is too large.
`compare_source_to_bundle` accepts the committed historical graph inline or as
`reference: {"kind":"file","path":"/absolute/reference.json"}`. The shared
reader checks resource bounds and the graph's root commitment on every read. File-backed
comparison Evidence records `parameters.reference_source`: the exact
`requested_path` and the reader's absolute selected `path`, preserving POSIX
`..`/symlink semantics. Normalized comparison facts and mapping identities agree
with inline input; Evidence IDs distinguish caller-selected file origins.
Omitting `view` preserves the complete comparison. For a compact initial reply,
select `view: {"kind":"summary"}` or a `source-to-bundle` page; the session retains
the complete comparison before delivery. Later calls use `inspect_analysis_view`
with the returned `parent_evidence_id`.
A `source-to-bundle` view selects `collection: "mappings"` or `"candidates"` with
exact `source_paths`, `mapping_ids`, `statuses`, `fields`, `offset` and `limit`.
Omitted filters select all; empty arrays select none. The default mapping fields
include status, confidence, source digest/language and candidate/mapped counts.
Candidate defaults include kind, score and confidence; request `signals` for exact
supporting values. Mapping identity and `source_index` always remain; candidates
also retain `current_node_id`, `candidate_index` and whether they were actually
mapped. Indices refer to the complete comparison's item array and that item's
candidate array. All classifications are computed before selection. The original
`source_coverage` remains separate from page `coverage`; exhausting a page never
turns unknown correspondence into absence or proves behavioral equivalence.
The CLI accepts the same reference-file alternative (relative paths resolve from
the working directory). Save its complete `--json` Evidence, then use
`inspect-analysis-view` with an `evidence-file` source and the same view. Neither
adapter executes the target or repeats analysis for retained queries.
Completed `inspect_artifact` and atomic `inventory_artifact` Evidence also
support summary and inventory views. A summary reports the manifest's expected
counts separately from available record counts, observed entry kinds, inventory
Evidence IDs and inspection coverage. An inventory view selects `nodes`,
Evidence IDs, the inventory producer's traversal `source_coverage` and
inspection coverage. Inspection coverage is `partial` when that traversal
coverage is unknown. An inventory view selects `nodes`,
`occurrences`, `edges`, or `integrity_contradictions`, with `offset` and `limit`
defaulting to 0 and 64. For occurrences, optional `logical_paths` matches exact
recorded strings: omission selects all available records; `[]` selects none.
@@ -355,6 +531,17 @@ checking that a client received the full catalog. An operation absent from all
groups is missing from the complete inventory; it is not an implicit
unavailable entry.
`get_call_graph` and `trace_call_path` report `forward` and `backward` modes.
Each requires `procedure_address` plus `procedure_callees` or `procedure_callers`,
respectively. The tool is available when either traversal mode is supported;
`default_mode_available` describes the default forward direction. Read each mode's
missing operations and remediation before selecting a direction. These facts
describe traversal prerequisites, not relationship completeness or a guarantee
for every selector. A trace whose start and goal resolve to the same address can
finish without reading relationships, even when neither traversal mode is
available. Discovery does not block that direct invocation or replace its typed
partial/error outcomes.
The `effects` and `annotations` arrays list the true members of their fixed
vocabularies. A known member omitted from either array is false. This applies
only to these closed flag sets; it does not make an omitted tool, provider, mode,
@@ -393,15 +580,132 @@ concrete version, selection source, and complete analysis profile through
choices return typed selection details. A selected provider is never replaced
automatically after a runtime failure.
Every successful target transition allocates `binary_session.analysis_run.run_id`
before any provider startup. `process_lineage` is `not_observed` until a dynamic
provider starts, then becomes `snapshots` with every started provider's identity
`open_bundle_resources` selects a separate read-only app resource session
without resolving `CFBundleExecutable`, including when it is absent or
unresolvable. `binary_session` reports `session_kind: "bundle_resources"` and the
caller and canonical resource-root paths; it does not report a binary target or
deep-provider binding. `inspect_plist`, `decode_interface_builder`,
`inspect_asset_catalog`, and `inspect_keyed_archive` retain resource-root
provenance and use a null Evidence subject when executable identity is unknown.
Binary-only operations and binary analysis snapshots do not apply in this
session. `open_binary` can switch back to executable analysis, and
`close_binary` retires either session kind.
Each provider client receives `binary_session.analysis_run.run_id` before any
provider startup, including clients whose startup fails. `process_lineage` is
`not_observed` until a dynamic provider starts, then becomes `snapshots` with
every started provider's identity
and retained ownership observation. Each observation records `observed_at` and
is `unavailable` with a reason when ownership could not be revalidated, or
`verified` with launcher PID, parent PID, process group, and descendants observed
at that bounded check. These are historical snapshots, not live process
inventories, and do not claim that no short-lived descendant existed.
`binary_session.cleanup_pending` reports whether the session retains a provider
client, session file resource, or provider-candidate resource whose cleanup
has not succeeded. It can be true with
`open: false`; target retirement does not establish resource release. For a
retained provider client, health, activity, and lineage remain available under
its original `analysis_run.run_id` where the provider supplies them.
Successful provider cleanup clears the retained run;
a replacement client receives a fresh run ID. Follow the cleanup error's
reported resources and recovery action before reopening; a missing health
observation does not establish an idle provider.
File cleanup can also remain after snapshot publication or after profile
inspection fails or is cancelled before a provider client exists. If no client
remains, no provider run or health observation is available; follow the cleanup
error's reported resources.
## Transports
stdio is the default transport and the only one that client registrations,
`rea mcp doctor` and the MCP registry entry describe. `rea mcp --http [--host H]
[--port N]` is an opt-in alternative that serves the same catalog over the
streamable HTTP transport at `/mcp`; it replaces stdio for that process rather
than running beside it. Host and port come from the flags, then `MCP_HOST` and
`MCP_PORT`, then `127.0.0.1` and an ephemeral port. The bound URL is announced
as one JSON log line on stderr (`"msg":"MCP transport listening"` with `url`)
at every `REA_LOG_LEVEL`, because an ephemeral port is otherwise unreachable;
stdout stays unused.
This endpoint uses plain HTTP without TLS or client authentication. Use it only
within a trusted local or isolated environment; an explicit non-loopback bind
makes the same tools and active analysis state reachable at that address. It
does not provide separate users or independent target sessions. Standard client
registrations continue to use a dedicated stdio process.
The HTTP server is stateful and serves one client at a time on one server
instance. `initialize` returns an `Mcp-Session-Id`; `notifications/initialized`
is acknowledged with `202`; responses arrive as JSON or SSE frames; `DELETE
/mcp` with the session id ends the session with `200`. Ending a session is not
a server shutdown: the process keeps listening, the instance keeps its providers
and the shared active target, and the next client opens a fresh session against
them. A client ending or replacing a session cancels its running tool calls and
prompt completions, stops request-body reads and fences further message dispatch,
then waits up to ten seconds for request dispatch, handlers and cleanup to finish before reusing the
server. A timeout returns `503`; the old calls remain owned, and later requests
retry the drain rather than starting another session against those resources.
Requests that finish parsing or SDK dispatch after their session closed are
refused, and their response streams are cancelled. Tool callbacks whose
validation finishes after cancellation never start.
Body forwarding stops when SDK request processing ends, including early header
and size refusals, rather than remaining active until the session closes.
HTTP input/route dispatch is limited to eight concurrent requests by default,
shared by MCP and plain routes. Excess requests receive `503` with a closing
connection before their bodies are converted, read or cloned. An embedder can
select `maxConcurrentDispatches`; the SDK's per-body size limit still applies.
MCP responses with fully read or bodyless requests do not hold admission slots,
so an idle MCP SSE stream does not prevent later calls. Plain routes retain their
slots through response delivery and response-source cancellation cleanup. An
unread upload retains its slot until input finishes or either side closes,
even with a long response. Sequential keep-alive is supported; a peer pipelining
behind an unfinished response is disconnected before queued requests dispatch.
Concurrent MCP requests use separate connections.
This bounds concurrent input work, not total provider or process memory.
Provider-backed prompt completions use the same request owner and drain, so a
cancelled completion cannot keep using providers after handover or shutdown.
HTTP factories enable `RequestTrackingMcpServer` tracking so completion is tracked
even when cancellation suppresses the SDK's reply; stdio retains the SDK's
original callback behavior. Full server shutdown stops the listener and waits
for MCP work, plain-route handlers and response-source cancellation to settle
before running provider shutdown hooks or closing providers. Response producers
must finish provider work before closing their stream and expose asynchronous
disconnect cleanup through the stream's `cancel()` result. Cancellation does not
delay delivery of a HEAD reply; a caller retaining a sibling of a teed response
must consume or cancel that sibling before shutdown can finish. Failed response
cleanup remains owned, blocks further admission and prevents provider teardown.
The server continues cleanup
automatically when they finish, including after a client handover timed out.
All routes return `503` until the server factory succeeds; startup failure closes
the listener and joins its pending replies before rejecting the factory.
The newest client replaces an open session: an `initialize` that arrives
while a session is open ends that session instead of being refused, because a
client that exits without `DELETE` must not wedge the listener; the replaced
client's open streams are closed and its later requests are refused. Only an
`initialize` that carries the streamable headers (`Content-Type:
application/json` and an `Accept` naming both `application/json` and
`text/event-stream`) and passes the Host and Origin checks can replace a
session; any other sessionless POST goes to the live transport, which refuses
it without disturbing the session. Requests that carry no valid session id are
refused with `400` or `404` as the SDK transport defines. A bind is loopback by
address class, not spelling, and is classified from the address the listener
actually bound: any `127.0.0.0/8` address, `::1`, or an IPv4-mapped loopback.
Configured names, including `localhost` and its aliases, do not establish that
the bound address is local; names are added only after classifying the actual IP.
On such a bind, requests whose `Host` header is outside the SDK loopback allowlist plus
the configured name and the bound address are refused with `403`, and a browser
`Origin` outside that allowlist is refused the same way (a missing `Origin`
passes); a non-loopback bind relaxes both checks and logs the relaxation. A reply sent before a request body was read (a refused `Host` or `Origin`, an unknown route, the startup `503`) closes the connection after the reply, so an unread upload cannot keep a socket open. Request targets are taken verbatim as origin-form paths (`//host/path` is a path, not another origin); an absolute-form target, a target containing a raw fragment delimiter (`#`), or one whose path URL parsing would rewrite (dot segments such as `/public/../mcp`), is refused with `400`, and a method Fetch does not support with `405`. Percent-encoded `%23` remains valid query data. Only a lone `initialize` can replace an open session; a batch that carries one is left to the transport's refusal. Scoped
IPv6 literals (`fe80::1%eth0`) are refused before binding, because no URL can
name them. Listener errors reach the runtime's connection-loss handler only
after a successful bind; a bind failure prints the listen-failure message alone. Request bodies are bounded by the SDK's
4 MiB limit, and the response budget keeps its stdio-derived floor. The
listener ends on SIGINT or SIGTERM only, not on stdin EOF, and exits `130` or
`143` after cleanup settles. A signal that arrives while startup is still
running is recorded and acted on as soon as the listener and its providers are
owned, so startup never ends without cleanup; a second signal during startup
exits immediately.
## Progress and cancellation
REA accepts ordinary `tools/call` progress tokens. The numeric `progress` counts
@@ -419,6 +723,9 @@ Provider calls receive the request cancellation signal. Artifact traversal,
hashing, version comparisons, Hopper requests, and process capture
check the same signal. Cancellation is distinct from timeout. A cleanup failure
uses `cleanup_incomplete` and lists only the owned resource kinds that remain.
Once those owners confirm cleanup, composed failures recover their original
typed primary outcome and recovery guidance. Historical composition diagnostics
remain in `details.cleanup_history`, separate from the primary diagnostics.
An atomic Evidence bundle or snapshot write can publish its complete output
before staging cleanup fails. In that case, `details.published: true`,
`details.published_path`, and `details.bytes` retain the publication receipt.
@@ -428,12 +735,25 @@ Native call tracing and process capture retain available observations in
`details.partial_observation` on failure, including when cleanup succeeds.
The observation reports its partial coverage; cleanup details describe host
state separately from the execution failure.
For these tools, cancellation observed during result finalization or owned
cleanup also returns typed cancellation with the available observations.
A normal target exit does not establish request completion. Incomplete cleanup
still takes precedence and retains its resource diagnostics.
A process capture that fails during a finalization interval keeps every signal
attempt and its delivery result in `details.partial_observation`, with
`elapsed_ms` and `exit_code` null when no exit was observed.
Derived comparisons and reconstruction verification yield before computation
and before publication, so cancellation cannot race with successful Evidence.
Bundle recovery requires a recorded Evidence ID. Some provider errors carry
typed raw observations in `details.partial_observation` instead of Evidence.
Those raw observations are not recorded in the Evidence bundle. If the SDK
suppresses the error reply after client cancellation, Evidence tools cannot
recover them. An empty bundle does not prove that no partial work or side
effect occurred; output receipts and cleanup observations remain distinct.
The broader retention decision is tracked in
[#4276](https://github.com/morluto/rea/issues/4276).
`analyze_javascript_application` also yields between reconstruction phases and
during graph/result sealing, cross-graph binding checks, Evidence JSON validation,
and canonical hashing. Its final result validation reuses exact graphs whose
@@ -492,12 +812,40 @@ analysis runtime. These failures are reported at the operation that needs
them. A configured provider or an endpoint alone does not establish that a
target is supported.
Composite Hopper regex searches report failures against the accepted
`search_strings` or `search_procedures` operation. The underlying inventory
method and request ID remain in `details.diagnostics.provider_request`, with
the bridge error code and diagnostic type when available. Reattribution
preserves the failure class, captured output, and live cleanup ownership.
Evidence bundles, snapshots, and extraction use the paths and output behavior
declared by their tools. Artifact extraction materializes the selected regular
files into a fresh REA-chosen temporary directory and reports its path.
`analyze_javascript_application` reads the supplied local directory or ASAR path
and returns its result and Evidence inline.
and returns its result and Evidence inline. Its owned analysis worker and private
temporary source files are declared as `launchesProcess` and `writesFilesystem`,
including for a single JavaScript source. This internal analysis does not execute
the supplied application code.
## Static Ghidra bytes and source mappings
`read_bytes` returns initialized analysis memory, including loader fixups, rather
than live process memory. Check `returned_bytes` and `complete` before decoding:
reads stop at unmapped or uninitialized bytes. Missing bytes are unknown; do not
pad an incomplete read with zeros and treat the result as an observation.
`list_segments` reports memory-block initialization and address-space facts.
`address_to_file_offset` maps one address through the provider's source-byte
mapping. A successful start-address mapping does not establish that an entire
range is contiguous in the original file, or that its bytes equal analysis
memory after loader fixups. Uninitialized, missing, and ambiguous mappings fail
explicitly; preserve the reported failure rather than guessing an offset.
For constructor-populated pointer or resource slots, a static zero is not proof
of a later null value. Inspect the slot's `xrefs` and the writer's assembly or
pseudocode, retaining uncertainty about execution order and runtime values.
These operations inspect static evidence and do not execute the target.
## Ghidra first-query deadlines and recovery
@@ -509,13 +857,13 @@ auto-analysis, bridge connection, and health readiness before returning analysis
These deadlines have different owners:
| Deadline | Owner and effect |
| -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| MCP initialize | The client bounds transport/REA connection startup, before any target query. |
| Hopper startup | REA allows 120,000 ms by default; `REA_HOPPER_STARTUP_TIMEOUT_MS` accepts decimal integers from 1–2,147,483,647 ms. Invalid values fail configuration validation. Startup failure is reported by the first provider query. |
| Ghidra startup | REA allows 330,000 ms by default; `REA_GHIDRA_STARTUP_TIMEOUT_MS` accepts decimal integers from 1–2,147,483,647 ms. Invalid supplied values fail configuration validation. Startup failure is reported by the first provider query. |
| Darwin swiftc helper | REA allows 120,000 ms by default for ownership-helper compilation; `REA_SWIFTC_COMPILE_TIMEOUT_MS` accepts decimal integers from 1–2,147,483,647 ms. Invalid values fail configuration validation. |
| Individual tool call | The client bounds its wait, including cold engine startup. The pinned client SDK 2.3.1 defaults to 60,000 ms and can cancel earlier than REA's startup deadline. |
| Deadline | Owner and effect |
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| MCP initialize | The client bounds transport/REA connection startup, before any target query. |
| Hopper startup | REA allows 120,000 ms by default; `REA_HOPPER_STARTUP_TIMEOUT_MS` accepts decimal integers from 1–2,147,483,647 ms. Invalid values fail configuration validation. Startup failure is reported by the first provider query. |
| Ghidra startup | REA allows 330,000 ms by default; `REA_GHIDRA_STARTUP_TIMEOUT_MS` accepts decimal integers from 1–2,147,483,647 ms. Invalid supplied values fail configuration validation. Startup failure is reported by the first provider query. |
| Darwin swiftc helper | REA allows 120,000 ms by default for ownership-helper compilation; `REA_SWIFTC_COMPILE_TIMEOUT_MS` accepts decimal integers from 1–2,147,483,647 ms. Invalid values fail configuration validation. Set it in the environment that starts REA and restart REA; the next ownership-helper compile uses the new value. |
| Individual tool call | The client bounds its wait, including cold engine startup. The pinned client SDK 2.3.1 defaults to 60,000 ms and can cancel earlier than REA's startup deadline. |
For [Codex and Kimi registrations](client-configuration.md), setup supplies
missing tool deadlines from the known provider allowance plus 120 seconds of
@@ -523,7 +871,8 @@ operation headroom: 450 seconds with the default Ghidra allowance, or 2820
seconds with `REA_GHIDRA_STARTUP_TIMEOUT_MS=2700000`. Positive caller-selected
deadlines remain unchanged. Doctor reports an informational recommendation
when a configured deadline is shorter than the known provider allowance. An
explicit JEB upstream request budget also contributes to that allowance.
explicit JEB upstream deadline (`REA_JEB_MCP_TIMEOUT_MS`, shared by catalog
discovery and each tools/call) also contributes to that allowance.
For an already connected client using the pinned SDK, request options are the
**second** argument of `callTool`:
@@ -592,6 +941,13 @@ the current client, so it is not a fresh-start recovery. On the same connection:
`provider_id: "ghidra"`, then retry the first query with an appropriate client
deadline. The connection and selected provider do not need to change.
If REA's provider startup allowance also needs raising, preserve Evidence and
close as above, then update `REA_GHIDRA_STARTUP_TIMEOUT_MS` in REA's launch
environment and restart the server. Reconnect the MCP client before reopening
the target. Configuration is read at process startup; close/reopen on the same
connection cannot apply a changed environment. See the
[CLI retry example](installation.md#ghidra).
This close/reopen flow was exercised on one real Linux stdio connection after a
controlled startup timeout, followed by a successful overview and function
analysis. A separate Windows x64 stdio run with Ghidra 12.1.4, JDK 21 and Node
@@ -653,6 +1009,16 @@ bytes and returns the `integrity_contradictions` records for the extracted
files. An active ASAR unpacked entry whose companion file is absent cannot be
materialized; extraction fails as `unavailable` with that logical path.
Extraction materializes regular files exposed by the active archive reader.
When that reader exposes a nested archive as a file, extraction copies the
archive file; inspect and extract that file separately to unpack its contents.
Extraction retains inventory and materialization traversal limitations in its
result and outer Evidence. If either traversal has unknown coverage, the
observed regular files can still be copied, but the result does not establish
a complete source copy. For example, a POSIX filename that is not valid UTF-8
remains omitted with its raw-name explanation; it is not replacement-decoded
into an output filename.
Packaging tools commonly sign or strip `.asar.unpacked` native binaries after
writing the archive header, so their declared hashes no longer match. Electron
does not check unpacked companions against those hashes at runtime. Integrity
@@ -691,18 +1057,39 @@ after SDK conversion and check advertised validation and actual calls.
Individual model APIs can impose additional nesting limits; complete producer
captures can exceed ten structural levels.
`search_bytes` advertises its pattern and mask as descriptive strings. Its
shared canonical validator scans byte pairs, whitespace, wildcards and aligned
masks without a repeating regular expression or temporary byte arrays. The
server rejects malformed or unconstrained patterns before provider dispatch;
the advertised strings alone do not express those rules.
Boolean literal constraints in input schemas are relaxed only in this
advertisement. For example, a boolean `const` is presented as `type: boolean`;
an affected `not` is omitted, conditional predicates and their branches are
omitted together, pure boolean-literal `oneOf` branches collapse to a boolean,
and other affected `oneOf` branches are presented as `anyOf` to retain branch
guidance without making overlapping widened branches reject inputs.
This can make the advertised schema accept inputs the canonical contract
rejects. The MCP handler still validates every call with the canonical
contract. The Gemini CLI verifier checks the outgoing
`parametersJsonSchema` and canonical contract examples against that declaration
using a loopback model fixture; it does not establish live Google API or
OpenCode client behavior. Set `REA_VERIFY_GEMINI_SCHEMA_PROFILE=compact` to
verify the compact advertisement through the same client boundary.
### Compact input schema profile
Some function-calling providers reject a request while any single tool's
`parameters` schema exceeds a literal serialized size cap; Moonshot clients
such as kimi-cli fail every request while the default catalog is attached
such as kimi-cli have reported failures with the full catalog attached
([#1484](https://github.com/morluto/rea/issues/1484)). Setting the server's
`REA_MCP_INPUT_SCHEMA_PROFILE` environment variable to `compact` renders every
advertised input schema within a 13,312-byte budget, measured to sit below
`REA_MCP_INPUT_SCHEMA_PROFILE` environment variable to `compact` (the default)
renders every advertised input schema within a 13,312-byte budget, measured to sit below
Moonshot's observed cap (community measurements: a 13.7 KB schema is accepted
while a ~15 KB one is rejected). The setting is read at startup; restart the
server to apply changes, as with `REA_MCP_MAX_RESPONSE_BYTES`. The default
value `full` keeps the complete advertisement unchanged. Compact presentation
server to apply changes, as with `REA_MCP_MAX_RESPONSE_BYTES`. Explicitly select
`full` to retain complete advertised input guidance and nested validation detail.
Existing explicit profile choices are preserved. Compact presentation
also reduces schemas whose expanded JSON tree exceeds ten levels, the limit
reported by strict-depth MCP clients in [#2813](https://github.com/morluto/rea/issues/2813).
WorkBuddy/CodeBuddy gateways have also rejected full REA catalogs with an
@@ -783,6 +1170,44 @@ includes all dimension limitations. Equal CFGs establish only equal observed
block sizes and successor relationships. Text from different provider
identities remains unknown.
The `calls` and `references` dimensions share endpoint identity and name
ambiguity rules. Calls retain procedure entry coordinates and direction;
references retain each edge's raw source and target coordinates, direction and
available kind facts. Resolving a reference endpoint to a containing procedure
does not replace its raw coordinate. Hexadecimal case and zero padding do not
change coordinates; address spaces remain distinct.
Coordinate equality requires equal artifact digests and formats, a known equal
architecture, and matching provider-owned native coordinate commitments. Those
commitments retain the exact selected Mach-O image where available and a digest
of the loader's mapping inputs. They exclude unrelated analysis settings, so
changing an analyzer option alone does not invalidate coordinate correspondence.
Equal coordinates in different slices or mappings cannot establish equality.
Hopper's default executable profile and Ghidra's supported deterministic imports
publish these commitments. Hopper loader overrides and attached databases, IDA
profiles, Ghidra universal-image imports without an exact slice commitment, and
older or imported Evidence without a commitment leave coordinate identity
unknown. Unparseable coordinates also remain unknown.
Across different artifacts, endpoints use nonempty stable procedure names.
Missing or address-generated names remain unknown. A name observed at multiple
procedure entries across either dossier's call and reference facets retains its
label constraint but cannot establish equal endpoints. Same-container records
without a common coordinate context likewise cannot establish equality from
matching labels. Procedure labels do not carry authoritative symbol-origin
facts, so recognition of generated names remains conservative and cannot prove
whether every provider label was generated or explicitly named.
Pairing preserves duplicate counts and considers alternative compatible
pairings; array order never identifies an edge. Compatible records with unknown
endpoint identity or reference kind facts remain `unknown`. Independent known
facts that contradict every possible pairing still establish `changed`.
Source Evidence retains the original addresses, names and unavailable facts.
Reconstruction verification uses the selected dimension's status: a same-name
call retargeting in a common context fails a calls claim; an unresolved slice or
mapping cannot pass it. Empty function labels leave `function_match` ambiguous
and the identity dimension unknown.
### Comparison input shapes
`compare_web_captures` accepts exactly one of two input shapes:
@@ -802,6 +1227,14 @@ invoking the handler. Domain validation additionally checks
relationships such as event sequence references and retained counts; JSON
Schema does not express those cross-field invariants.
Comparison also preserves the capture's coverage. A partial HTTP inventory can
establish an observed difference against a complete peer, but an omitted request
does not establish removal. Partial WebSocket streams do not establish frame
alignment. WebMCP frame IDs and tool keys are local to a capture; semantic owner
facts and known counts determine correspondence. Retained script digests can
establish source changes. See [browser comparison semantics](browser-observation.md)
for value uncertainty and storage-fingerprint recapture requirements.
## Generated catalog
Custom clients upgrading from 6.1 should follow the
Loaded 100 of 4038 files, more files were not shown because too many files have changed in this diff. Show more