Compare commits

...
Author SHA1 Message Date
morluto f803d66748 Merge remote-tracking branch 'origin/main' into HEAD 2026-11-07 23:08:57 +08:00
morluto ac30fbd658 style(test): format CLI help default annotations (#9280) 2026-11-07 23:02:44 +08:00
Nianze Wuandmorluto 5d21bddb18 fix(desktop): read JSON with deeply nested overwritten values (#9277)
* fix(desktop): read JSON with deeply nested overwritten values

* fix(desktop): bound exported JSON construction by view depth

* fix(desktop): preserve deep JSON numeric facts within bounded memory

---------

Co-authored-by: morluto <[email protected]>
2026-11-07 22:57:15 +08:00
morlutoandCursor Agent 2e2f981ccf test: assert absolute-path facts without frozen sentences (#9231)
* test: assert absolute-path facts without frozen sentences

Sibling path tests now require the field path, invalid_format, and regex
expectation, and they record that each negative case ran. NUL rejection
follows the argument that contains the byte instead of searching for the
word NUL.

* test: retain actionable NUL path diagnostics

---------

Co-authored-by: Cursor Agent <[email protected]>
2026-11-07 22:46:17 +08:00
morluto ac0336c9c7 fix(ghidra): reconcile Java socket boundary with main 2026-11-07 22:38:07 +08:00
Heo Hao f8bb792f15 fix(cli): stop restating schema defaults in help descriptions (#9278)
The help renderer appends "(default: X)" whenever the option field declares a
schema default, so descriptions that also spell the default out print it twice:

  --block-size <value>  Power-of-two block size from 256 to 65536 bytes
                        (default 4096) (default: 4096)

Remove the hand-written default from the eleven options that own a schema
default across inspect-byte-entropy, inspect-devicetree,
inspect-packet-capture, inspect-packet-fields, inspect-signal-decoders and
decode-signal-recording, matching the rule in cliDecimalField's contract that
the domain field owns bounds, defaults and help.

Options without a schema default keep their prose: --match_limit, --line_limit,
--limit and --packet_limit are nullable or optional, so the renderer emits no
annotation and the description is the only place the effective value appears.

Add a boundary test asserting each such option states its default exactly once,
so a restated default cannot come back unnoticed.
2026-11-07 22:38:03 +08:00
morlutoandCursor Agent 0ecf3a4cbf docs(mcp): document native function analysis scope (#8389)
Co-authored-by: Cursor Agent <[email protected]>
2026-11-07 22:14:55 +08:00
ShuaixinHuang 8842092671 fix(pe): reject misaligned certificate table offsets (#9276)
* fix(pe): reject misaligned certificate table offsets

* test: align ghidra limitation wording with main

* style: format ghidra limitation assertion
2026-11-07 22:14:29 +08:00
Cursor Agent 4d2dcfcaf3 merge(main): keep the Java socket limit and project-identity lane
OpenJDK still reserves two sockaddr_un bytes, so the path cap stays
102 on macOS and 106 on Linux. The real Ghidra run also keeps the
project-identity check from main.
2026-11-07 11:55:35 +00:00
Edgar Arakelyan c5301e6145 feat(desktop): capture, draft, export and witness a task inside a build computer (#9045) 2026-11-07 17:54:59 +08:00
gensokyo-dev 8eff651270 fix(ghidra): make checkpoint workflows discoverable (#9246)
Expose the existing opt-in Ghidra checkpoint lifecycle in tool descriptions and summary capabilities. Distinguish confirmed initial analysis from restored Programs, preserving the checkpoint path and digest through shared CLI/MCP results.

Remove prose-driven checks and their scaffolding; retain canonical effects and lifecycle/source-fact coverage. Validated with 445 focused tests, 13 real Ghidra CLI/MCP scenarios and three isolated local Codex CLI tasks covering disabled setup, saving edits and fresh-agent restoration.
2026-11-07 16:56:33 +08:00
morluto 441ba94086 fix: preserve source closure during snapshot replay (#8870)
* fix: preserve source closure during snapshot replay

* fix: revalidate restored snapshot sources lazily

* fix: bound and share source-closure checks
2026-11-07 16:22:21 +08:00
morlutoandCursor Agent b31fc5d5fb fix(cli): advertise domain constraints for selector and path options (#9181)
* fix(cli): advertise domain constraints for selector and path options

Android class, method, and mapping fields, ADB setting keys, and browser
endpoints used bare strings in --schema. They now reuse the domain fields,
so empty selectors, relative mapping paths, and malformed setting keys
fail in the advertised schema.

* fix(cli): keep selector limits when argument parsing rejects them

Empty browser and Electron selectors now fail in the CLI parser, so their
diagnostics are VALIDATION_ERROR with the one-character minimum. ADB
setting keys stay on the selector repair path, which restores maximum 256
and the identifier pattern.

* test(cli): assert empty-selector codes without frozen prose

Empty browser and Electron selectors still fail as VALIDATION_ERROR
too_small on the named field. The tests no longer copy Zod's sentence.

* test(cli): compare endpoint diagnostics with the domain schema

Browser and Inspector tests take custom issue text from the schema that
owns it, and empty capture comparison must name both missing inspection
fields.

* fix(cli): remove duplicate merged schema import

---------

Co-authored-by: Cursor Agent <[email protected]>
2026-11-07 16:17:09 +08:00
morlutoandCursor Agent 562779bd71 fix(mcp): advertise absolute paths for tools that reject relative inputs (#9179)
* fix(mcp): advertise absolute paths for tools that reject relative inputs

The domain schemas accepted relative paths, then handlers rejected them
with isAbsolute. Those inputs now use absoluteLocalPathStringSchema, and
the duplicate handler checks are gone.

* fix(javascript): own recovery path admission in the schema

Relative recovery paths now fail in absoluteLocalPathStringSchema, including
the additional_paths index. Drop the duplicate handler check, and make the
CLI, MCP, and tools/list tests assert that schema fact.

* test: assert absolute-path facts instead of error prose

Recovery and web-script tests keep the field, format code, and regex
expectation. They no longer freeze the schema's explanatory sentence.

---------

Co-authored-by: Cursor Agent <[email protected]>
2026-11-07 15:49:50 +08:00
吴佳浩Alben 923e6f77e3 fix(javascript): keep timer cancellation within the Node handle family (#7712)
* fix(javascript): keep timer cancellation within Node's handle family

Node keeps timeouts and immediates in disjoint handle families. clearTimeout
and clearInterval are aliases that act only on a Timeout from setTimeout or
setInterval, while clearImmediate acts only on an Immediate from setImmediate.
Crossing the families returns without doing anything, so the scheduled
callback still runs.

REA linked a cancellation whenever the cleared binding traced back to any timer
schedule, so clearImmediate(setTimeout(work, 10)) reported a complete
cancellation and the graph emitted a resolved cancels-timer relation. It also
flagged no uncertainty for it.

Require the cancel and the schedule to share a handle family before linking
them. A cross-family clear now keeps linkedTimerId null and resolution
unresolved, and the graph reports the cancellation as a candidate with an
ambiguous-target unknown instead of a resolved fact.

Verified against Node v24.22.1 over all nine schedule/cancel pairs: setTimeout
and setInterval with clearTimeout or clearInterval cancel, and setImmediate
with clearImmediate cancels; the other five combinations leave the callback
running. Node's own lib/timers.js shows why: clearTimeout requires
timer?._onTimeout, clearImmediate requires immediate?._onImmediate and returns
early otherwise, and clearInterval delegates to clearTimeout.

* chore: retrigger title and readiness checks after the title was corrected

* fix(javascript): say cross-family timer clearing is runtime-dependent

Review feedback from @morluto: the previous wording claimed clearing across the
timer handle families "returns without doing anything, so the timer keeps
running". That does not hold on every supported Node version. Measured on Node
24.22.1, all three cross-family pairs fire:

| scheduled with | cancelled with | Node 24.22.1 |
| --- | --- | --- |
| `setTimeout` | `clearImmediate` | fires |
| `setImmediate` | `clearTimeout` | fires |
| `setImmediate` | `clearInterval` | fires |

`clearImmediate` cancelling a `Timeout` on Node 22 is the case the old claim
missed. Within a family the clear always cancels, which is why the analysis
links only same-family pairs; across families the outcome depends on the
runtime, which is exactly why the analysis reports `unresolved` rather than
`complete` in either direction.

The behavior and the tests are unchanged. The `timerHandleFamily` doc comment,
the linked-origin comment, and the test comment now state the runtime
dependence and why it justifies an unresolved result.
2026-11-07 15:17:47 +08:00
Shivam Dwivedi 36dcd985e1 docs(clients): document llama.cpp MCP setup (#4878)
* docs(clients): document llama.cpp MCP setup

* docs(clients): clarify persistent installs and llama.cpp deadlines

* docs(clients): escape Windows paths in llama.cpp config
2026-11-07 15:16:02 +08:00
gensokyo-dev c07ab4b994 feat(ghidra): checkpoint owned Programs across sessions
Add opt-in REA_GHIDRA_CHECKPOINT_DIR for owned Ghidra Program persistence through the shared CLI/MCP provider. Save completed initial analysis and metadata changes on normal close; verify and restore the last successful packed Program without auto-analysis.

Preserve generation identity, exclusive ownership, typed cancellation and cleanup retry. Refresh checkpoint-dependent profiles after retirement, including failed-switch restoration, while retaining caller selection provenance. Keep source executables and caller projects unchanged.

Validated with 720 focused tests (3 platform skips), 13 real Ghidra CLI/MCP scenarios, static/docs checks and CI. Review findings addressed. No version bump or release publication.

Refs #8165.
2026-11-07 14:52:39 +08:00
morluto 99db80a13e fix(cli): reject non-decimal integer tokens on every numeric option (#9178) 2026-11-07 11:52:39 +08:00
Edgar ArakelyanandClaude Opus 5.5 ac23dc8077 feat(desktop): read XML with a plain DOCTYPE, and check an element's whole text (#9050)
* feat(desktop): read XML with a plain DOCTYPE, and check an element's whole text

- Both readers accept a DOCTYPE with only a name and an optional PUBLIC
  or SYSTEM identifier, record it as the view's doctype, and never fetch
  the DTD or expand entities beyond XML's five; an internal subset, an
  external entity and a parameter entity stay refused alike.
- text_equals and text_contains compare the concatenated text of the
  element (or text node or attribute) a pointer reaches, as XPath
  string() does, with optional normalize_whitespace; both evaluators
  agree, and a text check on a truncated element is undecided.
- Whitespace-only text between elements is dropped only when the element
  holds no other text, so text split into runs keeps its spaces.
- The draft suggests text_equals for an element whose text changed.

So an AbiWord .abw reads as XML, and its paragraph's text is checked
whole however AbiWord splits it into <c> runs, as Writer's spans are.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): draft whole-text checks on the paragraph, read only text as text, and skip asctime stamps

- A changed value is read at both ends, so text that became a run still
  marks its paragraph, and the drafted text_equals moves up out of mixed
  content to the nearest ancestor named by an attribute, such as
  AbiWord's p by its xid.
- Text checks accept only a text node or an attribute value; element
  names and doctype fields fail, in both evaluators.
- isTimestamp also recognises asctime dates (Www Mmm dd hh:mm:ss yyyy).
- XML files are drafted with the keyed diff the archive entries use, so
  no suggestion reaches an element by position; the shared logic moves to
  appTaskDraftNamed.ts.
- Text-check tests assert facts, not whole sentences.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): keep checks on a changed attribute inside a paragraph whose text check replaces its runs'

A drafted whole-text check on a paragraph hid every change inside it, so a
link whose label and href both changed drafted only the paragraph's text and
the new destination went unchecked. The text check now covers text and runs
added or removed whole; a changed name or attribute of an element both
versions hold keeps its own check.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): read an attribute called name or attributes as an attribute when the draft keeps its check

The owner of `.../attributes/name` was taken as `.../attributes`, so a
changed `name` attribute inside a paragraph with a text check was dropped
again. The owner is now read by segment, attributes first.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): draft text checks from text in runs added whole, and name a text element by its own id before its container

Text typed into a run the diff lists whole was never read, so the draft kept
only a presence check on the run, which wrong text also passed. Such text now
marks the nearest element both ends hold, or, when none can take the check,
the added element's own text. The climb also names an element by an
identifier of its own (id, xml:id, xid) before a larger container, so a
headline under a long layer keeps a whole-text check that survives re-split
runs.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* test(desktop): expect the whole-text check where #8988's pairing test meets text checks

On this branch a changed element's text is drafted as `text_equals` on the
element, so the identity pairing test now expects that check, under the
same `[@role=body]` key.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): keep checks for elements without text beside a text edit, and normalize DOCTYPE system id line endings

A review found two gaps.

- A paragraph's whole-text check covered every element added or removed
  inside it, including ones holding no text: an image frame, a line break.
  A solver who typed the text but never inserted the image passed. Such an
  element now keeps its own presence or absence check.
- The Python reader kept a DOCTYPE system literal's CRLF where TypeScript
  normalizes it to LF. Both now report LF.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* test(desktop): import textSyntaxFormat once in the state format tests

main's mixed-case JSON suffix fix and this branch both added the import,
and the merge kept both.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-11-07 03:20:12 +00:00
lostviolinist 57fc2d4617 fix(test): wait for valid debugger fixture PIDs (#9208)
* fix(test): wait for valid debugger fixture PIDs

* test(process): cover delayed and invalid PID publication

* test(process): share validated PID file readiness
2026-11-07 11:12:43 +08:00
Saurabh Sungh abfccc0683 fix(javascript): bound symbolic property and member queries (#8876)
Fixes #8838.

Admit all symbolic query entry points through the existing resolver budget, preserve cache-hit depth and restore entered frames in finally. Retain independent facts with partial source coverage instead of losing entire scripts to recursive spreads.

Validated 273 focused tests, check:fast/docs:check, real CLI/stdio-MCP projections and complete parent reads, independent public-source oracle, and unchanged full results for the official Electron control and Bitwarden selected preload.

Full technical CI 42308068679 passes, including all eight shards and required aggregates. Greptile reviews this head at 5/5; no completed Codex review of this head is available. The Author readiness-only exception is explicitly authorized; no human-review attestation is added.

Assisted-by: Reimu (gensokyo-skills:reimu-incident-triage)
2026-11-06 23:13:15 +00:00
Soukyu 48f5f30fd5 fix(cli): reject malformed dry-run literals before changes (#7870)
Fixes #7540.

Retain every raw dryRun literal at the shared inert token binder before Incur boolean coercion; preserve parser-owned values, original execution, defaults and variadics. Return an actionable typed PARSE_ERROR before setup or environment publication.

Validated 40 focused tests, independent nine-call compiled-CLI before/after configuration controls, check:fast and docs:check.

Full technical CI 42302162266 passes, including all eight shards and required aggregates. Greptile reviews this head at 5/5; no completed Codex review of this head is available. The Author readiness-only exception is explicitly authorized; no human-review attestation is added.

Assisted-by: Reimu (gensokyo-skills:reimu-incident-triage)
2026-11-06 22:30:39 +00:00
2SinceJuly e9ea9d6f50 fix(cli): validate MCP registration before side effects (#9057)
Fixes #7375. Fixes #7608.

Share argument consumption and agent facts between admission and scope checks; reject invalid registrations before settings, backups or runner startup. Preserve missing-value priority, explicit redaction, discovery and valid registration controls.

Validated 33 focused tests (one platform skip), current-main RED and independent compiled-CLI before/after workflows; check:fast passes.

Full technical CI 42297438912 passes, including all eight shards and required aggregates. Greptile reviews this head at 5/5; no completed Codex review is available and the bot reports exhausted quota. The original author readiness declarations remain verbatim; no new human-review attestation is added.

Assisted-by: Reimu (gensokyo-skills:reimu-incident-triage)
2026-11-06 21:56:35 +00:00
Nianze Wu a93eb39698 fix(managed): preserve unknown member ownership across invalid rows
Compare the preceding valid TypeDef coordinate independently for FieldList and MethodList, retaining readable members while leaving affected ownership unknown. Keep the scalar producer and shared owner lookup authoritative across member inspection, metadata inventory and native boundary workflows.

Validated with 577 managed/workflow tests, seven malformed/working ordering vectors, and independent dnfile/pefile raw metadata from a pinned public SystemEvents assembly. Four deliberately changed metadata bytes reproduce both decreases across an invalid row. Six CLI calls and ten stdio MCP tool calls before/after preserve all 458 fields and 195 methods and raw signatures/CIL hashes, keep ordinary results identical across three operations, and retain unresolved filter candidates rather than false absence. Alternative #8868 passes the expanded 206 inspector/ownership controls; converge on this generic implementation.

The original three unchecked human declarations are preserved. The explicitly authorized readiness-only exception applies after technical CI and review; no human-review attestation is added.

Fixes #8861

Full technical CI 42293217445 passes, including all eight shards and required aggregates. Greptile reviews this head at 5/5; Codex cannot complete because the repository review quota is exhausted. The Author readiness-only exception is explicitly authorized; no human-review attestation is added.

Assisted-by: Reimu (gensokyo-skills:reimu-incident-triage)
2026-11-06 21:21:56 +00:00
Sahinur fdc4e3908b fix(artifacts): report ZIP Zstd window refusals as resource limits
Classify a native method-93 decoder window refusal once at the ZIP owner as limit/memory, retaining the fixed 128 MiB cap, native cause and existing corruption/CRC/size/cancellation handling. Derive recovery labels from the cap and share one independent frame fixture.

Validated with 43 reader/translationtests,three actual CLI/stdio MCP cases, a pinned public GLFW payload rewrapped in independently verified ZIP controls, six equivalent CLI calls before/after, static checks and documentation checks. All input bytes remain unchanged, normal extraction matches exactly, and failed outputs are absent. ELF/GPU component refusal-policy gaps remain separate unverified full-format workflows.

The original three unchecked human declarations are preserved. The explicitly authorized readiness-only exception applies after technical CI and review; no human-review attestation is added.

Fixes #8623

Full technical CI 42279406899 passes, including all eight shards and required aggregates. Greptile reviews this head at 5/5; Codex cannot complete because the repository review quota is exhausted. The Author readiness-only exception is explicitly authorized; no human-review attestation is added.

Assisted-by: Reimu (gensokyo-skills:reimu-incident-triage)
2026-11-06 20:12:31 +00:00
Sahinur 81ada025c0 fix(native): preserve Objective-C records with unreadable protocol lists
Keep independent Objective-C class, metaclass, category and protocol records when an adopted-protocol list is unreadable. The shared list reader retains readable prefixes through tagged Result failures; consumers preserve partial/unknown semantics with unchanged limits. Advance the source-owned reader commitment to v3 and reject stale v2 snapshot bindings.

Validated with 232 focused tests, current-main and original-proposal RED checks, named provider-result/schema/identity controls, and a verified public GLFW Mach-O paired with independent LLVM/Python byte evidence. Ordinary binary results remain identical; a deliberately mutated count preserves ten rows instead of eight. The final head passes the real macOS CI lane, with twelve CLI tests including both new protocol-count cases over CLI and stdio MCP. Snapshot binding and same-version replay checks pass.

The original unchecked human declarations are preserved. The explicit maintainer readiness exception applies after technical CI and automated review, without adding a human-review attestation.

Fixes #8945

Full technical CI 42263548929 passes, including all eight shards and required aggregates. The Author readiness-only exception is explicitly authorized; no human-review attestation is added.

Assisted-by: Reimu (gensokyo-skills:reimu-incident-triage)
2026-11-06 19:09:18 +00:00
Edgar ArakelyanandClaude Opus 5.5 f8356f3d71 fix(desktop): read the PPA key add-apt-repository writes into Signed-By (#8982)
* fix(desktop): read the PPA key add-apt-repository writes into Signed-By

On Ubuntu 24.04, add-apt-repository puts the PPA's armored key inside the
deb822 Signed-By field, and REA refused such repositories, so PPAs added the
standard way could not be exported. REA now reads that key by deb822 rules,
verifies the release with it through the same private gpgv keyring as key
files, as the entry's only candidate (as apt does), retains it as armored and
exports it as apt-keys/<name>.asc. A held key that does not verify is refused
with a reason that names no URL.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* test(desktop): check the held-key refusal's facts, not its sentence

The PPA refusal tests now assert that the reason names the sources file
and the release file and no URL, and that export carries that reason,
instead of comparing whole sentences.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-11-07 01:49:11 +08:00
Beqa 91d537098a fix(setup): spell IDA and JADX in interactive completion (#9149) 2026-11-07 01:30:06 +08:00
RRFZ d1103beb8d fix(cli): reject non-decimal web capture deadlines (#9167) 2026-11-07 01:29:44 +08:00
ShuaixinHuang 10407fb624 fix(dotnet): reject invalid UTF-8 metadata version strings (#9156)
* test(process): align symlink fixture with filesystem checkpoints

* test(frida): assert the CLI spawn Evidence contract

* test(mcp): use an unsupported analysis-view parent

* test(browser): separate source-location line bounds from parity

* fix(dotnet): reject invalid UTF-8 metadata version strings

* test(dotnet): preserve valid multibyte metadata version text
2026-11-07 01:29:22 +08:00
ShuaixinHuang 2cf9c42df6 fix(desktop): recognise JSON scalar files with mixed-case suffixes (#9152)
* fix(desktop): recognise JSON scalar files with mixed-case suffixes

* test(setup): use the current registration command in CodeBuddy fixture

* test(cli): split doctor help and timing boundary groups

* style(test): format split MCP doctor boundary groups
2026-11-07 01:28:59 +08:00
ShuaixinHuang fdd4aefd18 fix(cli): preserve literal Bash completion candidates (#9153)
* test(process): align symlink fixture with filesystem checkpoints

* test(frida): assert the CLI spawn Evidence contract

* test(mcp): use an unsupported analysis-view parent

* test(browser): separate source-location line bounds from parity

* fix(cli): preserve literal Bash completion candidates
2026-11-07 01:28:34 +08:00
RRFZ 9cdcdf3dff fix(cli): reject non-decimal guidance search limits (#9165) 2026-11-07 01:28:12 +08:00
ShuaixinHuang 128f7c330f fix(browser): clarify shared source-map output budget (#9154)
* fix(browser): clarify shared source-map output budget

* test(browser): assert source-map budget facts independently

* test(process): align symlink fixture with filesystem checkpoints

* test(frida): assert the CLI spawn Evidence contract

* test(mcp): use an unsupported analysis-view parent

* test(browser): separate source-location line bounds from parity
2026-11-07 01:27:49 +08:00
ShuaixinHuang 0fc35a098a test(javascript): normalize module resolution oracle paths (#9159)
* test(javascript): normalize module resolution oracle paths

* test(process): align symlink fixture with filesystem checkpoints

* test(frida): assert the CLI spawn Evidence contract

* test(mcp): use an unsupported analysis-view parent

* test(browser): separate source-location line bounds from parity
2026-11-07 01:27:27 +08:00
gensokyo-devandgensokyo-dev 39bd0cc625 test(cli): select shutdown budgets for worker lifecycle scenarios (#9170)
Co-authored-by: gensokyo-dev <[email protected]>
2026-11-06 16:58:52 +00:00
Nianze Wu 93ce13e76f fix(desktop): read SQLite state despite metadata name collisions
Resolve exported SQLite metadata through built-in PRAGMAs on the private connection. Preserve application tables/views, source encoding, nullable rows and the existing resource limits.

Validated with 93 focused tests, current-main RED controls, and actual CLI exports of the public Chinook dataset with native/shipped-reader parity and unchanged source hashes. No live GUI, container or installed agentenv qualification is claimed.

Full technical CI 42247722923 (all eight shards and final aggregate) passed; Greptile review scored 5/5 on the merged head. The original human readiness declarations remain unfilled; the explicitly authorized maintainer readiness exception applies without adding a human-review attestation.

Assisted-by: Reimu (gensokyo-skills:reimu-incident-triage)
2026-11-06 16:21:02 +00:00
gensokyo-devandgensokyo-dev 98c589e420 perf(tests): reuse bounded CLI workers for native matching errors (#9163)
Co-authored-by: gensokyo-dev <[email protected]>
2026-11-07 00:08:27 +08:00
gensokyo-dev bacc1e3846 perf(mcp)!: defer retained views in progressive catalogs (#9151)
Keep full schemas and canonical analysis unchanged while loading the cross-domain view through existing named discovery. Qualify real recent-tool workflows with token ledgers, latency, CPU, family RSS and retained-follow-up controls.

BREAKING CHANGE: The opt-in hot-cold catalog no longer preloads inspect_analysis_view. Discover it before calling it, batch with its producer when needed, or use the unchanged default full profile.
2026-11-06 22:44:56 +08:00
ShuaixinHuang cfa7ebb020 fix(environment): ignore unsafe paging bounds (#9145) 2026-11-06 22:18:25 +08:00
ShuaixinHuang 772d6098f0 fix(environment): validate JSON pointer array indexes (#9147) 2026-11-06 22:17:31 +08:00
Beqa fbe7532fab fix(managed): surface capacity limits in member comparison
Preserve per-side capacity diagnostics in shared managed member comparisons and apply repeatable CLI type selection to both source inspections. Keep partial and historical unknown semantics intact, with one typed diagnostic projection and side schema.

Validated with 159 focused tests, final-main CLI/MCP boundary checks, schema validation and official public WPF 8.0.32/8.0.33 assemblies. Full source Evidence remains available through the retained-record MCP workflow. No target execution or budget increase.

Fixes #9021

Assisted-by: Reimu (gensokyo-skills:reimu-incident-triage)
2026-11-06 13:23:13 +00:00
gensokyo-dev 1480a7c72e docs(firmware): qualify component comparison workflows (#9143) 2026-11-06 21:15:58 +08:00
RRFZ 55ee36809d fix(javascript): apply package type scope to application sources (#7883)
* fix(javascript): apply package type scope to application sources

Application analysis parsed .js sources without their package scope, so an unbound require in a type-module package became a resolved CommonJS dependency. The parent now observes each source's nearest same-container package.json, stopping at node_modules, and the worker parses with that package type as reference imports already do.

Fixes #7323

* fix(javascript): keep package type unknown behind unavailable manifests

The package-type lookup indexed only readable files, so a nearer
package.json whose bytes were unavailable was skipped and an ancestor's
"type" applied to sources it does not own. Index unavailable manifests
by container and directory too, and stop the walk with "unknown" when
one is the nearest owner, as the Next app-context lookup already does.

* fix(javascript): keep Electron preloads outside module package scope

Electron's preload loader ignores a package's "type": "module" and gives
a .js preload `require`; ESM preloads need the .mjs suffix. Applying the
package type to every .js source marked such a preload as an ES module,
so its `require("electron")` edges and IPC pairing disappeared.

A source that requires an Electron module now keeps the context REA
inferred before package scope was applied. Node-loaded sources in a
module scope still drop unbound `require`.

* fix(javascript): admit Electron script loaders only on renderer evidence

The previous commit dropped a module package type whenever a source
required an Electron module. A Node-loaded ES module that requires
Electron, or one whose Electron call goes through a local `require`,
then regained the false require edges the package scope removes.

The parser applies the package type unconditionally again. The worker
re-reads a package-module script that requires an Electron module once
outside the package scope. The source keeps that context only when the
static pass there directly observes a contextBridge exposure or a
renderer IPC operation, which only Electron's renderer-side loaders
provide.

* fix(javascript): check Electron renderer evidence without a second static pass

b54845a6b ran the full static pass on an unscoped parse of every
package-module script that requires Electron, then discarded it when no
renderer operation turned up, so such main-process sources paid for two
full passes.

A candidate must now also name contextBridge or ipcRenderer, which any
renderer evidence needs. The evidence check runs only the static pass's
binding and Electron identity preamble with its exposure and IPC
descriptors. The full static pass runs once, on the chosen parse.

* fix(javascript): confirm Electron renderer evidence with the static pass

The focused evidence check from 4a86bca9b also counted renderer calls in
bundler factory defaults that the bundler convention never runs, which
the static pass skips. Such a default switched a type-module file to
script context, so its unrelated unbound requires became imports again.

The focused check now only screens. It follows the static pass's own
traversal, so a negative result still rules evidence out. A positive
result is confirmed by the static pass's direct findings on the unscoped
parse, and those findings become the file's facts when they hold.

* fix(javascript): confirm renderer evidence before wrapper substitution

f71236400 confirmed a screened source from the static pass's final
contextBridge and IPC findings, without wrapper substitutions. The
wrapper pass replaces a directly observed exposure inside a helper with
a substituted finding, so such a preload lost its imports again.

Before its wrapper pass, the static pass now records whether its source
traversal directly observed a contextBridge exposure or a renderer IPC
operation, and observedElectronRendererCall exposes that. The traversal
still skips bundler factory defaults that never run, and the reported
findings are unchanged.

* fix(javascript): resolve declared preload loaders before source analysis

* fix(javascript): leave preload discovery failures to source analysis

A source that failed during preload discovery threw out of the whole
application analysis, so one oversized source or a per-source timeout in a
module package returned no Evidence. Skip a source that exceeds its worker
limits and end discovery on any other failure except cancellation; source
analysis then meets the same source, records its failure and keeps partial
Evidence as it does without discovery.

* fix(javascript): report preload discovery gaps in Evidence limitations

When preload discovery skipped a source or stopped early, source analysis
could still read that source, so the result looked complete while a .js
preload declared there kept its package scope. Return each discovery gap
with the discovered paths and add it to the analysis limitations, which
both complete and partial Evidence carry.
2026-11-06 20:49:41 +08:00
2SinceJulyand2SinceJuly 6c08320654 feat(process): add a non-executing dry run to capture_process_scenario (#8469)
* feat(process): add a non-executing dry run to capture_process_scenario

Preparation prompts asked an agent to prepare a scenario without running
it but could only suggest the tool that launches the target. Add
dry_run: true so the CLI and MCP resolve the executable, working
directory, observation paths and admission checks, then return planned
Evidence under the rea.process-scenario-plan predicate. Planned Evidence
is not capture Evidence and comparison tools refuse it as a capture.

The preparation prompt now suggests the dry-run call, and the scenario
identity commitments exclude the operation-only field.

Fixes #8398

* fix(process): redact planned sensitive input and share admission

Address review findings on the dry-run contract:

- Redact sensitive input events in planned Evidence and preserve only
  their byte length.
- Recheck cancellation after the last awaited admission step.
- Run the sensitive-matcher and filesystem-root admission checks that a
  capture performs before launch.
- Assert the prompt advertises the dry_run field without freezing
  explanatory wording.

* test(process): hold the final admission step for cancellation

Add a preparation host seam so the regression can cancel while the last
filesystem admission call is pending, then assert the returned
AnalysisCancelledError retains the caller's reason.

* refactor(process): remove the unused plan alias

Keep the plan schema as the single exported contract and satisfy the
unused-export check.

* fix(process): report admission-time aborts as planning cancellation

`prepareProcessScenarioPlan` returned the admission error verbatim, so an
abort that landed while `admitFilesystemObservationRoots` awaited its
`lstat` surfaced as a cancelled `ProcessCaptureError` with
`operation: process_capture` even though no process ran. Convert that
result through the same `cancelled()` projection the neighbouring checks
use, and pin it with a test.

* fix(contracts): keep the process result invariants on the dry-run union

The output schema for `capture_process_scenario` became a union of the
capture and plan shapes, and the union no longer carried the description
that advertised the cross-field invariants to MCP clients — the boundary
test reads `normalized_result.description`. Attach a description to the
union that names both variants and keeps the capture invariants verbatim.

* test(process): exercise the post-admission cancellation check

The previous regression aborted immediately after starting preparation and
released the held admission afterwards. Preparation awaits runtime path
resolution first, so that abort was always observed by the pre-admission
check; deleting the post-admission "if (aborted()) return cancelled();"
still left the test green.

Signal from the admitRoots seam when admission is actually entered, abort
only after that point, then let admission resolve ok. Preparation now has
to reach the check between a successful admission and the ok result, and
the assertion pins both the AnalysisCancelledError tag and the caller's
reason as its cause.

Verified red-green: removing only the post-admission check fails this test
(expected true to be false at prepared.ok) while the other two pass, and
restoring it passes all three.

* test(process): gate the admission seam on acknowledgement

Race the admission acknowledgement against planning's own settlement and a
bounded deadline, and release and join planning in `finally`, so a planning
result that arrives before admission fails the test instead of stalling it
until the suite timeout.

* fix(contracts): hold plans to the scenario refinements and redact inputs

`processScenarioPlanSchema` described the resolved dry-run response but
dropped every cross-field check the input contract applies, so a plan
could advertise out-of-order events, an event past `timeout_ms`, or a
terminal beyond the renderer cell budget while claiming it had been
validated. Extract those checks into `scenarioRefinements` and refine the
plan's scenario with them.

The plan's input event also carried `sensitive` and an optional
`input_bytes`, which accepted a sensitive event that still shipped its
data. Model the public and sensitive forms as separate exact variants:
public input forbids `input_bytes`, sensitive input requires an empty
`data` plus its byte length.

---------

Co-authored-by: 2SinceJuly <[email protected]>
2026-11-06 20:49:20 +08:00
Entrpi 81f9f71f6d feat(rizin): select the decoder and map address for one-shot commands (#8991)
* feat(rizin): select the decoder and map address for one-shot commands

execute_rizin_command and rea execute-rizin-command accept optional
asm_arch, asm_bits, asm_cpu and base_address (CLI --asm-arch,
--asm-bits, --asm-cpu and --base-address). REA passes them to Rizin
before the artifact path as -a, -b, -e asm.cpu= and -m. Rizin 0.9.1
refuses -e asm.arch= and -e asm.bits= at startup, while -a and -b work
in 0.7.4 and 0.9.1. Rizin applies -e settings after -a and -b.
base_address is the address of file offset 0, as in REA's raw-binary
inputs.

REA checks only the spelling, because Rizin owns the arch and CPU
vocabulary and its builds differ. The Homebrew build of Rizin 0.9.1
has no xtensa or riscv plugin; the official static Linux build of
0.7.4 has both. Evidence records each input and the complete argument
list. When any input is set, Evidence also states that REA does not
confirm the decoder or map that Rizin used: Rizin uses another value
for one it cannot apply and exits 0. Rizin 0.9.1 keeps its default and
reports the value on stderr, and Rizin 0.7.4 turned -b 8 for arm into
16 without a message. Calls without these inputs keep their argument
list and Evidence.

The real-provider gate tests/boundary/providers/RizinDecoderInputs.test.ts
checks the effect of each flag through MCP with Rizin's own e asm.*,
omlj and byte reads. It skips when no Rizin is on PATH. It passes with
Rizin 0.9.1 (Homebrew, macOS arm64) and 0.7.4 (official static build,
Linux x86_64).

* docs(rizin): map multi-segment ESP32 images with om

An ESP32-family app or bootloader image holds several segments, each
loaded at its own address, so one base_address cannot map it. The
recipe removes the default map with om-* and adds one om map per
segment, using the segment offsets from the image format or from
esptool image-info. It does not depend on an ESP image reader in REA.

The offsets were checked against esptool 4.12.0 image_info on a
synthetic two-segment ESP32 image. Rizin 0.9.1 and 0.7.4 mapped its
segments at their load addresses. Rizin 0.7.4, whose static Linux
build has the xtensa plugin, decoded the IRAM segment as nop.n, nop.n,
ret.n and nop.n.

* fix(rizin): read the file at base_address when a loader would claim it

On Rizin 0.9.1, base_address could read 0xff at the mapped address with
exit 0. -m maps the file, but Rizin still runs its format loaders, and
a loose one can claim a raw image. Rizin 0.9.1's AVR loader matches
when bytes 1 and 3 look like rjmp and turns io.va off, so reads use
file offsets. The BIOS loader, in 0.7.4 and 0.9.1, matches a 64 KiB
file with a jmp 16 bytes from its end and maps it at another address.
An acceptance run on a raw ESP32-S3 segment hit the first case.

With base_address, REA now passes -n together with -m. Rizin then skips
the loaders and maps the whole file at the address: rizin.c calls
rz_io_map_new at the -m address and leaves io.va at its default. The
input therefore means a raw image, and Rizin loads no sections, symbols
or entry point, even from an ELF file. Evidence states this in its own
limitation, and the decoder limitation no longer covers the map.

The real-provider gate adds an AVR-like and a BIOS-like image and reads
below the base, at it, at the end of the image and past it. Without -n
it fails on Rizin 0.9.1. It passes with Rizin 0.9.1 (Homebrew, macOS
arm64) and 0.7.4 (official static build, Linux x86_64). The ESP om
recipe now starts with e io.va=true, because reads use the maps only
while io.va is on.

* fix(rizin): advertise name patterns that compile in v mode

The asm_arch and asm_cpu pattern ended its character class with an
unescaped '-', which JavaScript's v (unicodeSets) mode rejects, so
clients compiling advertised patterns in that mode could not use
execute_rizin_command's schema. Escaping it keeps the same names valid
in every mode, as main's own patterns write it.

* fix(rizin): state what the decoder and map inputs leave unconfirmed

The base_address limitation reported a map REA never observes and left
out that -n keeps Rizin's default arch, bits and byte order. The asm_*
limitation implied Rizin replaces a bad CPU, but e asm.cpu echoes any
value. Compact schemas drop property descriptions, so the tool
description now carries the raw-loading warning. The real-provider
gate checks that asm_cpu changes a decode, and a base_address-only call.

* docs(rizin): record the gate's full run on Rizin 0.7.4

* test(rizin): follow the test guide in the decoder input tests

Focused tests resolve execute_rizin_command from its contract family,
the harness owns the temporary directory before setup can fail, and
guidance is compared with its owner, not with sentences from it.
2026-11-06 20:48:59 +08:00
Harsh Asalkar 7e9cf83fca feat(mcp): guide agents from argument-name mistakes in procedure and search tools (#2146) (#8649)
* feat(domain): guide agents from neighboring-tool argument mistakes (#2146)

* feat(mcp): guide agents from argument-name mistakes in MCP tools (#2146)

* fix(domain): suppress argument replacement when expected field is present (#2146)

Suppress argument replacement guidance when the expected field is already present in the call input, preventing invalid suggestions such as advising procedure when procedure was already provided. Defrost explanatory prose assertions in tests and bind test cases to production toolContract schemas.

* test(domain): use named contract families and decouple guidance prose in tests (#2146)

Import named contract families (OFFICIAL_TOOL_CONTRACTS, APPLICATION_TOOL_CONTRACTS, ENHANCED_TOOL_CONTRACTS) instead of the full tool catalog per docs/testing.md. Replace hardcoded explanatory message and remediation strings with dynamic references to the owning guidance constants.

* test(domain): restore independent replacement field assertions (#2146)

Keep exact checks for expected replacement field names ('procedure', 'pattern', 'address', 'query') independent of the owning guidance helper per docs/testing.md, while retaining dynamic validation for message and remediation prose.
2026-11-06 20:48:37 +08:00
ShuaixinHuang ee2f5b965a fix(desktop): bound exported task array-index conversion (#9139) 2026-11-06 20:48:17 +08:00
gensokyo-dev 5303199163 feat(api-contract): inspect local GraphQL SDL and field references (#9140)
* feat(api-contract): inspect local GraphQL SDL and field references

* fix(api-contract): retain caller metadata and bound selection work
2026-11-06 20:16:31 +08:00
Saim Shafique 14d2c51d85 fix(managed): decode only selected types in member inspection (#9044)
Apply declaring-type selection before member-row admission, recovering only referenced unselected names. Reuse known cores and skip recovery for unscoped inspection. Add valid/malformed selected and unselected generic call regressions.

251 focused tests and check:fast pass. Public CLI scale reproduction and independent metadata oracle confirm complete selected members; public CoreLib/Roslyn/WPF facts remain identical. Real stdio MCP summary and paging retain complete source coverage and provenance.

Original proposal by sx4im. The maintainer explicitly authorized integration and waived the human-declaration gate; unchecked author declarations are preserved without attesting human review.

Fixes #8980.

Assisted-by: Reimu (gensokyo-skills:reimu-incident-triage)
2026-11-06 12:05:09 +00:00
Christopher Pruijsen abdbaa544a fix(rizin): report the output cap instead of a provider signal (#9033)
Attribute REA capture-limit failures at the shared Rizin/objdump execution boundary. Preserve raw signals, output prefixes, unknown completion and incomplete-cleanup facts; report a termination request without claiming confirmed exit.

134 focused tests, check:fast and actual Rizin CLI controls over an owned ELF and /usr/bin/ls pass. Original proposal by cpruijsen.

Fixes #9031.

Assisted-by: Reimu (gensokyo-skills:reimu-incident-triage)
2026-11-06 11:20:43 +00:00
ShuaixinHuang f3d6f45315 fix(desktop): preserve exported SQLite text encoding (#9123)
Preserve original SQLite TEXT bytes and decode them using the database encoding in exported Python readers. Extend the real encoding matrix to leading FEFF and quoted column names.

45 focused tests, native public CLI and full export/helper parity checks, formatting and check:fast pass. Retains the original correction by ShuaixinHuang from #9074.

Fixes #9004.

Assisted-by: Reimu (gensokyo-skills:reimu-incident-triage)
2026-11-06 10:50:12 +00:00
gensokyo-devandgensokyo-dev fe27f96720 test(ci): restore client registration and doctor checks (#9128)
Apply the two-file repair proposed in #9109 to unblock main CI. Preserve every original test assertion and use the current registration command owner.

Co-authored-by: gensokyo-dev <[email protected]>
2026-11-06 18:15:25 +08:00
xin zhuang aefd903adb fix(desktop): preserve nullable SQLite row identity (#8999)
Use whole-row multiset identity for incomplete/NULL declared keys and share it with task drafting. Count actual projected predicates across all rows so timestamp omission and duplicate multiplicities yield satisfiable checks.

Fixes #8997.

Validation:106 focused tests; nativeSQLite public CLI before/after cases and generatedPython evaluator agreement.

Assisted-by: Reimu (gensokyo-skills:reimu-incident-triage)
2026-11-06 09:54:08 +00:00
gensokyo-dev 985b9eae96 feat(browser): import saved Playwright v10 trace evidence (#9127)
* feat(browser): import saved Playwright v10 trace evidence

* fix(browser): preserve original Playwright ZIP member names
2026-11-06 17:52:09 +08:00
Rishikesh Gowda K K 9044b6fb36 fix(kuna): mark project-dependent operations unavailable on export failure (#8748)
* fix(kuna): mark project-dependent operations unavailable on export failure

* fix(providers): preserve sibling operations in capability error copies

* fix(kuna): propagate sibling unavailability on export fallback

* fix(binary): preserve observed sibling recovery instructions across fallback observations

* fix(binary): build sibling unavailability from structured reasons without rewriting display text

* test(kuna): synchronize fallback test on settled export workspace retirement

* test(kuna): release and join export synchronization barrier across exit paths

* test(kuna): synchronize fallback test on observed export process exit

* test(kuna): gate fallback completion on settled export cache build

* test(kuna): preserve build promise chain and restore spy after teardown
2026-11-06 17:22:02 +08:00
996977edf2 feat(cli): expose caller-selected reference-source path exclusions (#2630)
* feat(cli): expose caller-selected reference-source path exclusions

Let import-reference-source accept a JSON array of Git-ignore rules and
pass them to the existing importer before the source tree is opened.

Fixes #2443

* docs(cli): use an effective reference exclusion negation

* test(cli): expect entry_kind on reference-source exclusions

Main records entry_kind on every historical source exclusion. The caller
exclusion assertions still compared objects without that field, so the
pruning test failed after merging main.

Co-authored-by: Yunzhou Li <[email protected]>

* test(cli): record caller *.tmp over project ignore

Caller exclusions are mandatory and override project rules, so project.tmp
matched by *.tmp is caller-excluded. Align the pruning assertion with that
result from the merged importer.

---------

Co-authored-by: morluto <[email protected]>
Co-authored-by: REA Maintenance <[email protected]>
Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Yunzhou Li <[email protected]>
2026-11-06 17:21:28 +08:00
김준호andmorluto f1bfa1cba2 fix(cli): honor the selected format for unknown mcp subcommands (#7802)
* fix(cli): honor the selected format for unknown mcp subcommands

Incur 0.6.1 renders its unknown mcp subcommand rejection as TOON
regardless of --format or --json. Report the same COMMAND_NOT_FOUND
envelope in the explicitly selected format before dispatching to
Incur, share the pinned mcp subcommand table with completions, and
redact explicitly declared sensitive values from the diagnostic.

Fixes #7691

* fix(cli): correct only the subcommand in the mcp suggestion

The suggested command replaced every token equal to the unknown
subcommand, including option values such as --command. Record each
command token's argument position in the shared builtin split and
replace only the subcommand position.

* fix(cli): leave earlier Incur builtins and caller output flags intact

Incur serves --llms, --llms-full and its update check before the mcp
subcommand branch, so defer those invocations to Incur as well. Build
the suggested command from the caller's arguments rather than the
arguments adapted for Incur, so a delimited format and its columns
remain in the suggestion.

* fix(cli): ignore output values spelled like earlier Incur builtins

The earlier-builtin check matched raw tokens, so an output flag value
such as --columns --llms or --filter-output --llms deferred the
rejection to Incur's TOON path. Check the arguments Incur receives and
skip positions its builtin pass consumes as output values.

* fix(cli): omit mcp suggestions that would not replay caller input

Joining the corrected tokens with spaces re-tokenizes a value with
whitespace or shell metacharacters when the suggestion is copied, and
quoting differs between POSIX shells, cmd and PowerShell. Offer the
corrected command only when every token is a plain shell word and no
declared sensitive value was redacted; the message still names the
suggested subcommand.

* fix(cli): preserve safe MCP diagnostic output

* fix(cli): omit mcp suggestions with words zsh expands

zsh expands a word that starts with = to an executable path by
default, so a copied suggestion such as --command =node would replace
the caller's literal value. Treat =-leading words as non-replayable.

* fix(cli): render mcp diagnostics through the document renderer

Markdown output went through Incur's formatter, so caller text such as
*oops* rendered as emphasis instead of literal text. Use REA's document
renderer for every format, and redact declared sensitive literals from
the fixed call-to-action strings as well as the message.

* test(cli): keep the mcp correction on the subcommand after timing globals

---------

Co-authored-by: morluto <[email protected]>
2026-11-06 17:20:56 +08:00
gensokyo-devandgensokyo-dev afc9cfe9ff perf(tests): reuse bounded CLI workers in mobile archive checks (#9116)
Co-authored-by: gensokyo-dev <[email protected]>
2026-11-06 17:09:46 +08:00
gensokyo-dev 39827ec717 docs(mcp): document verified FastMCP catalog recovery (#9113)
Document existing compact output advertisements for full catalogs and explicit hot/cold refresh under the FastMCP1MiB SSE limit. Preserve canonical server validation and spell out the remaining Unsloth and large-result verification gaps.

Refs #9093; related #5861,#7155.

Validation: four real FastMCP HTTP profile trials; docs:check and formatting.

Assisted-by: Reimu (gensokyo-skills:reimu-incident-triage)
2026-11-06 08:22:26 +00:00
gensokyo-dev 3e0599a949 fix(javascript): preserve loader identity across terminal origins (#9107)
Exclude only proven separate values from Webpack loader alias walks; preserve unsupported, projected and unresolved origins as unknown and retain the real alias-state bound.

Refs #7253. The full Bitwarden precision regression remains open.

Validation: 336 focused tests and check:fast passed; real Bitwarden preload identity retained; full main/preload completed with disclosed partial coverage.

Assisted-by: Reimu (gensokyo-skills:reimu-incident-triage)
2026-11-06 07:59:03 +00:00
Hasan ZayniandClaude Sonnet 5.5 1cc979c104 feat(ghidra): admit DOS MZ targets on Windows P0 (#7144)
* feat(ghidra): admit DOS MZ targets on Windows P0

Windows Ghidra P0 refused dos-mz although the MzLoader import, 16-bit
real-mode language attestation and load-image evidence are host-neutral.
Admit dos-mz in inspectWindowsP0TargetSupport and run verify:ghidra:dos on
Windows: inherit the system environment there, expect annotation and naming
edits to be refused (P0 has no mutation authority), and skip verifier
lineage like the 6502 lane. Explicit COM stays refused on Windows; its
verifier is built around annotation edits.

Refs #7121

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* test(ghidra): check naming refusal in the read-only DOS lane

With mutable: false the address-boundary helper skipped every naming
request, so the Windows report overstated its coverage. Assert that
set_address_name and set_addresses_names are refused with
capability_unavailable and leave the entry name unchanged, and declare
the optional mutable argument in the .d.mts.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* test(ghidra): unwrap failed profile Result before asserting

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>

* fix(ghidra): expect an unchanged DOS entry on Windows P0 verification

Windows P0 refuses edits, so verifyGhidraAddressBoundaries skips the
rename-and-restore and the entry keeps its imported symbol source. The
post-check still expected user_defined, which only follows a rename.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-11-06 15:39:03 +08:00
Xiangfang Chen fb841a9596 feat(setup): support CodeBuddy as a setup client (#2492)
Register rea in the global ~/.codebuddy/mcp.json through a new codebuddy
entry in SUPPORTED_CLIENT_DEFINITIONS (json dialect, ~/.codebuddy marker,
~/.codebuddy/skills skill directory). Document the client and cover
registration and uninstall with boundary tests. Loading, the bundled
skill, and real REA tool calls were verified in the CodeBuddy client.
2026-11-06 15:38:47 +08:00
Jay-Alexander Elliot 1902fcb13d fix(cli): load Evidence record pairs from JSON files (#5129)
* fix(cli): read explicit Evidence record pairs from JSON files

* test(cli): isolate record-pair file regression

* style(cli): format record-pair file regression

* test(cli): reuse comparison fixture for record-pair file input

* style(cli): format reused record-pair test invocation

* test(cli): keep paired-input coverage within lint function limit
2026-11-06 15:38:30 +08:00
吴佳浩Alben fe9797b487 feat(python): qualify a recognized 3.14/3.15 pyc header (#8927)
* feat(python): qualify a recognized 3.14/3.15 pyc header

Python 3.15 is the default `python3` on current Homebrew, but its pyc was rejected with
"Pyc magic 3666 is outside the pinned CPython 3.7–3.13 table" — a message that says
nothing about the file and reads as if the version were unknown. Issue #8206.

Two facts were wrong.

**3.15 was not in the identity table at all,** so `pythonBytecodeIdentity` reported it as
unknown even though the table already pins 3.14 (3627). 3666 is now pinned as 3.15.

**A known identity without a pinned code layout shared one refusal with an unknown
magic.** The two are different outcomes: every 3.7+ pyc carries the same packed 16-byte
header, so a recognized newer magic can still be qualified even when no marshal layout is
pinned for it. The parser now separates them:

    magic 3666 (3.15)  -> CPython 3.15 header is recognized (magic 3666, 16-byte header,
                          flags 0x0, timestamp-based), but no marshal code layout is
                          pinned for that version, so its code objects cannot be decoded.
                          Standalone marshal decoding is pinned for CPython 3.7–3.13.
    magic 1            -> Pyc magic 1 is not a pinned CPython identity, so its header
                          cannot be qualified; ...

The recognized case reports the observed version, the header shape, the PEP 552 flags and
whether the pyc is hash-based or timestamp-based, which is what an agent needs to decide
that a `.jsc`-style text-recovery attempt is pointless and that the file is a 3.15 pyc
rather than a corrupt one. It stays a typed `unsupported` failure: no pinned layout means
no decoded code object, so there is no partial success.

I did not guess a 3.14/3.15 marshal layout. The 3.11+ `TYPE_CODE` order is pinned through
3.13; asserting the same order holds for 3.14 or 3.15 without a real interpreter to
verify against would be an unsupported claim, which is why this change qualifies the
header instead of decoding it.

Docs and the tool description now say marshal decoding is pinned for 3.7–3.13 while the
identity table also recognizes 3.14 and 3.15.

Three regressions fail on the base commit.

* chore: retrigger title and readiness checks after the title was corrected

* test(python): pin the qualified header facts to the packed fields

Review finding. Every new case used `pythonPycHeader(magic)` with its default zero flags
and never asserted the reported flags or header mode, so a refusal that hardcoded
`flags 0x0, timestamp-based` would have passed all of them.

The cases now build the header from explicit fields and assert the facts that field
produces: `flags 0x0/timestamp-based` and `flags 0x1` or `0x3` with a PEP 552 source hash
for hash-based. A separate case parses the same magic in both modes and requires each
refusal to name its own mode and not the other.

Verified by hardcoding `flags 0x0` in the refusal: the two hash-based cases fail.
2026-11-06 15:36:22 +08:00
吴佳浩Alben 99bcd3e4e0 fix(javascript): report empty Electron coverage instead of silent zeroes (#8332)
* fix(javascript): report empty Electron coverage instead of silent zeroes

`analyze_javascript_application` reported `browser_windows: 0` and
`ipc.operations: 0` with only generic partial/unknown flags, so a bundle that
imports `electron` but whose Electron facts were not recovered read exactly like
an application with no IPC at all. Issue #8201 is that false negative on a real
Rolldown bundle.

Two additions, both scoped to "Electron is referenced but nothing was recovered":

**A machine-readable indicator.** `electron_summary` gains
`electron_semantics: "recovered" | "empty"`. It is absent when no analyzed source
links an Electron module, so its presence distinguishes "not an Electron
application" from "Electron coverage is empty". It follows the existing
`protocol_registrations.exactOptional()` precedent, so previously captured
evidence stays valid.

**A graph limitation.** The limitation list already carried a note when at least
one Electron fact was recovered but said nothing when zero were. It now states
that zero IPC operations and zero BrowserWindows mean empty coverage rather than
absence.

`electronModuleReferenced` is the single owner of the module test. It reuses
`ELECTRON_MODULE`, which matches exactly the supported entry points (`electron`,
`electron/common`, `/main`, `/renderer`, `/utility`), so `electron-helper` does
not count, and it ignores type-only links because importing Electron types
promises no boundary fact.

Five tests cover recovered, empty, not-referenced, type-only, and supported
subpath versus unrelated package. The empty, recovered and type-only cases fail
on unmodified `main`.

* chore: retrigger title and readiness checks after the title was corrected

* fix(javascript): report Electron coverage from retained facts

Four review findings on the coverage indicator.

**A rejected application projection hid the reference.** A file whose expanded
relationships exceed the result budget is replaced by a placeholder with no
module links before the summary runs, while `graphLimitations` reads the raw
analysis. The same response could then warn in prose and omit the field. The
summary now takes the retained analysis for coverage only, and both outputs
read one `electronSemanticsState`, so they cannot disagree.

**A skipped semantic analysis hid the reference too.** `electronModuleReferenced`
read only `semantic.ir.moduleLinks`, which is absent when semantic analysis is
skipped for a budget and drops `import("electron")` or `module.require("electron")`
forms. It now falls back to the retained `javascript.references`, which the static
producer records for those inputs, while still excluding `type-import` links when
the richer owner is available.

`electronBoundaryFactCount` is now the single owner of the fact total, replacing a
duplicate inline reduce in the graph builder.

**Omission no longer claims absence.** The schema comment said a missing
`electron_semantics` means the application does not use Electron. The predicate
only establishes that no retained fact names one, and unread sources, failed
analysis, and dynamic module names can hide its use, so the contract now says
unknown.

Two regressions cover a rejected projection and a skipped semantic analysis; both
fail on the previous commit.

* fix(javascript): count a dynamic Electron import as coverage

Review finding: `electronModuleReferenced` returned as soon as module links
existed, so a literal dynamic import was ignored. Verified against the two fact
owners:

    import("electron")   -> references: [dynamic-import electron]
                            moduleLinks: []
    require("electron")  -> references: [require electron]
                            moduleLinks: [require electron]

An application that only loads Electron through `import("electron")` therefore
got neither `electron_semantics: "empty"` nor the empty-coverage warning, and the
zero counts stayed unexplained.

The two owners now answer their own half instead of one short-circuiting the
other. Module links answer a runtime static import or require and keep excluding
`type-import`; the retained reference is read for every kind except
`static-import`, which is exactly the type-only risk. A dynamic specifier is
still never counted by either owner, so this remains an observed-reference claim
rather than an absence claim.

The regression uses `import("electron")` and fails on the previous commit.

* fix(javascript): read only module-loading Electron references

Two review findings, both verified against the static reference producer.

**A retained runtime static import was skipped.** The excluded kind was meant to
protect the type-only distinction, but the static analyzer records a whole
`import type { IpcMain } from "electron"` declaration as no reference at all:

    import type { IpcMain } from "electron"   -> references: []
    import { app } from "electron"            -> references: [static-import electron]
    require("electron")                       -> references: [require electron]
    import("electron")                        -> references: [dynamic-import electron]
    new Worker(new URL("electron", m))        -> references: [worker electron]

When semantic analysis is skipped for a budget the module links are absent, so
that exclusion dropped a real runtime ESM import and left the zero counts
unexplained.

**A worker URL path is not an Electron module.** `new Worker(new
URL("electron", import.meta.url))` records a `worker` reference whose specifier is
a path relative to the importing source, so counting it reported an ordinary
worker file as `electron_semantics: "empty"`.

`ELECTRON_REFERENCE_KINDS` is now an explicit allowlist of the kinds that load a
module by package specifier — `static-import`, `dynamic-import` and `require` —
so `worker` and `service-worker` URL paths are excluded. Module links are still
read first for the type-only distinction.

Three regressions cover a skipped-semantics ESM import, a skipped-semantics whole
type-only import, and a worker URL path; two of them fail on the previous commit.

* fix(javascript): own Electron applicability in the static producer

Review finding. A `worker` reference records a URL relative to the importing
source, so `new Worker(new URL("electron", import.meta.url))` names a local file
and is not an Electron module. The boundary owner already read only module-loading
reference kinds, but `finalizeStaticAnalysis` still treated every matching static
reference as `analysis_coverage.applicability.electron: "observed"`, which
`javascriptStaticSourceCoverageSummary` publishes. One response could therefore
say Electron was observed in its coverage view while the Electron summary omitted
`electron_semantics`.

`ELECTRON_REFERENCE_KINDS` now lives next to `ELECTRON_MODULE` in the domain owner
and is consumed by both the static producer and the Electron boundary owner, so the
two outputs cannot disagree. A worker URL path is `unknown` for both, while a
`require("electron")` is `observed` for both.

The regression asserts the domain applicability fact and the boundary summary agree
for a worker URL path; it fails when the producer stops filtering by kind.

* fix(javascript): require a loaded Electron module for applicability

Review finding. A CommonJS `require.resolve("electron")` records a `require`
reference with `operation: "resolve"`: it locates the module without loading it,
so no Electron boundary fact is expected. Both predicates dropped the operation,
so such a source reported `electron_semantics: "empty"` and the missing-coverage
warning even though Electron was never loaded.

Measured on the static producer:

    require.resolve("electron")  -> references: [require resolve electron]
                                    applicability.electron: unknown
    require("electron")          -> references: [require load electron]
                                    applicability.electron: observed
    import("electron")           -> references: [dynamic-import load electron]
                                    applicability.electron: observed

`electronModuleReferenced` and `finalizeStaticAnalysis` now both require
`operation === "load"`, so the boundary summary and the coverage view stay aligned
and reachability-only references no longer claim missing coverage.

The regression asserts a resolve-only source produces neither the field nor the
warning, and it fails when the operation check is removed.

* fix(javascript): classify Electron loads in the static fact owner

Review finding, and the correct home for the previous two rounds of the same
complaint. A Webpack/Rspack factory may name a local module `"electron"`, and the
static producer then records a loaded `require` reference whose specifier is that
bundle module key. Treating the key as a package specifier reported
`electron_semantics: "empty"` plus the missing-coverage warning for a bundle that
never loads Electron.

Three changes, all in the domain owner, so no consumer re-derives the question:

- `JavaScriptModuleRangeIndex.hasKey` answers whether a specifier is a recovered
  bundle module key. A `require` whose specifier is such a key loads a module
  inside the artifact.
- `electronObserved` in `finalizeStaticAnalysis` now requires a loaded module-load
  reference whose specifier is not a bundle key. The published
  `analysis_coverage.applicability.electron` therefore owns every exclusion:
  `type-import`, `require.resolve`, `worker`/`service-worker` URL paths, and
  bundle-local keys.
- `electronModuleReferenced` consumes that owned fact instead of re-deriving it,
  keeping the boundary summary and the coverage view from disagreeing. Historical
  evidence without the field still falls back to the retained references.

Measured:

    bundle-local module ID "electron"  -> applicability unknown
    require("electron")                -> applicability observed
    webpack external n(45840)          -> applicability observed

The regression covers both the local key and the real external in the same
factory shape; it fails without the bundle-key exclusion.

* fix(javascript): identify a bundle module by its loader, not its key

Both review findings were right, and they show why the previous key-set approach
was wrong: bundle-key membership is a property of the file, but whether a
specifier names a module inside the artifact is a property of the reference.

`JavaScriptStaticReference` now records the loader that produced it, and
`electronObserved` excludes `factory-loader` references instead of every specifier
that happens to match a recovered module key. Measured on Node 24.22.1:

    local module keyed "electron"                 -> unknown  (factory-loader)
    external 45840 = require("electron")          -> observed (commonjs-loader)
    factory keyed "electron" that loads the pkg   -> observed (commonjs-loader)

The third case is the regression: a bundle whose factory is *named* `"electron"`
but whose body does `e.exports = require("electron")` is a real Electron load, and
the old key-set check suppressed it. The real load keeps its `commonjs-loader`
identity even when the same specifier also names a factory, so one response can
rely on the loader while the other relies on the key without either lying.

`JavaScriptModuleRangeIndex.hasKey` is removed; it answered the wrong question.

One regression covers all three shapes and fails when the `factory-loader`
exclusion is dropped.

* fix(javascript): carry the reference loader across the worker boundary

The worker schema is strict, so the new `loader` field broke the worker handshake:
`unrecognized_keys` on every `references` entry. That surfaced in CI as both a
worker-schema test failure and a package-baseline failure reporting
`worker_failure_reason: "io"` for a one-file Electron app.

`referenceShape` now accepts the same three loader identities as the domain type, so
the fact survives the worker boundary instead of collapsing every analysis to an
execution failure.

Verified: `JavaScriptAnalysisWorkerSchemas.test.ts` 10 passed, and the full
domain/application/javascript suite is 5484 passed | 10 skipped. The remaining
`SourceMapCommand` and `DarwinHelperCache` findings are present on unmodified
`main` and are not from this change.

* fix(javascript): remove the superseded key set and forward a deferred loader

Two review findings.

**The `keys` set is dead.** `JavaScriptModuleRangeIndex.keys` is left over from the
abandoned key-membership approach: the loader-identity refactor removed `hasKey` and
the write in `add`, so nothing reads it. Removed.

**The deferred require path dropped the loader identity.** `requireCallSpecifier`
returns a `loader` for `commonjs-loader`, `factory-loader` and `create-require`, and the
direct `addReference` call forwards it, but `finishModuleRequireCalls` did not. A
`createRequire` loader saved under a member literally named `require` therefore lost
its identity:

    import { createRequire } from "node:module";
    const holder = { require: createRequire(import.meta.url) };
    holder.require("x");

    before: require "x" with loader undefined
    after:  require "x" with loader "create-require"

Three regressions cover it; one fails without the change.

I did **not** change the inline-type-only import case. `isTypeOnlyImportDeclaration`
deliberately keeps a declaration whose bindings are all inline type-only, because
`hasUncertainTypeOnlyModuleSideEffect` reports that decision as
`type-only-module-side-effect` uncertainty: whether TypeScript emits the import depends
on the consumer's compiler options. Narrowing it to erased broke
`javascriptStaticCoverage.test.ts` and `javascriptSemanticAnalysis.require_links.test.ts`,
which assert exactly that contract. If Electron applicability should ignore that
reference, the decision belongs where applicability is derived, not in the shared
declaration predicate — say the word and I will do it there instead.

* fix(javascript): keep a commonjs-loader reference out of the bundle module index

Review finding. `finalizeLocatedFindings` attaches `module_runtime` to every finding
that sits inside a factory module range, whoever produced it. `resolveReference` then
sent every reference carrying `module_runtime` through the bundle-module lookup, so a
real CommonJS load inside a factory body resolved to the local module key that shared
its name:

    "electron": (e, t, n) => { e.exports = require("electron") }

    fact:  specifier "electron", loader "commonjs-loader", module_key "electron"
    before: resolved through the bundle module index to `#module:electron`
    after:  resolves as an external package specifier

Only a `factory-loader` specifier names a recovered bundle module key, which is what
`loader` already records after the loader-identity change. The module lookup is now
gated on it, and an absent `loader` keeps the previous behaviour for older findings.
2026-11-06 15:15:43 +08:00
Canandeco-null 85775b635b test(ghidra): verify installation metadata budget through doctor (#6690)
* fix(ghidra): name installation metadata limit

* test(reference): return byte-preserving entry names

* fix(javascript): coalesce projected source facts

* test: repair isolation and format fixtures

* fix(evidence): retain rebound snapshot proof

Carry immutable member proof across caller and target path rebinding so low-headroom error projection reuses completed Evidence without cloning its payload.

---------

Co-authored-by: eco-null <[email protected]>
2026-11-06 15:15:23 +08:00
Canandeco-null 7ad3d7928b test(search): cover Unicode literal syntax and astral case folding (#6685)
* fix(search): fold Unicode literal matches

* test(ida): require Unicode literal case folding

* test(reference): return byte-preserving entry names

* fix(javascript): coalesce projected source facts

* test: repair isolation and format fixtures

* fix(evidence): retain rebound snapshot proof

Carry immutable member proof across caller and target path rebinding so low-headroom error projection reuses completed Evidence without cloning its payload.

---------

Co-authored-by: eco-null <[email protected]>
2026-11-06 15:15:03 +08:00
Canandeco-null 1d4e9f31c3 test(cli): cover production doctor output option ordering (#6683)
* fix(cli): retain production doctor output routing

* test(reference): return byte-preserving entry names

* fix(javascript): coalesce projected source facts

* test: repair isolation and format fixtures

* fix(evidence): retain rebound snapshot proof

Carry immutable member proof across caller and target path rebinding so low-headroom error projection reuses completed Evidence without cloning its payload.

---------

Co-authored-by: eco-null <[email protected]>
2026-11-06 15:14:43 +08:00
Jay-Alexander Elliot dc9f329764 test(ghidra): verify PSP VFPU prefix and vector decoding (#8614)
* test(psp): add source-owned VFPU prefix and vadd sequence

* test(psp): assert VFPU prefix and consumer decode facts

* test(psp): verify VFPU prefixes and consumers through public Ghidra MCP

* docs(psp): delimit VFPU prefix/consumer conformance coverage

* test(psp): preserve unknown when Ghidra omits high-pcode data

* style(psp): apply pinned oxfmt output to verify-real-ghidra-mips.mjs

* style(psp): apply pinned oxfmt output to psp-fixture.mjs
2026-11-06 15:14:24 +08:00
吴佳浩Alben 172f248884 fix(javascript): emit no fs acquisition for a rejected literal mode (#8624)
* fix(javascript): emit no fs acquisition for a rejected literal mode

`fs.open`, `fs.openSync` and `fs/promises.open` validate the `mode` in argument 2 before
touching the filesystem, so a mode Node rejects throws synchronously and no descriptor
exists. The guard read only the flag, so a rejected mode kept its acquisition and any
linked release.

Measured on Node 24.22.1:

    fs.openSync(p, "r", -1)          -> THREW ERR_OUT_OF_RANGE
    fs.openSync(p, "r", 1.5)         -> THREW ERR_OUT_OF_RANGE
    fs.openSync(p, "r", NaN)         -> THREW ERR_OUT_OF_RANGE
    fs.openSync(p, "r", 1e21)        -> THREW ERR_OUT_OF_RANGE
    fs.openSync(p, "r", true)        -> THREW ERR_INVALID_ARG_TYPE
    fs.openSync(p, "r", 0n)          -> THREW ERR_INVALID_ARG_TYPE
    fs.openSync(p, "r", {})          -> THREW ERR_INVALID_ARG_TYPE
    fs.openSync(p, "r", "8")         -> THREW ERR_INVALID_ARG_VALUE
    fs.openSync(p, "r", "")          -> THREW ERR_INVALID_ARG_VALUE
    fs.openSync(p, "r", "0o644")     -> THREW ERR_INVALID_ARG_VALUE
    --- accepted ---
    fs.openSync(p, "r", 0o644)       -> ok
    fs.openSync(p, "r", 2147483647)  -> ok
    fs.openSync(p, "r", -0)          -> ok
    fs.openSync(p, "r", "0644")      -> ok
    fs.openSync(p, "r", null)        -> ok
    fs.openSync(p, "r", void 0)      -> ok

The guard decides only exactly-readable literals. A numeric mode is validated as an
unsigned 32-bit integer; a string mode must be a canonical octal digit string, padded
leading zeros allowed, and its value must stay inside uint32; `null`, `undefined` and
`void <expr>` default the mode and are kept, as is a computed value.

Three boundaries are deliberate:

- Values in `[2^31, 2^32-1]` **abort the Node process** rather than throwing
  (`Assertion failed: args[2]->IsInt32()`), so they stay undecided instead of being
  claimed as a synchronous throw.
- A spread before the mode slot shifts the runtime position.
- A unary chain is resolved through its innermost readable operand, and a nested
  boolean or string result is left undecided rather than applied as a number.

Regressions cover both directions. Full suite here: 5466 passed | 7 skipped.

* fix(javascript): remove the superseded string mode check

Review finding. `rejectsOpenModeLiteral` builds `octal` from a string literal or a
no-expression template and returns inside that branch for every value it matched. A
`StringLiteral` therefore never reaches the later `isStringLiteral` check, which cannot
run and is a second copy of the canonical-octal rule to maintain.

* test(javascript): cover the spread and boundary paths of the mode guard

Review feedback from @greptile-apps: the new mode tests skipped both paths the
guard deliberately leaves undecided. Two spread cases are added:

- `openSync(...args, "r", "0600")`, where a spread in the flag slot shifts the
  runtime position of the mode;
- `openSync(p, "r", ...modes)`, where a spread occupies the mode slot itself.

Both must keep the acquisition, because neither establishes the mode value. The
boundary cases were already present: `2147483647` and `2147483648` are keep
cases (`2147483648` aborts the process rather than throwing) and `4294967296` is
the reject case, alongside the octal `37777777777` keep and `40000000000` reject
pair.

127 cases pass in this file.
2026-11-06 15:14:03 +08:00
gensokyo-dev 91d73a113e feat(contracts): inventory offline OpenAPI operation declarations (#9104)
* feat(contracts): inventory offline OpenAPI operation declarations

* docs(contracts): link fixture provenance outside the docs site

* fix(contracts): describe the shared API document byte allowance

* fix(api-contract): preserve safe diagnostics and completed source facts
2026-11-06 15:13:51 +08:00
RRFZ 42aaef8bdb fix(cli): require absolute ADB device paths for file pull and push (#9048)
pull-adb-file and push-adb-file declared device-path as any non-empty string. The CLI schema advertised a relative path as valid, and the service then refused it under the MCP field name. Reuse the shared device_path fields, and keep the regex constraint in CLI field errors as list-adb-directory does.
2026-11-06 15:13:43 +08:00
Luis Araya 3959b68d91 test(rust): assert non-retryable compiler-hash capacity in CLI and MCP (#5504) (#7520) 2026-11-06 15:13:24 +08:00
吴佳浩Alben 1c0a02d4a4 fix(evidence): bound malformed file field diagnostics (#8557)
* fix(evidence): bound malformed file field diagnostics

`readEvidenceFileSource` projected every validator issue, and the generic error
delivery applied the MCP response budget afterwards. A file with many invalid
fields therefore produced a transport `resource_constraint` that replaced the
typed file error, so the caller lost the selected path entirely. Issue #8453
reports exactly that: 20,000 invalid values still returned a direct
`invalid_request` with the path and every field issue, while 125,000 returned
`resource_constraint` with neither.

`projectInputIssuesWithinBudget` already existed for this but had no production
caller. The reader now projects within a 64 KiB allowance and, when the whole list
does not fit, keeps the largest leading group that does and appends one explicit
omission issue naming how many of how many were dropped. The typed file error and
its `path` therefore survive the response boundary, and the diagnostics stay
actionable and explicitly incomplete instead of hiding the real failure.

Two regressions cover the bound and the fits-in-budget case. With the unbounded
projection restored, the bound test and its supporting fixtures fail.

* chore: retrigger title and readiness checks after the title was corrected

* fix(evidence): bound file diagnostics in one pass and cover the MCP boundary

Review findings on the first version.

**The prefix was not maximal.** `boundedContentIssues` halved the list and returned
the first attempt that fit, so a file just over the allowance lost roughly half its
diagnostics. Prefix selection now lives in the shared projector
(`projectInputIssuesBounded`), which walks the issues once and stops exactly at the
allowance, keeping the largest fitting prefix.

**A compound issue could still lose its location.** One `unrecognized_keys` issue
naming hundreds of keys expands into hundreds of field diagnostics and can exceed
the allowance on its own. The projector now retains a diagnostic at that issue's
path instead of returning only the omission notice.

**The regression never crossed the MCP boundary.** The issue is about the delivered
MCP error, so `evidenceFileSourceMcp.test.ts` sends the 125,000-value fixture
through the production `inspect_analysis_view` tool and asserts the delivered error
stays `invalid_request`, keeps `details.path`, keeps bounded `content_issues`, and
fits the response budget. Both regressions fail without the bound.

* test(evidence): move the MCP delivery regression to the adapters project

Review finding. `scripts/lib/test-projects.mjs` assigns `src/application/**` to the
`services` project, so the delivery regression sat in the wrong project even though
it exercises the production MCP tool registration. Moving it to `src/server/` puts
it in `adapters`, which runs the other MCP boundary tests and plans the same way.
2026-11-06 14:53:25 +08:00
e69f27fba0 fix(web-capture): bound the mitmproxy headline facet index (#2876)
* fix(web-capture): keep mitmproxy request targets and status codes in previews

Capture previews selected HAR/SAZ headline names (url, status) only, so a
native mitmproxy record lost its request target (scheme, host, port,
authority, path) and its status_code while keeping the reason phrase, and
the matching numeric sidecars were filtered out. Select those mitmproxy
headline fields too; headers and payloads stay omitted.

* 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 <[email protected]>

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

* fix(android): make DEX descriptor pattern portable

* style: format comparison and IDA sources

* fix(web-capture): preview mitmproxy targets only as safe scalars

Select the mitmproxy target and status fields only for mitmproxy
records, only when they are scalars, and the path only in origin form.
An absolute, network-path or otherwise ambiguous path spelling can carry
userinfo that the native bridge's redaction does not recognise, and a
nested value could copy an arbitrary record subtree into the preview;
both stay in complete record pages. Other formats keep their existing
headline fields.

* fix(web-capture): keep the asterisk-form OPTIONS target in mitmproxy previews

* fix(web-capture): exclude ambiguous and oversized mitmproxy targets from previews

Origin-form checks alone accepted slash-backslash and slash-control
spellings such as /\user:password@host/a, which WHATWG resolves as an
authority with credentials and the recorded transport-URL interpretation
already treats as ambiguous credentials. Preview a mitmproxy path only
when parseRecordedTransportUrl finds no credentials, and omit mitmproxy
headline strings longer than 8 KiB so one record cannot exceed the reply
budget; both stay in complete record pages.

* fix(web-capture): keep userinfo-bearing mitmproxy target fields out of previews

Native redaction covers only request path and authority, so a malformed
state such as host "user:[email protected]" passed through the
preview. Omit every non-path mitmproxy headline string that contains a
userinfo marker (@); valid schemes, hosts and ports never do, and the
value stays in complete record pages.

* fix(web-capture): admit mitmproxy headline values only in their well-formed shapes

A per-string length cap still let a malformed capture fill every target
slot with escape-heavy text, so one default preview page could expand to
about 18 MiB of JSON. Preview each mitmproxy field only in its own shape:
a short scheme token, host/authority without controls, whitespace or
userinfo markers, integer port and status code, and a path of at most
2 KiB without control characters. That bounds every record's headline to
about 4 KiB serialized, even for an escape-heavy path.

* fix(web-capture): bind mitmproxy headlines to their message side and integer type

Select mitmproxy target fields only from the request and status_code
only from the response, so an unknown scalar extension such as
response.path or request.status_code is not promoted to a headline.
Admit port and status_code only when the record's numeric sidecar
reports the producer's integer type; a native float such as 443.0 is
otherwise indistinguishable from 443 after JSON parsing.

* fix(web-capture): require one canonical integer sidecar for mitmproxy numeric headlines

An inline parent could pair request.port 443 with an integer sidecar
whose literal is unbounded or does not represent the value, or with
duplicate sidecars, and the selected-pointer filter would copy them into
the preview. Admit port and status_code only with exactly one integer
sidecar whose literal is the value's canonical decimal, which also
bounds the copied literal to a few characters.

* fix(web-capture): add mitmproxy headlines only for HTTP flows

The mitmproxy container format does not make every retained flow HTTP; the bridge keeps non-HTTP and unknown flow states with their extensions. Add the mitmproxy target and status candidates only when the record's own type is "http".

* fix(web-capture): require consistent numeric sidecars for every mitmproxy headline

Selecting a field also selects its numeric sidecars, so a string target such as request.path with an unrelated multi-megabyte sidecar copied that literal into the preview. Admit integer slots only with exactly one canonical integer sidecar and string slots only with no sidecar.

* fix(web-capture): fail closed on any userinfo marker in previewed mitmproxy paths

URL parsing can fail before recognising userinfo: /\user:[email protected]:65536/a has an invalid port, so the parser reported no credentials and the path was previewed with them. Every userinfo needs "@", so preview a path only when it contains none and does not start with // or /\; such paths stay on complete pages. The parser check is no longer needed.

* fix(web-capture): never promote a redacted pointer to a mitmproxy headline

Inline or imported Evidence can carry a value at a pointer that its own redactions mark as sensitive. Skip any mitmproxy headline whose pointer has a redaction entry, so a preview never re-emits a value the record says was redacted.

* fix(web-capture): check integer headline types before comparing sidecar literals

The sidecar comparison called String(value) before the numeric shape check, so an inline object such as {"toString": null} at request.port threw a TypeError out of the view instead of being omitted. Require a number before canonicalizing it.

* fix(web-capture): honor ancestor value redactions before selecting mitmproxy headlines

A value redaction at /request or the record root excludes its whole subtree, but only exact-pointer matches were honored, so contradictory inline Evidence could still preview /request/host or /request/path. Treat a value redaction as covering every descendant pointer; a property-name redaction still only identifies the parent of an omitted key and hides no sibling values.

* fix(web-capture): treat redacted byte descriptors as exclusions for mitmproxy headlines

Producers mark excluded bytes with a redacted binary descriptor as well as a redaction entry, and the record schema accepts either facet alone. Skip a mitmproxy headline when a redacted descriptor covers its pointer, so contradictory Evidence that keeps the value cannot preview it.

* fix(web-capture): refuse a redacted flow type as the mitmproxy HTTP discriminator

Contradictory Evidence can keep type "http" while marking /type redacted. Apply the same redaction check to /type before treating the flow as HTTP, so no mitmproxy headline is added when the flow kind is unavailable.

* fix(web-capture): refuse mitmproxy headlines under contradictory numeric sidecars

A numeric sidecar records that the producer held a number at that pointer. Treat the flow as HTTP only when /type has no sidecar of its own, and promote a mitmproxy headline only when no ancestor (the record root, /request or /response) carries one.

* fix(web-capture): require a textual flow type before adding mitmproxy headlines

mitmproxy's flow state carries type as a string. When the native value was bytes, the bridge shows the UTF-8 display text "http" next to a retained binary descriptor, so the producer did not supply the discriminator mitmproxy defines. Treat the flow as HTTP only when /type has no numeric sidecar and no binary descriptor at or above it.

* fix(web-capture): refuse mitmproxy headlines under byte-valued ancestors

The mitmproxy bridge emits either a byte descriptor for a value or children for a dictionary, never both. Treat a byte descriptor on an ancestor like a numeric sidecar, and refuse a byte descriptor on an integer headline, while keeping the exact descriptor of byte-backed string fields such as the request path.

* fix(web-capture): check child facets and byte provenance of mitmproxy headlines

A facet beneath a scalar coordinate says the producer held a container there, so refuse the flow type or a headline field with a numeric sidecar, byte descriptor or redaction below it. A string headline may carry at most one byte descriptor, and only retained mitmproxy producer bytes whose content is the value's UTF-8; HAR content bytes or mismatched content keep it out.

* fix(web-capture): verify the byte count and digest of byte-backed headlines

The record schema checks a descriptor's length and digest shapes, not their relation to its content. Require a byte-backed string headline's descriptor to match the value's UTF-8 in content, byte count and SHA-256 before promoting it.

* fix(web-capture): reject byte-backed headlines without a UTF-8 form

Buffer.from replaces an unpaired surrogate with U+FFFD bytes, so a descriptor for those bytes matched a string no producer could have decoded. Require the value to round-trip through UTF-8 before comparing it with its descriptor, as CaptureRedaction already does.

* fix(web-capture): index mitmproxy facets once and refuse non-UTF-8 targets

Reject host, authority and path text containing an unpaired surrogate whether or not a byte descriptor backs it, since no producer could have reported such text. Build a per-record index of facet pointers (sidecars and descriptors by pointer, scalar pointers, container ancestors, withheld values and omitted names) once, only for mitmproxy HTTP records, so each headline check is a few set lookups instead of several passes over every facet array.

* fix(web-capture): bound the mitmproxy facet index to headline coordinates

Facet pointers have no length or depth bound, and recording every ancestor kept one set entry per prefix (a million for a 1 MB pointer). Record only the fixed headline coordinates (/type and the request/response target and status fields) that have a facet beneath them, examining prefixes no longer than those coordinates.

* fix(web-capture): index only facets at headline coordinates and their ancestors

Grouping every numeric sidecar and byte descriptor allocated an entry per facet even though only /type, the request/response headline fields and their ancestors are queried. Keep facets only at those pointers (at most two per pointer, which already decides every check), and let other facets merely mark the headline coordinates they lie beneath, using prefix comparisons that derive no strings.

* test(web-capture): pin headline omissions and controls beside many unrelated facets

The bounded facet index must not change what a mitmproxy preview withholds or keeps. Beside 20,000 unrelated sidecars, a redacted host and a path with a facet beneath it stay withheld while the scheme, port and status remain, with only their own sidecars retained. The test passes against both the current main projection and this change.

* test(web-capture): count long set additions in the million-character pointer tests

The two million-character pointer tests had no time or size bound, so on
Node 22 and 24 they passed against the old ancestor index too, and only
Node 26 caught a regression by running out of memory. Count the strings
longer than 1,000 characters the preview adds to sets instead: only the
pointer and strings cut from it are that long, the old index added
999,000 and 499,507 of them, and the new index adds none.

---------

Co-authored-by: Lucien <[email protected]>
Co-authored-by: morluto <[email protected]>
Co-authored-by: Kaoru0822 <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-11-06 14:53:14 +08:00
gongfppandgongfpp 08bc52104a fix(cli): preserve Unicode edge whitespace in Markdown tables (#8915)
* fix(cli): preserve Unicode edge whitespace in Markdown tables

* fix(cli): bound Markdown whitespace encoding to linear time

---------

Co-authored-by: gongfpp <[email protected]>
2026-11-06 14:53:03 +08:00
RRFZ 6430f395a8 fix(cli): reject non-decimal pyc and BEAM limit options (#9060)
inspect-python-pyc and inspect-erlang-beam took their max-* limits through Number(), so 0x10 meant 16 and 1e2 meant 100. Admit decimal text only through cliDecimalField, which now keeps the shared field's description for help.
2026-11-06 14:52:53 +08:00
Christopher Pruijsen c9797c5dd5 fix(native): keep inspect_macho usable when dyld_info refuses the slice (#9034)
* fix(native): keep inspect_macho usable when dyld_info refuses the slice

dyld_info exits 1 with 'unknown platform' for Mach-O files without a
platform load command and with 'old arm64e bind opcodes not supported'
for pre-ABI arm64e binaries. inspect_macho then failed outright with the
generic execution_failure remediation although nm, otool and the load
table still describe the slice. Treat these two refusals like the
missing-UUID refusal: imports are reported unavailable, exports keep the
nm observations, and the dyld_info diagnostic is recorded as a limitation.

* test(native): cover dyld refusal fallback and limitations
2026-11-06 14:52:42 +08:00
hhangandhhang d77442228d fix(desktop): align exported XML formatting whitespace (#9020)
* fix(desktop): align exported XML formatting whitespace

* test(process): align symlink comparison snapshots

* test(boundary): align inherited CLI and MCP fixtures

---------

Co-authored-by: hhang <[email protected]>
2026-11-06 14:52:32 +08:00
Hasan ZayniandClaude Sonnet 5.5 6571be631a fix(decompile): keep the output failure streak across external functions (#7924)
* fix(decompile): keep the output failure streak across external functions

An external function writes no file, but the streak counter treated its outcome as a success and reset to zero. With external functions interleaved between unwritable local functions, decompile_all never reached the documented five-failure stop. The streak now ignores external outcomes.

Fixes #7374

* style(decompile): restore the repository formatter output

oxfmt --check rejected decompileAll.ts after a local prettier pass reflowed an unrelated type alias. Restore it so the diff is only the streak change.

* ci: retrigger after artifact-download ECONNRESET

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>

---------

Co-authored-by: Claude Sonnet 5.5 <[email protected]>
2026-11-06 14:51:47 +08:00
ShuaixinHuang f624c3b8e3 test(desktop): make Python output fixtures portable (#9066)
* test(process): align symlink fixture with filesystem checkpoints

* test(frida): assert the CLI spawn Evidence contract

* test(mcp): use an unsupported analysis-view parent

* test(browser): separate source-location line bounds from parity

* test(desktop): make Python output fixtures portable
2026-11-06 14:41:44 +08:00
ShuaixinHuang d3a4fc87cd fix(cli): stop completion after the option terminator (#9064)
* test(process): align symlink fixture with filesystem checkpoints

* test(frida): assert the CLI spawn Evidence contract

* test(mcp): use an unsupported analysis-view parent

* test(browser): separate source-location line bounds from parity

* fix(cli): stop completion after the option terminator
2026-11-06 14:37:29 +08:00
ShuaixinHuang 6993e4ac73 fix(managed): explain single-type member capacity exhaustion (#9065)
* test(process): align symlink fixture with filesystem checkpoints

* fix(managed): explain single-type member capacity exhaustion

* test(frida): assert the CLI spawn Evidence contract

* test(mcp): use an unsupported analysis-view parent

* test(browser): separate source-location line bounds from parity

* fix(managed): count selected types in capacity guidance
2026-11-06 14:37:16 +08:00
RRFZ a74ce4512c fix(cli): reject non-decimal desktop application limits (#9061)
inspect-desktop-application took --max_entries and --max_probe_bytes through Number(), so 0x1 examined one entry and 1e1 ten. Admit decimal text only and keep the shared fields' bounds and defaults.
2026-11-06 14:34:07 +08:00
RRFZ 415ddb74c0 fix(cli): publish the GPU inventory digest format in the CLI schema (#9062)
inspect-gpu-code-objects declared --expected_sha256 as any string. The CLI schema accepted an upper-case digest, as PowerShell's Get-FileHash prints it, and the command then refused it as invalid_request. Reuse the shared lowercase SHA-256 field so argument parsing refuses it with the CLI field name.
2026-11-06 14:33:56 +08:00
ShuaixinHuang 34b954ec8a test(cutter): synchronize peer descriptor close before cancellation (#9082)
* test(cutter): synchronize peer descriptor close before cancellation

* test(process): align symlink fixture with filesystem checkpoints

* test(frida): assert the CLI spawn Evidence contract

* test(mcp): use an unsupported analysis-view parent

* test(browser): separate source-location line bounds from parity
2026-11-06 14:33:21 +08:00
ShuaixinHuang edd32e5af1 test(paths): isolate POSIX cwd fixture from the host platform (#9092) 2026-11-06 14:33:03 +08:00
ShuaixinHuang c92cbcaf28 fix(desktop): preserve significant keyfile value whitespace (#9077)
* fix(desktop): preserve significant keyfile value whitespace

* fix(desktop): preserve keyfile whitespace in exported verifier

* test(process): align symlink fixture with filesystem checkpoints

* test(frida): assert the CLI spawn Evidence contract

* test(mcp): use an unsupported analysis-view parent

* test(browser): separate source-location line bounds from parity
2026-11-06 14:27:41 +08:00
Ace c7b0e0dec6 feat(harmony): disclose the unread pkgContextInfo.json declaration (#9096)
* feat(harmony): disclose the unread pkgContextInfo.json declaration

A package that ships pkgContextInfo.json shows the entry in
unclassified_components, yet nothing said its isSO declarations were
skipped: bridge_candidates came only from the libs/<abi>/lib<name>.so
path rule. State that gap in limitations when the entry is inventoried,
so a path-convention hypothesis is not read as a package declaration.

The declaration-parsing half of #4489 needs a decision about who owns
reading inventoried file content; this projection receives identity and
digests only, and the shared inventory carrier guard rejects other
Evidence. No content is parsed here.

* test(harmony): assert the disclosure by its names, not its sentence

docs/testing.md requires asserting structured facts instead of freezing an
explanatory sentence, and says a replaced prose assertion must still pass on a
harmless wording change while failing when the fact is lost. The first version of
this test compared the whole limitation string verbatim, so any rewording of the
disclosure would have failed CI.

The assertions now require exactly one limitation naming pkgContextInfo.json,
carrying bridge_candidates, isSO, and the not-read/not-narrowed fact, and keep the
structural claim that the same package without the declaration file yields
identical bridge candidates and no such limitation.
2026-11-06 14:26:41 +08:00
hhangandhhang 8766bcef59 fix(desktop): retain UTF-16 seed content after the signature (#9098)
Co-authored-by: hhang <[email protected]>
2026-11-06 14:26:28 +08:00
highmachs 711b374d1c fix(native): retain defined global and weak ELF symbols with GNU version index 0 (#9100) 2026-11-06 14:26:00 +08:00
highmachs 15fad0efa3 fix(desktop): normalize XML attribute whitespace and line endings during seed scrubbing (#9099) 2026-11-06 14:25:26 +08:00
morlutoandCursor Agent 8580f1430c fix(desktop): launch spaced REA archives and Electron apps (#9085)
* fix(desktop): launch spaced REA archives and Electron apps

Link the unpacked REA entrypoint to a fixed path so a top folder with
spaces is not split by the generated rea launcher. When a build request
marks the app as Electron, rewrite its installed desktop command with
the export path's launch policy before gtk-launch.

* fix(desktop): escape rewritten Exec values for desktop files

Command quoting alone left literal backslashes one escape short of the
desktop-entry value layer, so gio rejected the rewritten entry. Escape
the command with the same rules parseDesktopEntry undoes before writing
the Exec line.

---------

Co-authored-by: Cursor Agent <[email protected]>
2026-11-06 14:07:01 +08:00
gensokyo-dev c467cf8685 fix(mcp): clarify discovery bounds and isolate catalog evals (#9101)
State the existing discovery page and exact-name bounds using the validation constants. Isolate account apps and plugins in catalog trials by default, with an explicit opt-in and one shared feature policy for config and receipts.
2026-11-06 05:48:06 +00:00
gensokyo-dev 3708538e85 feat(javascript): compare static endpoints with HAR captures (#9090)
* feat(javascript): compare static endpoints with HAR captures

* test(javascript): preserve original HAR fixture bytes during formatting

* test(javascript): load the byte-preserved HAR fixture
2026-11-06 13:34:26 +08:00
Edgar ArakelyanandClaude Opus 5.5 fc4c381dd7 feat(desktop): keep an agentenv computer up and drive it through REA's MCP tools (#9041)
* feat(desktop): keep an agentenv computer up and drive it through REA's MCP tools

- A Python helper keeps one agentenv computer up per build with
  run_bundle(keep=True) and reports its address, MCP URL, control token
  and sandbox id; it tears the computer down when stdin closes.
- AgentEnvComputer owns the helper, waits for readiness, holds one MCP
  session to the computer's computer_* tools, records through the
  computer-recording routes, and removes the sandbox's containers by
  label when agentenv cannot confirm teardown. The token never leaves it.
- start_build_computer starts a computer from a request (archive package,
  apt repository or .deb, opened on demand with REA in /opt/rea) or from
  a bundle folder; act_on_build_computer forwards screenshot, click,
  drag, scroll, type, key and wait and returns the driver's images
  unchanged; close_build_computer tears it down.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): remove a build computer's container by name when agentenv cannot confirm teardown

agentenv's local provider runs an ordinary computer as agent-<sandbox id>
without the agentenv.sandbox label, so the label-only fallback found
nothing and reported a running computer as removed. The fallback now
lists containers by exact name and by label. The real test holds its
recording open long enough for a first frame, checks the container by
name, and runs rea inside the computer; the guide and tool descriptions
say the app opens from the Applications menu and that a screenshot after
opening a menu should come from a wait.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): own failed build-computer starts, keep ids past oversized screenshots, bound recordings

- A start is owned from the helper's spawn. One that fails and whose
  teardown is unconfirmed stays held under a computer_id close can retry;
  closeAll refuses new starts, cancels and joins starts in progress, then
  closes every held computer.
- When the first screenshot is too large, the error carries REA's result
  with the computer_id, and start_build_computer takes screenshot region,
  format and quality.
- Recordings are refused before reading when the manifest's total exceeds
  the allowance, then fetched one segment at a time, each read bounded and
  checked against its size and sha256.
- The helper keeps the tail of its output (a new tail retention mode in
  ProviderProcessSupervisor), so a build's own error is not crowded out.
- A helper that reported no computer exits on its own before SIGTERM, so
  no phantom owner is kept.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): bound a build computer's action log

Every action kept its full arguments for the computer's whole life, so a long
session grew REA's memory without limit. The log now keeps the latest 256
actions within 256 KiB, cuts each string to 1 KiB and each list to 64 items
(naming what it cut), and keeps the total and the last applied index of each
kind of action, so rules over the whole session hold after entries drop.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-11-06 11:01:11 +08:00
RRFZ de8b2f3592 fix(pe): reject malformed and repeated certificate extensions (#8928)
RFC 5280 4.1 defines an Extension as extnID, an optional critical
BOOLEAN and an extnValue OCTET STRING. Section 4.2 says a certificate
MUST NOT include more than one instance of an extension.

The decoder only checked that extensions were a SEQUENCE. Any entry,
and any repeat, was accepted.
2026-11-06 10:15:05 +08:00
RRFZ b2672d8727 fix(pe): reject a SignerInfo version that does not match its sid (#8924) 2026-11-06 10:14:35 +08:00
RRFZ 4b9392e41c fix(pe): reject certificate versions and fields RFC 5280 does not allow (#8921) 2026-11-06 10:14:15 +08:00
RRFZ f1212b9e4e fix(cli): reject non-decimal native UI and command deadline options (#8920) 2026-11-06 10:13:45 +08:00
Afloat 3eff8065e7 fix(process): preserve UTF-8 at diagnostic truncation boundaries (#8907)
* fix(process): preserve UTF-8 at diagnostic truncation boundaries

* fix(process): preserve malformed UTF-8 excerpt evidence
2026-11-06 10:13:14 +08:00
Afloat a3621e5e1f fix(process): resume buffered NDJSON frames on partial chunks (#8909) 2026-11-06 10:12:43 +08:00
Afloat b153ba8ddd fix(process): honor cancellation at bounded file read completion (#8908) 2026-11-06 10:11:56 +08:00
Dragolone b28b1e0ae2 fix(adb): list devices adb reports with bare no-permissions text (#8905)
adb prints the no-permissions state as "no permissions[ (reason)]; see [URL]"
and omits the reason when it finds no udev problem to name, for example on a
Linux host without a plugdev group. The state matcher required whitespace after
"no permissions", so that line counted as unrecognized and list_adb_devices
refused the whole listing, hiding every other attached device.

Accept ";" after this one state. Other states still need a whitespace or
end-of-line boundary.
2026-11-06 10:11:24 +08:00
Edgar ArakelyanandClaude Opus 5.5 c363f3e27b feat(desktop): pick seed and end by checkpoint, and keep the capture host out of bundles (#9037)
* feat(desktop): capture a Linux desktop application in use

Add `capture-desktop-application`, a CLI-only, Linux and X11 capture that
launches an installed desktop application, watches it while it is used and
writes capture Evidence plus SHA-256 addressed content:

- inventory: desktop entry, launch command, executable, package and its
  apt sources (or snap, flatpak, AppImage), Electron markers and candidate
  state directories;
- session: the launched process tree, client windows from xprop with the
  first application window time and a window match, and files held open;
- state: a stat walk of the home directory before launch, at checkpoints
  read from stdin, at stop and after exit, with created, modified, deleted
  and retyped paths and the content of changed files and state directories.

It refuses non-Linux hosts, missing DISPLAY or xprop, a non-empty output
folder and an application whose window is already open, and signals only
processes it started.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): own launched processes, confirm walk coverage and keep cancelled captures

- Launch through the owned provider path with a run token; release with
  SIGTERM, then the token-verified group cleanup, after every session.
  Processes without the token are never signalled; each records whether it
  carries it, and the session records whether cleanup was verified.
- Report creations and deletions only where a walk listed the nearest
  existing directory; count the rest as unconfirmed.
- Retain a file only when it is still the walked version, through owned
  no-follow reads; charge the budget only for new content; create files
  exclusively in a private output folder owned by the caller.
- Walk content roots outside the home directory, compare paths in their
  physical spelling, and run executables from the caller's directory.
- Refuse when the window list cannot be read, re-evaluate window ownership
  each sample, handle one queued request per sample with a bounded stdin
  queue, and write the capture with end reason `cancelled` on interruption.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): keep owned cleanup failures and fresh digests

- Allow a capture cancelled before any requested checkpoint to hold only the
  baseline.
- Retry an incomplete stop once; if cleanup is still unverified, write the
  capture and fail with the run token and process group as cleanup
  resources.
- Classify path changes after a walk (ENOTDIR and other selected-file
  errors) as changed_since_walk instead of failing the capture.
- Treat a non-directory ancestor as proof that nothing existed beneath it.
- Report a before digest only for the file version it was read from.
- Derive stderr_truncated from stderr's own byte counts.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* wip(desktop): state view over a capture folder

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): keep failed file-handle closes owned for retry

Content reads pass retainFailedClose, so a handle whose close fails stays
owned by the content store; the capture retries those closes after it is
written and fails with the output folder as the cleanup resource if one
still cannot be closed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* feat(desktop): read a captured application's state

Add inspect-desktop-application-state, a CLI command that reads a capture
folder into the application's state: every retained or changed file with
its role and reasons, a typed JSON view of each version (SQLite through the
shared worker, dconf through a private dconf dump, JSON, XML, key files,
text) and the kinds of change between versions (lines, values, keys, rows).

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): report process and handle cleanup failures together

When both the launched processes and a retained file's handle remain
unresolved after the capture is written, one cleanup error now lists both
reasons and both resource sets.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* feat(desktop): export a captured application as an agentenv computer app

Add export-desktop-application, which writes an agentenv bundle with one
computer_app environment from a capture folder and its state view: the
apt package, a boot launch matched on the captured window class with a
window ready check, owned paths from the state roles, a seed of the
baseline files, data hooks that call agentenv's add/get/reset helpers
with a typed JSON view on get, a skill, a README and a deploy smoke task.
The desktop CLI commands now load their services lazily.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): keep state views faithful to the capture

- Read key files, XML attributes, dconf groups and SQLite tables into
  dictionaries without inherited properties, so captured keys such as
  __proto__ stay data.
- Version databases with their write-ahead log as of each checkpoint, and
  stage only digest-verified bytes for the SQLite and dconf readers.
- Detect the format per version, report format changes, keep JSON null and
  empty containers as values, keep XML text and preserved whitespace, and
  mark truncated views (including XML depth) and comparisons of them.
- Number versions and changes by checkpoint index, decide baseline
  presence from the captured type, and keep tagged SQLite integers, text
  bytes and blobs distinct.
- Carry the capture's limitations and omitted content forward, match state
  directories by their home path, index changes once per capture, keep
  failed handle closes owned, and finish the CLI through the shared owner.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): keep coverage, logs and handles honest in the state view

- Read coverage flags only from formats whose readers write them, so JSON
  fields named truncated stay data.
- Fail a SQLite version whose write-ahead log existed but was not retained
  or does not match its digest, instead of reading the database alone.
- Close retained content on every exit, including cancellation, and report
  a close that still fails together with the primary failure.
- Finish the CLI with the canonical operation name.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): export only what the computer can install and seed

- Refuse packages not from the Ubuntu 24.04 archive, since the computer
  installs by name from that archive.
- Start the launch command in the captured working directory.
- Respell the home path only in files the state view read as text.
- Report files that were not retained instead of dropping them silently.
- Load captured dconf keys for apps that own no files.
- Keep home-level files as owned paths; rename reserved Python class names.
- Mark truncated SQLite and text views in the class's get view, and read
  file types the way the state view does.
- Release the output tree through a resource scope, and read seeds through
  an owner closed on every exit.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): keep staged folders owned, symlinked state roots and XML values

- Stage database bytes in a private runtime root released through a
  resource scope, so a failed removal keeps its owner and is reported with
  the read's outcome.
- Report cleanup facts and owners from desktop failures instead of
  wrapping them as a plain adapter error.
- Record the state directories as walked, in their physical spelling, and
  match roles against both spellings, so files in a symlinked state
  directory are not mistaken for selected documents.
- Keep whitespace-only XML leaf values; drop only formatting between
  elements.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): read XML in the exported app the way the state view does

Keep whitespace-only leaf values and xml:space="preserve" text in the
generated get view; drop only formatting between elements.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): release retained handles with their owners, read older captures

- Release retained-content handles whose close failed through a resource
  scope on every exit; a release that fails again reports the exact handle
  owners. A read that finished keeps its bytes when only its close failed.
- Accept captures written before state directories were recorded, and say
  in the state view's limitations that symlinked state directories may be
  misclassed for them.
- Compute each XML element's whitespace context once instead of scanning
  siblings for every text run.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): boot without the working folder, bound XML, accept mirrors

- Try the captured working directory only when an argument is relative,
  and start the app even when that folder is missing.
- Cut the generated XML view at REA's node and depth limits, and keep one
  unreadable file from failing data/get.
- Accept Ubuntu 24.04 mirrors, not only the canonical hosts; PPAs and
  other repositories are still refused.
- Read seeds through the retained-content owner's new signature.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): state view review round

- Stage a retained -journal beside its database so the SQLite worker
  accepts an inactive journal and refuses a hot one; a -wal or -journal
  that existed but was not retained (including one omitted at the
  baseline) makes the version unreadable.
- Keep tables that read when another fails, and let cancellation and
  unfinished cleanup propagate instead of becoming view errors.
- Read text as a key file only when every line parses as one.
- Rank a file named in a window title or selected by the caller as a
  document even when the application holds it open; match titles on
  file-name boundaries; leave the display name out of dconf matching.
- Bound JSON and key file views (4 MiB, 500 levels, exact numbers) and
  report too-large, missing and altered retained content separately.
- Add no version or change while a database stays absent.
- Read staged databases in place instead of copying them again.
- Give Evidence the same limitations as the result.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): export review round

- Load captured dconf keys without D-Bus: install dconf-cli, compile the
  existing and captured key files into the user database, and warn on
  failure instead of stopping the server.
- Start relative-argument launches in the captured working directory,
  the home included, since the framework launches from the app folder;
  respell the home itself and option values under it.
- Read views in a thread, skip files over 64 MiB, align the generated
  readers with the state view's, and close SQLite connections.
- Never own the home or a shared base folder; map a symlinked state
  directory's files back to their home spelling.
- Report omitted files and unretained opened folders.
- Name the module so it cannot shadow another, and keep the class off
  the module's own names; derive names the bundle check accepts.
- Write seeds as they are read, and remove an unpublished bundle on
  failure.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): capture review round

- Ask the launch process group and token carriers to exit before
  killing them, on errors too; report incomplete cleanup with its facts.
- Count descendants of the launch as started by the capture even when
  their environment cannot be read; treat a reused pid as a new process.
- Follow symlinked desktop entries; name a flatpak's state after its
  application id; match flatpak windows by name only.
- Stop on end of input; refuse empty, duplicate and reserved checkpoint
  names; require whitespace after the checkpoint keyword.
- Stay on each walk root's device, and never walk or retain `/` or a
  directory containing the home; say so in the limitations.
- Refuse a window manager that does not publish _NET_CLIENT_LIST.
- Remove retained content when a capture fails before it is written.
- Skip reading a file larger than the remaining content budget.
- Record apt's archive origin for each package source.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): read an unset client list as no windows when supported

xfwm4 leaves _NET_CLIENT_LIST unset while it manages no windows, so the
capture refused an ordinary desktop with nothing open. Read
_NET_SUPPORTED with the list: an unset list means no windows when the
window manager supports it, and the capture is refused only when it
does not. `xprop`'s "no such atom" output now reads as unset too.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): accept packages by apt's archive origin

Accept a package source when apt reports its origin as Ubuntu for a
noble suite, so mirrors count and PPAs or other repositories that merely
have "ubuntu" in their URL do not. Captures made before origins were
recorded fall back to Canonical's own archive hosts. Seed reads report
why retained content could not be used.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* feat(desktop): capture and export apps from vendor apt repositories

A closed-source app installed from its vendor's apt repository, such as
Typora, can now be captured and exported as a computer app.

- The capture records each non-Ubuntu repository listing the installed
  version (uri, suite, components from apt's sources files, one-line or
  deb822) and the key that signs it: the signed-by key, or the trusted
  key that gpgv shows verifies the repository's signed index. The key
  is retained with the capture.
- The capture records the packages owning the libraries the executable
  loads (DT_NEEDED with RUNPATH and the linker cache, through REA's ELF
  readers), since a vendor package may not depend on them.
- The export declares a verified vendor repository as an AptSource with
  its key, installs a vendor package's library packages, and adds
  --no-sandbox to an Electron app's launch, since a container denies
  Chromium's sandbox.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): report a locked database at once in data/get

A database the running app holds locked made each data/get wait sqlite's
five-second busy timeout before reporting it. Record the Typora run in an
agentenv computer in the verification.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* perf(desktop): detect key files without splitting the whole text

Read lines one at a time and stop at the first that a key file view
would drop, instead of splitting a file of up to 64 MiB into arrays
before its format is known.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): keep symlinked homes and state folders under the home in exports

- Respell a path under its state folder's logical spelling only when that
  spelling is under the captured home, so a symlinked HOME keeps its
  files; pair walked and logical state folders only when the capture
  lists the same number, and match the innermost folder first.
- When two files map to one home path, keep the one present before the
  capture and name the other in the limitations.
- Read key files in the generated data/get with the state view's rule,
  checked against it by running the helper under python3.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): end captures on zombies and keep cleanup owners

- A zombie or dead process no longer keeps the session open.
- Remove only the partial capture file this run created.
- Keep the exact owners of handles and processes whose cleanup failed on
  the returned error, so a retry can release them; test the published
  capture's cleanup-failure branch with mixed outcomes and retries.
- Release the application once even when the exit checkpoint fails.
- A cancellation before the capture is written removes what it retained.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): vendor package review round

- Match apt sources through `apt-get indextargets`, which covers flat `./`
  and `/` suites and gives each release file and sources entry.
- Accept a key whose signature verifies even when the release carries
  another signature without a key here; keep only the verifying key from
  a keyring holding several (OpenPGP packet splitter).
- Install the package from its repository's release (`name/<release>`),
  declare one source per URI and suite, support only the capture host's
  architecture, and refuse Ubuntu Pro repositories.
- List only library packages the computer can install; walk libraries only
  for vendor installs, with RPATH inherited as ld.so does, normalized
  paths, and DT_NEEDED bounded while reading.
- Put --no-sandbox after the program an env wrapper launches; keep the
  gpgv folder's owner when its removal fails; respect --max-file-bytes for
  keys; wait 0.25 s on a busy SQLite database in data/get.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(desktop): keep the vendor spec's gpgv command on one line

A code span broken across lines left `<binary keyring>` for VitePress to
read as an unclosed HTML tag, so the docs build failed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* feat(desktop): add a validated task to exported desktop applications

Each capture can now yield one agentenv task in its exported bundle. An
agent writes the task (a prompt plus declarative checks over the app's
data/get views); REA validates it and stays model-free.

- `describe-desktop-application-task` summarises a capture's net change
  per file (role, format, views before and after) and drafts checks.
- Checks: file presence, lines present/absent/counted, JSON-pointer values,
  SQLite rows and counts. REA keeps a task only if a check fails on the
  seed and every check passes on the captured end state; a file that was
  not retained at the baseline counts as absent, as the computer starts.
- `export-desktop-application --task` writes tasks/NAME.json (deploy,
  agent, skills, recording, prompt, verifier), a compiled verify.py that
  reads data/get, and an eval. A boundary test runs the verifier under
  python3 and checks it agrees with REA's evaluator on every check kind.
- Docs, spec and skill guidance, with the solve-rate recipe.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* test(desktop): keep the seed test in its own block

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(desktop): record the task verifier driven in an agentenv computer

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(desktop): record the Typora task run in an agentenv computer

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): keep task checks bounded, complete and attributable

- line_count counts lines holding a substring (contains) instead of
  matching a regular expression, which could stall the verifier on
  agent-written text and read differently in Python.
- value_equals of a whole object or array is undecided on a truncated
  view.
- The draft keeps a database whose rows changed under an unchanged hash,
  and a file whose truncated views match while it changed.
- A task file that cannot be read keeps the reader's detail, and a failed
  cleanup keeps the read failure as its primary.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): leave a file with one version out of the task draft

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(desktop): update the task verification's test counts

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(desktop): record the task solve rate with Claude Code and Sonnet 5.5

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* feat(desktop): capture and export apps installed from a downloaded .deb

capture-desktop-application --package-file reads the file's control fields
with dpkg-deb and records its SHA-256, size and dependencies when it holds the
installed package; the file itself is never kept. The library walk now also
runs for local installs.

export-desktop-application --package-url declares agentenv's
Download(url, sha256) for that file, its dependencies and library packages as
apt packages, and a run script that installs it with dpkg.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): seed app-named dotfiles and respell UTF-16 seeds

The capture now treats dotfiles in the home named after the application
(.varicad-cfg.ini, .notesrc) as state roots, so a file the app only reads
during the capture is still seeded. The exported seeding respells the
captured home in UTF-16 text files with a byte-order mark, keeping the mark.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): hold readiness until the app's own window shows

An exported app is ready only once a window of its class that the taskbar
lists is shown, so a splash screen of the class, which skips the taskbar, no
longer counts as the app being up.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(desktop): record VariCAD captured, exported and run in an agentenv computer

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): keep exported views strict JSON for infinite and inexact numbers

The exported data/get spells infinite and negative-zero SQLite reals as REA's
SQLite reader does ("Infinity", "-Infinity", "-0"), and refuses a JSON file
holding a number that reading would change (1e999, integers past 2^53, -0,
rounded fractions) with the state view's own error, instead of a view with
an infinity in it.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): keep package-file facts tied to one file and URLs unechoed

- A --package-url refusal never repeats the URL, and credentials are checked
  before the scheme.
- The package file's identity must hold between dpkg-deb's read and the hash.
- A failed close keeps its cleanup owner even when the capture is cancelled.
- Readiness also ignores windows typed as splash screens.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): record database logs and keep state changes distinct

- A selected path that is not a directory brings its -wal and -journal as
  content roots, so the capture records whether they exist and retains them.
- The state view treats a file as absent only under recorded content roots
  and refuses a database whose log presence is unknown.
- JSON changes list containers too, so an array that becomes an object with
  the same members is a change.
- Key file entries escape the key as a JSON pointer segment, so [a/b] c and
  [a] b/c stay apart.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): keep watching through unread samples and late workers

- An unreadable window list is unknown, not zero windows, and is named in
  the limitations.
- Before ending the session, live run-token carriers no parent link reached
  are tracked; an unreadable process table leaves the exit unknown.
- An env prefix's assignments find the program and name its XDG folders.
- flatpak run options that take a value are skipped with it, -- is honoured
  and a /arch/branch suffix is dropped from the id.
- State directories include the entry's full id.
- Path options refuse empty values; roots count toward the entry limit.
- A capture file whose close failed stays owned and is retried on discard.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): bound apt sources, keep older captures and cleanup owners

- Sources files parse into one stanza per line or deb822 paragraph with its
  URIs and suites kept as lists, so no URI x suite product is built.
- Repository architectures and release are optional, so captures written
  before they were recorded parse; the export treats them as unrestricted
  and unknown.
- Library sources are matched against every index of a declared
  repository's URI, suite and components.
- Two failures side by side keep both causes and both cleanup owners, and a
  host file whose close fails reports a lease on its handle.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): match escaped key file entries and suggest containers through members

- The draft's key file lookup uses the state view's escaped `group/key`, so a
  key holding `/` or `~` still gets its suggestion.
- JSON and XML changes now list containers too; the draft suggests an added
  or removed container through its listed members, and a container only
  when none of its members is listed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): read relative PATH entries from the launch folder

A program, or an empty or relative PATH entry, is resolved from the folder the
program starts in, as the launch does, so `env PATH=. app` with the entry's
Path still finds the app.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* perf(desktop): compare JSON views on one shared path

JSON and XML changes are found by walking both trees with one shared pointer
path, building a pointer only for a listed value, so memory follows depth
rather than every container's full path. A pointer over 4096 characters is
counted but not listed. A 256-level document with 4000-character keys went
from about 251 MiB of heap to under 1 MiB.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): keep JSON Lines as text in the exported reader

The exported reader parses the whole text before an inexact number makes it
JSON with an error, so JSON Lines with an integer above 2^53 stay text as in
REA. Over-long integer spellings are unsafe without converting them. The
guide no longer claims large integers keep their value.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): keep the state-path help free of dash-led words

The CLI alias check reads `-wal` and `-journal` in help text as short flags,
so the help names the write-ahead log and rollback journal instead.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): never record or export apt source credentials

- Source URIs lose any user name and password as they are parsed, the way apt
  splits them, and so do index-target URIs and descriptions and the sources
  apt-cache policy lists, so matching works whether or not apt prints them.
- A repository whose entry held credentials is marked
  `uri_credentials_removed`, and the export refuses it, or an older capture
  whose URI still holds them, with a reason that names no URL.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): look programs up without normalizing their paths

Program paths and PATH entries are joined rather than resolved, so `..` after
a symbolic link means what it means to the launch. The lookup moves to its own
module.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): note inexact JSON numbers with a flag, not a list

The exported reader only needs to know whether any number was inexact, so it
keeps a boolean instead of every rejected spelling.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): refuse older captures that still hold apt credentials

A capture written before credentials were removed counted the credentialed
repository's libraries as installable, so the export refuses it whole and asks
for a new capture rather than declaring packages no source offers. Recorded
policy sources are redacted in export messages too.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* test(desktop): keep the credential fixture on an example host

A login on a real host reads as a secret to the commit scanner, so the older
capture's fixture uses apt.example.com like the other credential tests.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): tidy looked-up paths and search relative entries only for env

Empty and `.` segments are dropped from a looked-up program path, which keeps
`/snap/bin/` entries recognisable as snaps, while `..` is still left for the
kernel. Relative and empty PATH entries are searched only in the PATH an `env`
prefix sets, which travels with the exported launch; the host's own PATH does
not.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): keep excluded and unwalked database logs unknown

The capture records the paths every walk left out, and the state view treats
a log as absent only under content roots the baseline walked completely and
outside those exclusions, so an excluded or unvisited log leaves the database
unread rather than read without it.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): judge JSON numbers after duplicate keys settle

The exported reader marks an inexact number instead of flagging it during the
parse, then looks for marks in the parsed value, so a number a later duplicate
key overwrote no longer refuses the file, as in REA.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): redact a repository's policy source when it names it

An older capture whose policy source matched no sources entry kept that text
whole, and the export named the repository by it; the export now names it
without any user name or password.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): never signal the launch group on unknown membership

When the current read cannot observe a carrier's process group, the carrier
is signalled on its own instead of letting an older process-table row grant
a group-wide SIGTERM.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): look programs up in the launch's order and refuse host-relative finds

The lookup searches PATH as the launch does, an empty entry meaning the
launch folder, so inspection and launch name the same program. A program found
only through a relative or empty entry of the host's own PATH is refused,
since the exported computer would not have it; an env prefix's PATH travels
with the command.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): count unchecked paths and keep logs behind them unknown

Each walk counts the paths it could not stat and the directories on another
device it did not enter, and the checkpoint records it as `unchecked_paths`.
The state view treats a log as absent only when the baseline left nothing
unchecked, so a log whose stat failed stays unknown.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* perf(desktop): scan parsed JSON with one iterator per level

The check for marked numbers walks the parsed value with an iterator per
nesting level instead of copying each container's children; a million-entry
array went from about 15.7 MiB to 8.1 MiB of peak traced memory.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): start a relative program from its captured folder

An exported launch whose program path is relative, such as `./app` from an
entry's Path, now changes to the captured working directory first, as it
already did for relative arguments.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* feat(desktop): read zipped documents and name XML elements in task checks

- A file that starts as a zip archive gets a `zip` view listing its entries in
  archive order, each with its own format and view, within limits on entries,
  bytes and expansion; the exported Python reader matches it.
- A pointer segment applied to an array may be a selector, `[KEY=VALUE]` or
  `[@KEY=VALUE]` for XML attributes, matching exactly one member, so checks
  name elements instead of positions that shift between saves and versions.
- `value_equals` takes an optional numeric `tolerance`, comparing numbers and
  numeric strings.
- Changes inside zip views are listed by selector, and the task draft suggests
  name-addressed checks.

FreeCAD's `.FCStd` is the first case: a hole cut into a plate is verified from
the zipped `Document.xml` on files from both FreeCAD 0.21 and 1.0.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): record the desktop entry's package and app profiles in any case

- A launch from a desktop entry records the package that owns the entry, so
  the export installs the app the entry starts (Writer, not only
  libreoffice-common); the program's own package is kept as
  executable_package when it differs.
- State folders match the app's names ignoring letter case, as direct
  children of the XDG folders and the home, so GIMP's ~/.config/GIMP profile
  is seeded and reset; roles follow the same rule.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* feat(desktop): read namespaced XML alike in REA and the exported verifier

The exported Python reader now names XML elements and attributes as the
document spells them (svg, text:p, sodipodi:docname, xml:space), with
namespace declarations kept as attributes, which is what REA's view already
produced. Export therefore accepts checks into Inkscape SVGs, LibreOffice
content.xml and namespaced XML inside zips, and the draft suggests them.

expat replaces ElementTree: one pass with namespace processing refuses what
REA's parser refuses, and a streaming pass builds the view within the same
node and depth limits. Text splits at CDATA, comments and processing
instructions as REA's DOM splits it. Fixtures are real Inkscape 1.2.2 and
LibreOffice 24.2 saves.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): install the program's own package and keep unknown owners unknown

- When the desktop entry's package does not own the program it runs, the
  export installs the program's package too. The capture records why it
  cannot (executable_package_reason), and the export then refuses.
- The export owns a state folder in the letter case the app wrote it, as
  the state roles already did, so data/get and data/reset cover a profile
  folder the app created during the capture.
- A dpkg-query that fails or exits 2 or higher leaves the package unknown
  instead of reading as "no owner".

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): keep selector checks undecided on partial views and pair members one to one

- A selector check is undecided while an array it searched may be missing
  members: a cut zip listing, or a truncated XML or entry view. Both
  evaluators agree.
- Diffs pair the members of two versions one to one, so two new elements
  can no longer both match the same old one and hide an addition.
- The task draft descends into JSON values and readable entries added
  whole, and never suggests an element by its position, last step included.
- The export's namespace refusal also checks XML entries inside the value a
  pointer names, such as the whole listing.
- The exported reader keeps REA's 500-level JSON depth limit, so one deep
  entry no longer hides the rest of the archive, and treats an entry whose
  data runs past its declared size as damaged.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): check approved packages against the repositories the export keeps

The capture approved library packages and the program's package against
every repository with a verified key, but the export can still leave a
repository out: its key dropped by the content budget, an architecture
restriction, or a conflicting key. Each repository now records the packages
only it supplies (`supplies`). The export drops stranded library packages
with a limitation, and refuses when the program's own package is stranded.
Captures without `supplies` behave as before.

The install limitations move to appExportInstallLimitations.ts to keep
appExportInstall.ts under the line limit.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): compare type changes in place, keep empty values and unread entries honest

- When an array becomes another type, its members are compared with what
  their pointers name there, so values that are still present no longer
  look removed.
- The draft checks an added empty array or object by equality.
- The exported reader's JSON depth check keeps one iterator per level and
  copies no wide list, back to the memory peak before the check.
- An entry whose view could not be read is no `null` value: the draft
  suggests no check on it, and a check into its view fails in both
  evaluators with the read error.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): decide entry view checks only on entries with a view, and walk JSON depth by level

- A check into an archive entry's view needs an entry read in a format
  with a view. A binary or unread entry's `view` of `null` no longer
  passes a `null` or absence check, in either evaluator, and the draft
  suggests no absence inside an entry that ends without a view.
- REA's JSON depth check keeps one iterator per level instead of queuing a
  pair per member, so 800,000 nulls need 18 MiB instead of more than 64.
  The helper came with the state view; its Python twin was fixed here.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): refuse an export whose desktop entry runs a program no package owns

When a package owns the desktop entry but no package owns the program it
runs, such as one in ~/.local/bin first on PATH, the capture keeps the
entry's package and records why the program cannot be installed. Export
refuses instead of installing a package that does not bring that program.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): bound zip entry reads, export metadata checks, and diff by the evaluator's pointer rules

- The exported reader inflates a zip entry at most 64 KiB at a time and
  stops a byte past its declared size, so a forged small size no longer
  holds the whole stream in memory (77.5 MiB to 0.1 MiB on a 32 KiB bomb).
- The namespace refusal applies only to checks that depend on an entry's
  XML, so checks on an entry's name, size or digest, or on its absence,
  export.
- Diffs find a member's counterpart by the evaluator's pointer rules, so a
  selector step across a type change no longer reports a present value
  removed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): read at most 1,000 zip directory records, and index arrays once per diff

- Both zip readers now read the central directory a record at a time,
  keep the first 1,000 file records and count the rest, then hand the
  library an archive whose directory holds only those. Directories are
  told apart as zip.js does, so the Python reader stops listing
  attribute-marked directories as files. 600,000 entries now peak at
  1.3 MiB in Python (was 293.6 MiB) and need 24 MiB of heap in REA.
- The exported Python zip reader moves to appExportPythonZip.ts.
- Diffs look members up through an index built once per compared array,
  so 20,000 selector keys against 20,000 members take 31 ms, not 5.4 s.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* feat(desktop): pick seed and end by checkpoint, and keep the capture host out of bundles

- Export, task export and the task draft take --seed-checkpoint and
  --end-checkpoint, so one capture can make the seed in the app and then
  the edit; the defaults stay the baseline and the last version.
- Capture reads apt's lists folder and trusted keys from apt-config, so
  a user-space apt-get update under APT_CONFIG is found, and starts the
  app without AGENTENV_* variables.
- Capture records the host name and user. Export rewrites user@host, the
  host name as a whole name and the captured home in text, UTF-16 and
  dconf seeds, ships SEED_HOME=/home/user instead of the capture user's
  home, lists scrubbed and unscrubbable seeds, and refuses checks that
  name the host.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): check tasks against the shipped seed and scrub JSON and XML by value

- Task validation reads the seed as the bundle ships it, after the scrub,
  so a task whose end value the scrub writes into the seed is refused.
- JSON strings and XML text, attribute values, comments and CDATA are
  scrubbed as their decoded values and rewritten only when they change,
  so syntax stays valid and escaped identities are found.
- One leftover search covers every shipped file's path and contents,
  generated module and dconf keys included; the strict whole-name rule
  is kept for replacing only.
- A host named localhost is never replaced, but user@localhost is.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): resolve a record's selector keys together, and follow Zip64 locators without sentinels

- The diff resolves all of a record's selector-shaped keys in one lookup:
  conditions are normalized per field, selectors grouped by field set,
  and only the values asked for are kept, so retained memory grows with
  the keys, not members times field sets (68 MB to 5.5 MB, 76 MB to 4.9 MB
  on the reported cases).
- Both zip readers follow a Zip64 locator just before the end record
  whatever the classic record holds, as zipfile does, so valid Zip64
  archives with ordinary classic values read again. The exported
  module's synthetic tail carries a real Zip64 end record, so bytes that
  only look like a locator no longer make zipfile refuse it.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): scrub JSON values only, with a scanner whose stack does not grow with string length

JSON seeds are walked one string at a time, finding each closing quote
with indexOf and a backslash count instead of a backtracking regex, so a
16,000,000-character string no longer overflows the stack. Member names
are never rewritten, so two keys cannot collide; a name holding the host
leaves the file in unscrubbed_files.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): scrub dconf and UTF-16 seeds by format, search check values for the host, and gate checks on the selected ends

- dconf: the host scrub ran over the whole key file, so on a host named
  `true` `wrap=true` became an invalid `wrap=localhost`. Only decoded GVariant
  string values are scrubbed now; keys, groups, bytestrings, dictionary keys
  and object paths ship as captured and are reported.
- Task identity: the host search read the serialized check, whose field
  names (`file`, `kind`) and default ids matched hosts of those names. It now
  reads the check's string values only.
- Value checks: the text-file refusal read every recorded version; it now
  reads only the shipped seed and the end.
- UTF-16 seeds: the plain scrubber renamed JSON keys and XML names; the
  decoded text's format now picks the scrubber, keeping the byte order mark.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): pair members by identity first, build selectors lazily, and refuse oversized and deep text before building a view

- Pairing: an element with an identifier (name, id, xid, *:id) pairs only by
  it, so an unchanged id still names an edited element instead of a removal
  and an addition, and two objects of one type are never paired as a rename.
  Selectors keep the tag apart from each attribute condition and spell only
  the chosen one, so a long tag with many attributes no longer multiplies.
- Size: REA's detection and the exported reader decide JSON with a scanner
  that builds no value, and refuse a view over the parsed-view limit before
  building it, in files and zip entries alike.
- Deep text: the exported reader confirms the whole text is JSON before
  reporting a nesting depth error, as REA reads it.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): count a selector's matches over every sibling while identified elements choose only their identifiers

Identity-first pairing also dropped an identified sibling's other
attributes from the counts that prove a selector names one member, so a
shared attribute such as `color` or `fill` could be chosen though it matched
two siblings: the diff reported present elements removed and drafts lost
their checks. Counting now covers every attribute of every sibling; only the
choice is limited to an identified element's tag and identifiers.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): tell a UTF-16 seed's format without building it, and scrub XML without one piece per token

Choosing the scrubber for a decoded UTF-16 seed ran the full state
detection, which built the whole XML document (or JSON value), so a large
seed within the capture limit could exhaust memory at export. A syntax pass
now decides the format (the JSON token scan, and the XML parser's events
keeping only the open elements), and the XML scrubber copies out only the
spans it changes.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-11-06 10:01:25 +08:00
a7574d4274 fix(filesystem): settle a Windows ChangeTime before recording a baseline (#8979)
* fix(filesystem): settle a Windows ChangeTime before recording a baseline

Windows advances a file's ChangeTime shortly after it is written while
security components attach catalog and file-hash extended attributes. A
baseline captured during that advance disagrees with the next observation,
so an untouched file was reported as changed: "Root artifact changed before
inventory" and "Selected input changed while being read".

Dropping ctime on Windows is not an option because it is the only field
that catches a same-size rewrite whose modification time was restored.
Instead, keep ctime at full strength and stop capturing it mid-flight: on
win32 a ChangeTime still inside the drift window is observed again until two
consecutive observations agree, and the settled value becomes the baseline.

Applied where a baseline is captured and later compared: openRegularFile's
admitted metadata and the inventory root snapshot. The exhaustive process
capture walk is left alone, since settling there would delay every freshly
written file in the tree.

Refs #8975

Co-Authored-By: Claude Sonnet 5 <[email protected]>

* fix(filesystem): wait out the drift window instead of a repeated value

Settling on two matching observations was not enough. Two reads taken before
Windows performs its delayed ChangeTime update agree with each other and
disagree with every later one, so an unchanged file still failed a later
check: the reported drift lands 0.3-1.0 s after the write, well past a pair
of reads 25 ms apart.

Decide on the observation's age in the drift window instead, and keep
observing until the ChangeTime itself has aged out. Both the first read and
the empty-observation path now go through the same window check.

The window-exit test could not fail before: its clock was frozen, so the
window never closed and the exit branch never ran. Advance the clock while
sleeping and cover a match that precedes a delayed update.

Refs #8975

Co-Authored-By: Claude Sonnet 5 <[email protected]>

* fix(filesystem): configure Windows ChangeTime settle limits

REA_WINDOWS_CHANGE_TIME_DRIFT_MS, REA_WINDOWS_CHANGE_TIME_SETTLE_POLL_MS, and REA_WINDOWS_CHANGE_TIME_SETTLE_MAX_POLLS keep today's 2000 ms, 25 ms, and 40-poll defaults. Invalid values name the setting, current value, source, and range, and say to restart REA.

* fix(filesystem): settle on the selected environment and stop when cancelled

`settleChangeTimeBaseline` resolved its limits from `process.env`, but the
inventory already carries the environment its owner selected. A Windows
runtime with explicit settle settings therefore ran on ambient values or
the defaults, and an invalid ambient value could fail a runtime whose own
configuration was valid. `scanCanonical` now passes `options.environment`
through.

The wait loop also never observed the owning operation's signal, so a
cancelled inventory could keep polling for the whole drift window — up to
a minute at the configured maximum — and closing the artifact scope waited
on it. The loop now checks the signal before each poll and after the
sleep, and both call sites pass theirs.

Refs morluto/rea#8979

* fix(filesystem): keep the caller's typed error on cancellation

Checking the signal was not enough: `throwIfAborted` threw the raw
`signal.reason`, which `openRegularFile` passed through unchanged. Its
callers recognise cancellation through `RegularFileOperationCancelled`, so
a cancelled admission surfaced as an `ArtifactReaderFailure` with reason
`io` instead of a cancellation.

Let the seam supply the error a caller matches on, and have
`openRegularFile` pass its own cancellation type.

Refs morluto/rea#8979

* fix(filesystem): keep inventory cancellation typed during ChangeTime settle

scanCanonical passed the abort signal into settleChangeTimeBaseline without a cancellationError, so an aborted inventory threw the raw AbortError and analyze reported execution_failure. Inventory now throws ArtifactReaderFailure reason cancelled, which callers already translate as cancellation.

* fix(filesystem): skip ChangeTime settling for private PYZ images

External admission still waits out a fresh Windows ChangeTime. A private image REA just wrote is recorded as written, so a compressed PYZ spool does not add about a second per image.

---------

Co-authored-by: 2SinceJuly <[email protected]>
Co-authored-by: Claude Sonnet 5 <[email protected]>
Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
2026-11-06 07:57:14 +08:00
gensokyo-dev 0beedcd712 perf(mcp): add opt-in hot/cold tool discovery (#9046)
Add per-connection discovery that loads canonical named tools on demand while preserving the default full catalog. Bound discovery replies before changing visibility, preserve family exclusions and Evidence workflows, and reset visibility on HTTP reconnect.

Verified with focused protocol tests and real Codex CLI artifact investigations; token costs remain task-dependent, so the profile is opt-in.
2026-11-05 20:27:31 +00:00
APZNandgensokyo-dev ad518cff65 feat(artifacts): select extraction entries and output destination (#4031)
* feat(artifacts): select extraction entries and output destination

Refs morluto/rea#3490

* fix(artifacts): omit absent selection from execution parameters

* fix(artifacts): protect source directories and verify public selection

* fix(artifacts): keep optional selection inputs JSON-safe

* fix(artifacts): separate reader ownership from selected destinations

Validate complete reader inventory independently from collisions among selected output entries. Use occurrence-parent ownership instead of filename prefixes so independently owned PyInstaller entries remain selectable while nested-reader members remain refused. Validate advertised MCP schemas through the shared acceptance helper and add real PyInstaller/ASAR and excluded-entry regressions.

* fix(artifacts): advertise absolute extraction destinations

Reuse the shared host-absolute local path schema instead of an opaque refinement so SDK-converted JSON Schema preserves the runtime constraint. Extend the advertised-path and MCP-handler regression matrix for extract_artifact.output_root.

* fix(contracts): advertise unique extraction occurrence IDs

---------

Co-authored-by: gensokyo-dev <[email protected]>
2026-11-06 03:28:05 +08:00
gensokyo-devandgensokyo-dev 8084916859 perf(eval): measure MCP catalog usage through the real Codex harness (#9035)
* perf(eval): measure MCP catalog usage through real Codex sessions

* fix(eval): control the active Codex code mode host

* fix(eval): reject unavailable host controls without hiding client errors

* test(eval): cover the supported default and actionable client errors

* docs(eval): record pinned Codex catalog and real package measurements

* fix(eval): pin matrix identity and retain failed trial observations

---------

Co-authored-by: gensokyo-dev <[email protected]>
2026-11-06 02:25:34 +08:00
+27 fb453f2930 fix(mcp): support retained pseudocode summaries and paging (#5046)
* refactor(mcp): share official tool evidence registration

Route official tools through registerEvidenceTools and remove the duplicate
execution, progress, and Evidence delivery pipeline.

Preserve relationship coverage in the shared path and reuse the existing
source Evidence recording helper.

Validation: typecheck and 29 focused MCP tests passed.
Real native providers were not exercised.

* fix(mcp): support retained pseudocode summaries and paging

Allow procedure_pseudo_code to return an opt-in summary linked to its
retained complete Evidence. Extend inspect_analysis_view to page the
original pseudocode without repeating provider analysis.

Preserve default complete output and exclude delivery detail from
provider parameters and source Evidence identity.

Add regression coverage for large results, Unicode, null and empty
output, missing retention, transport-overflow recovery, and CLI paging.

Validation: 178 focused tests, build, check, docs:check, and test discovery
passed. Real stdio simulation reconstructed 98,000 code units exactly.
Real Ghidra, iTunes, and Claude Code verification remains unavailable.

Fixes #5032

* test(mcp): scope pseudocode contract lookup to official tools

* feat(desktop): capture and export apps installed from a downloaded .deb (#8954)

* feat(desktop): capture a Linux desktop application in use

Add `capture-desktop-application`, a CLI-only, Linux and X11 capture that
launches an installed desktop application, watches it while it is used and
writes capture Evidence plus SHA-256 addressed content:

- inventory: desktop entry, launch command, executable, package and its
  apt sources (or snap, flatpak, AppImage), Electron markers and candidate
  state directories;
- session: the launched process tree, client windows from xprop with the
  first application window time and a window match, and files held open;
- state: a stat walk of the home directory before launch, at checkpoints
  read from stdin, at stop and after exit, with created, modified, deleted
  and retyped paths and the content of changed files and state directories.

It refuses non-Linux hosts, missing DISPLAY or xprop, a non-empty output
folder and an application whose window is already open, and signals only
processes it started.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): own launched processes, confirm walk coverage and keep cancelled captures

- Launch through the owned provider path with a run token; release with
  SIGTERM, then the token-verified group cleanup, after every session.
  Processes without the token are never signalled; each records whether it
  carries it, and the session records whether cleanup was verified.
- Report creations and deletions only where a walk listed the nearest
  existing directory; count the rest as unconfirmed.
- Retain a file only when it is still the walked version, through owned
  no-follow reads; charge the budget only for new content; create files
  exclusively in a private output folder owned by the caller.
- Walk content roots outside the home directory, compare paths in their
  physical spelling, and run executables from the caller's directory.
- Refuse when the window list cannot be read, re-evaluate window ownership
  each sample, handle one queued request per sample with a bounded stdin
  queue, and write the capture with end reason `cancelled` on interruption.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): keep owned cleanup failures and fresh digests

- Allow a capture cancelled before any requested checkpoint to hold only the
  baseline.
- Retry an incomplete stop once; if cleanup is still unverified, write the
  capture and fail with the run token and process group as cleanup
  resources.
- Classify path changes after a walk (ENOTDIR and other selected-file
  errors) as changed_since_walk instead of failing the capture.
- Treat a non-directory ancestor as proof that nothing existed beneath it.
- Report a before digest only for the file version it was read from.
- Derive stderr_truncated from stderr's own byte counts.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* wip(desktop): state view over a capture folder

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): keep failed file-handle closes owned for retry

Content reads pass retainFailedClose, so a handle whose close fails stays
owned by the content store; the capture retries those closes after it is
written and fails with the output folder as the cleanup resource if one
still cannot be closed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* feat(desktop): read a captured application's state

Add inspect-desktop-application-state, a CLI command that reads a capture
folder into the application's state: every retained or changed file with
its role and reasons, a typed JSON view of each version (SQLite through the
shared worker, dconf through a private dconf dump, JSON, XML, key files,
text) and the kinds of change between versions (lines, values, keys, rows).

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): report process and handle cleanup failures together

When both the launched processes and a retained file's handle remain
unresolved after the capture is written, one cleanup error now lists both
reasons and both resource sets.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* feat(desktop): export a captured application as an agentenv computer app

Add export-desktop-application, which writes an agentenv bundle with one
computer_app environment from a capture folder and its state view: the
apt package, a boot launch matched on the captured window class with a
window ready check, owned paths from the state roles, a seed of the
baseline files, data hooks that call agentenv's add/get/reset helpers
with a typed JSON view on get, a skill, a README and a deploy smoke task.
The desktop CLI commands now load their services lazily.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): keep state views faithful to the capture

- Read key files, XML attributes, dconf groups and SQLite tables into
  dictionaries without inherited properties, so captured keys such as
  __proto__ stay data.
- Version databases with their write-ahead log as of each checkpoint, and
  stage only digest-verified bytes for the SQLite and dconf readers.
- Detect the format per version, report format changes, keep JSON null and
  empty containers as values, keep XML text and preserved whitespace, and
  mark truncated views (including XML depth) and comparisons of them.
- Number versions and changes by checkpoint index, decide baseline
  presence from the captured type, and keep tagged SQLite integers, text
  bytes and blobs distinct.
- Carry the capture's limitations and omitted content forward, match state
  directories by their home path, index changes once per capture, keep
  failed handle closes owned, and finish the CLI through the shared owner.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): keep coverage, logs and handles honest in the state view

- Read coverage flags only from formats whose readers write them, so JSON
  fields named truncated stay data.
- Fail a SQLite version whose write-ahead log existed but was not retained
  or does not match its digest, instead of reading the database alone.
- Close retained content on every exit, including cancellation, and report
  a close that still fails together with the primary failure.
- Finish the CLI with the canonical operation name.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): export only what the computer can install and seed

- Refuse packages not from the Ubuntu 24.04 archive, since the computer
  installs by name from that archive.
- Start the launch command in the captured working directory.
- Respell the home path only in files the state view read as text.
- Report files that were not retained instead of dropping them silently.
- Load captured dconf keys for apps that own no files.
- Keep home-level files as owned paths; rename reserved Python class names.
- Mark truncated SQLite and text views in the class's get view, and read
  file types the way the state view does.
- Release the output tree through a resource scope, and read seeds through
  an owner closed on every exit.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): keep staged folders owned, symlinked state roots and XML values

- Stage database bytes in a private runtime root released through a
  resource scope, so a failed removal keeps its owner and is reported with
  the read's outcome.
- Report cleanup facts and owners from desktop failures instead of
  wrapping them as a plain adapter error.
- Record the state directories as walked, in their physical spelling, and
  match roles against both spellings, so files in a symlinked state
  directory are not mistaken for selected documents.
- Keep whitespace-only XML leaf values; drop only formatting between
  elements.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): read XML in the exported app the way the state view does

Keep whitespace-only leaf values and xml:space="preserve" text in the
generated get view; drop only formatting between elements.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): release retained handles with their owners, read older captures

- Release retained-content handles whose close failed through a resource
  scope on every exit; a release that fails again reports the exact handle
  owners. A read that finished keeps its bytes when only its close failed.
- Accept captures written before state directories were recorded, and say
  in the state view's limitations that symlinked state directories may be
  misclassed for them.
- Compute each XML element's whitespace context once instead of scanning
  siblings for every text run.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): boot without the working folder, bound XML, accept mirrors

- Try the captured working directory only when an argument is relative,
  and start the app even when that folder is missing.
- Cut the generated XML view at REA's node and depth limits, and keep one
  unreadable file from failing data/get.
- Accept Ubuntu 24.04 mirrors, not only the canonical hosts; PPAs and
  other repositories are still refused.
- Read seeds through the retained-content owner's new signature.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): state view review round

- Stage a retained -journal beside its database so the SQLite worker
  accepts an inactive journal and refuses a hot one; a -wal or -journal
  that existed but was not retained (including one omitted at the
  baseline) makes the version unreadable.
- Keep tables that read when another fails, and let cancellation and
  unfinished cleanup propagate instead of becoming view errors.
- Read text as a key file only when every line parses as one.
- Rank a file named in a window title or selected by the caller as a
  document even when the application holds it open; match titles on
  file-name boundaries; leave the display name out of dconf matching.
- Bound JSON and key file views (4 MiB, 500 levels, exact numbers) and
  report too-large, missing and altered retained content separately.
- Add no version or change while a database stays absent.
- Read staged databases in place instead of copying them again.
- Give Evidence the same limitations as the result.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): export review round

- Load captured dconf keys without D-Bus: install dconf-cli, compile the
  existing and captured key files into the user database, and warn on
  failure instead of stopping the server.
- Start relative-argument launches in the captured working directory,
  the home included, since the framework launches from the app folder;
  respell the home itself and option values under it.
- Read views in a thread, skip files over 64 MiB, align the generated
  readers with the state view's, and close SQLite connections.
- Never own the home or a shared base folder; map a symlinked state
  directory's files back to their home spelling.
- Report omitted files and unretained opened folders.
- Name the module so it cannot shadow another, and keep the class off
  the module's own names; derive names the bundle check accepts.
- Write seeds as they are read, and remove an unpublished bundle on
  failure.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): capture review round

- Ask the launch process group and token carriers to exit before
  killing them, on errors too; report incomplete cleanup with its facts.
- Count descendants of the launch as started by the capture even when
  their environment cannot be read; treat a reused pid as a new process.
- Follow symlinked desktop entries; name a flatpak's state after its
  application id; match flatpak windows by name only.
- Stop on end of input; refuse empty, duplicate and reserved checkpoint
  names; require whitespace after the checkpoint keyword.
- Stay on each walk root's device, and never walk or retain `/` or a
  directory containing the home; say so in the limitations.
- Refuse a window manager that does not publish _NET_CLIENT_LIST.
- Remove retained content when a capture fails before it is written.
- Skip reading a file larger than the remaining content budget.
- Record apt's archive origin for each package source.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): read an unset client list as no windows when supported

xfwm4 leaves _NET_CLIENT_LIST unset while it manages no windows, so the
capture refused an ordinary desktop with nothing open. Read
_NET_SUPPORTED with the list: an unset list means no windows when the
window manager supports it, and the capture is refused only when it
does not. `xprop`'s "no such atom" output now reads as unset too.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): accept packages by apt's archive origin

Accept a package source when apt reports its origin as Ubuntu for a
noble suite, so mirrors count and PPAs or other repositories that merely
have "ubuntu" in their URL do not. Captures made before origins were
recorded fall back to Canonical's own archive hosts. Seed reads report
why retained content could not be used.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* feat(desktop): capture and export apps from vendor apt repositories

A closed-source app installed from its vendor's apt repository, such as
Typora, can now be captured and exported as a computer app.

- The capture records each non-Ubuntu repository listing the installed
  version (uri, suite, components from apt's sources files, one-line or
  deb822) and the key that signs it: the signed-by key, or the trusted
  key that gpgv shows verifies the repository's signed index. The key
  is retained with the capture.
- The capture records the packages owning the libraries the executable
  loads (DT_NEEDED with RUNPATH and the linker cache, through REA's ELF
  readers), since a vendor package may not depend on them.
- The export declares a verified vendor repository as an AptSource with
  its key, installs a vendor package's library packages, and adds
  --no-sandbox to an Electron app's launch, since a container denies
  Chromium's sandbox.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): report a locked database at once in data/get

A database the running app holds locked made each data/get wait sqlite's
five-second busy timeout before reporting it. Record the Typora run in an
agentenv computer in the verification.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* perf(desktop): detect key files without splitting the whole text

Read lines one at a time and stop at the first that a key file view
would drop, instead of splitting a file of up to 64 MiB into arrays
before its format is known.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): keep symlinked homes and state folders under the home in exports

- Respell a path under its state folder's logical spelling only when that
  spelling is under the captured home, so a symlinked HOME keeps its
  files; pair walked and logical state folders only when the capture
  lists the same number, and match the innermost folder first.
- When two files map to one home path, keep the one present before the
  capture and name the other in the limitations.
- Read key files in the generated data/get with the state view's rule,
  checked against it by running the helper under python3.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): end captures on zombies and keep cleanup owners

- A zombie or dead process no longer keeps the session open.
- Remove only the partial capture file this run created.
- Keep the exact owners of handles and processes whose cleanup failed on
  the returned error, so a retry can release them; test the published
  capture's cleanup-failure branch with mixed outcomes and retries.
- Release the application once even when the exit checkpoint fails.
- A cancellation before the capture is written removes what it retained.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): vendor package review round

- Match apt sources through `apt-get indextargets`, which covers flat `./`
  and `/` suites and gives each release file and sources entry.
- Accept a key whose signature verifies even when the release carries
  another signature without a key here; keep only the verifying key from
  a keyring holding several (OpenPGP packet splitter).
- Install the package from its repository's release (`name/<release>`),
  declare one source per URI and suite, support only the capture host's
  architecture, and refuse Ubuntu Pro repositories.
- List only library packages the computer can install; walk libraries only
  for vendor installs, with RPATH inherited as ld.so does, normalized
  paths, and DT_NEEDED bounded while reading.
- Put --no-sandbox after the program an env wrapper launches; keep the
  gpgv folder's owner when its removal fails; respect --max-file-bytes for
  keys; wait 0.25 s on a busy SQLite database in data/get.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(desktop): keep the vendor spec's gpgv command on one line

A code span broken across lines left `<binary keyring>` for VitePress to
read as an unclosed HTML tag, so the docs build failed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* feat(desktop): add a validated task to exported desktop applications

Each capture can now yield one agentenv task in its exported bundle. An
agent writes the task (a prompt plus declarative checks over the app's
data/get views); REA validates it and stays model-free.

- `describe-desktop-application-task` summarises a capture's net change
  per file (role, format, views before and after) and drafts checks.
- Checks: file presence, lines present/absent/counted, JSON-pointer values,
  SQLite rows and counts. REA keeps a task only if a check fails on the
  seed and every check passes on the captured end state; a file that was
  not retained at the baseline counts as absent, as the computer starts.
- `export-desktop-application --task` writes tasks/NAME.json (deploy,
  agent, skills, recording, prompt, verifier), a compiled verify.py that
  reads data/get, and an eval. A boundary test runs the verifier under
  python3 and checks it agrees with REA's evaluator on every check kind.
- Docs, spec and skill guidance, with the solve-rate recipe.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* test(desktop): keep the seed test in its own block

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(desktop): record the task verifier driven in an agentenv computer

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(desktop): record the Typora task run in an agentenv computer

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): keep task checks bounded, complete and attributable

- line_count counts lines holding a substring (contains) instead of
  matching a regular expression, which could stall the verifier on
  agent-written text and read differently in Python.
- value_equals of a whole object or array is undecided on a truncated
  view.
- The draft keeps a database whose rows changed under an unchanged hash,
  and a file whose truncated views match while it changed.
- A task file that cannot be read keeps the reader's detail, and a failed
  cleanup keeps the read failure as its primary.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): leave a file with one version out of the task draft

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(desktop): update the task verification's test counts

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(desktop): record the task solve rate with Claude Code and Sonnet 5.5

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* feat(desktop): capture and export apps installed from a downloaded .deb

capture-desktop-application --package-file reads the file's control fields
with dpkg-deb and records its SHA-256, size and dependencies when it holds the
installed package; the file itself is never kept. The library walk now also
runs for local installs.

export-desktop-application --package-url declares agentenv's
Download(url, sha256) for that file, its dependencies and library packages as
apt packages, and a run script that installs it with dpkg.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): seed app-named dotfiles and respell UTF-16 seeds

The capture now treats dotfiles in the home named after the application
(.varicad-cfg.ini, .notesrc) as state roots, so a file the app only reads
during the capture is still seeded. The exported seeding respells the
captured home in UTF-16 text files with a byte-order mark, keeping the mark.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): hold readiness until the app's own window shows

An exported app is ready only once a window of its class that the taskbar
lists is shown, so a splash screen of the class, which skips the taskbar, no
longer counts as the app being up.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(desktop): record VariCAD captured, exported and run in an agentenv computer

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): keep exported views strict JSON for infinite and inexact numbers

The exported data/get spells infinite and negative-zero SQLite reals as REA's
SQLite reader does ("Infinity", "-Infinity", "-0"), and refuses a JSON file
holding a number that reading would change (1e999, integers past 2^53, -0,
rounded fractions) with the state view's own error, instead of a view with
an infinity in it.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): keep package-file facts tied to one file and URLs unechoed

- A --package-url refusal never repeats the URL, and credentials are checked
  before the scheme.
- The package file's identity must hold between dpkg-deb's read and the hash.
- A failed close keeps its cleanup owner even when the capture is cancelled.
- Readiness also ignores windows typed as splash screens.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): record database logs and keep state changes distinct

- A selected path that is not a directory brings its -wal and -journal as
  content roots, so the capture records whether they exist and retains them.
- The state view treats a file as absent only under recorded content roots
  and refuses a database whose log presence is unknown.
- JSON changes list containers too, so an array that becomes an object with
  the same members is a change.
- Key file entries escape the key as a JSON pointer segment, so [a/b] c and
  [a] b/c stay apart.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): keep watching through unread samples and late workers

- An unreadable window list is unknown, not zero windows, and is named in
  the limitations.
- Before ending the session, live run-token carriers no parent link reached
  are tracked; an unreadable process table leaves the exit unknown.
- An env prefix's assignments find the program and name its XDG folders.
- flatpak run options that take a value are skipped with it, -- is honoured
  and a /arch/branch suffix is dropped from the id.
- State directories include the entry's full id.
- Path options refuse empty values; roots count toward the entry limit.
- A capture file whose close failed stays owned and is retried on discard.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): bound apt sources, keep older captures and cleanup owners

- Sources files parse into one stanza per line or deb822 paragraph with its
  URIs and suites kept as lists, so no URI x suite product is built.
- Repository architectures and release are optional, so captures written
  before they were recorded parse; the export treats them as unrestricted
  and unknown.
- Library sources are matched against every index of a declared
  repository's URI, suite and components.
- Two failures side by side keep both causes and both cleanup owners, and a
  host file whose close fails reports a lease on its handle.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): match escaped key file entries and suggest containers through members

- The draft's key file lookup uses the state view's escaped `group/key`, so a
  key holding `/` or `~` still gets its suggestion.
- JSON and XML changes now list containers too; the draft suggests an added
  or removed container through its listed members, and a container only
  when none of its members is listed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): read relative PATH entries from the launch folder

A program, or an empty or relative PATH entry, is resolved from the folder the
program starts in, as the launch does, so `env PATH=. app` with the entry's
Path still finds the app.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* perf(desktop): compare JSON views on one shared path

JSON and XML changes are found by walking both trees with one shared pointer
path, building a pointer only for a listed value, so memory follows depth
rather than every container's full path. A pointer over 4096 characters is
counted but not listed. A 256-level document with 4000-character keys went
from about 251 MiB of heap to under 1 MiB.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): keep JSON Lines as text in the exported reader

The exported reader parses the whole text before an inexact number makes it
JSON with an error, so JSON Lines with an integer above 2^53 stay text as in
REA. Over-long integer spellings are unsafe without converting them. The
guide no longer claims large integers keep their value.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): keep the state-path help free of dash-led words

The CLI alias check reads `-wal` and `-journal` in help text as short flags,
so the help names the write-ahead log and rollback journal instead.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): never record or export apt source credentials

- Source URIs lose any user name and password as they are parsed, the way apt
  splits them, and so do index-target URIs and descriptions and the sources
  apt-cache policy lists, so matching works whether or not apt prints them.
- A repository whose entry held credentials is marked
  `uri_credentials_removed`, and the export refuses it, or an older capture
  whose URI still holds them, with a reason that names no URL.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): look programs up without normalizing their paths

Program paths and PATH entries are joined rather than resolved, so `..` after
a symbolic link means what it means to the launch. The lookup moves to its own
module.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): note inexact JSON numbers with a flag, not a list

The exported reader only needs to know whether any number was inexact, so it
keeps a boolean instead of every rejected spelling.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): refuse older captures that still hold apt credentials

A capture written before credentials were removed counted the credentialed
repository's libraries as installable, so the export refuses it whole and asks
for a new capture rather than declaring packages no source offers. Recorded
policy sources are redacted in export messages too.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* test(desktop): keep the credential fixture on an example host

A login on a real host reads as a secret to the commit scanner, so the older
capture's fixture uses apt.example.com like the other credential tests.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): tidy looked-up paths and search relative entries only for env

Empty and `.` segments are dropped from a looked-up program path, which keeps
`/snap/bin/` entries recognisable as snaps, while `..` is still left for the
kernel. Relative and empty PATH entries are searched only in the PATH an `env`
prefix sets, which travels with the exported launch; the host's own PATH does
not.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): keep excluded and unwalked database logs unknown

The capture records the paths every walk left out, and the state view treats
a log as absent only under content roots the baseline walked completely and
outside those exclusions, so an excluded or unvisited log leaves the database
unread rather than read without it.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): judge JSON numbers after duplicate keys settle

The exported reader marks an inexact number instead of flagging it during the
parse, then looks for marks in the parsed value, so a number a later duplicate
key overwrote no longer refuses the file, as in REA.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): redact a repository's policy source when it names it

An older capture whose policy source matched no sources entry kept that text
whole, and the export named the repository by it; the export now names it
without any user name or password.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): never signal the launch group on unknown membership

When the current read cannot observe a carrier's process group, the carrier
is signalled on its own instead of letting an older process-table row grant
a group-wide SIGTERM.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): look programs up in the launch's order and refuse host-relative finds

The lookup searches PATH as the launch does, an empty entry meaning the
launch folder, so inspection and launch name the same program. A program found
only through a relative or empty entry of the host's own PATH is refused,
since the exported computer would not have it; an env prefix's PATH travels
with the command.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): count unchecked paths and keep logs behind them unknown

Each walk counts the paths it could not stat and the directories on another
device it did not enter, and the checkpoint records it as `unchecked_paths`.
The state view treats a log as absent only when the baseline left nothing
unchecked, so a log whose stat failed stays unknown.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* perf(desktop): scan parsed JSON with one iterator per level

The check for marked numbers walks the parsed value with an iterator per
nesting level instead of copying each container's children; a million-entry
array went from about 15.7 MiB to 8.1 MiB of peak traced memory.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): start a relative program from its captured folder

An exported launch whose program path is relative, such as `./app` from an
entry's Path, now changes to the captured working directory first, as it
already did for relative arguments.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>

* fix(electron): reject non-directory scenario roots before launch (#8967)

* fix(contracts): align managed example MethodDef scope (#8966)

* docs(debugger): document configurable session limits (#8963)

* docs(debugger): document configurable session limits

* docs(debugger): clarify GDB control command deadlines

* docs(debugger): qualify configurable GDB output budget

* docs(debugger): clarify one-shot CLI settings scope

* docs(debugger): preserve repository line endings

* docs(testing): explain npm launcher selection in PowerShell (#8962)

* docs(mcp): explain PowerShell launch environment scope (#8961)

* docs(mcp): explain PowerShell launch environment scope

* docs(mcp): qualify saved setup selections and OpenCode environment

* fix(native): declare temporary filesystem writes for inspect_macho (#8959)

* fix(environment): retain ID ordering with missing clocks (#8957)

* fix(environment): retain ID ordering with missing clocks

* fix(environment): compare known creation clocks across gaps

---------

Co-authored-by: hhang <[email protected]>

* fix(gpu): distinguish ELF string scan limits from corruption (#8955)

Co-authored-by: hhang <[email protected]>

* fix(setup): replace the fixed Hopper download deadline with a stall timeout (#7150)

* fix(setup): replace the fixed Hopper download deadline with a stall timeout

Hopper package downloads aborted after a fixed 120 s, which was also the
largest deadline the download owner accepted. The current 32 MB Linux
package needs about 132 s at the reporter's sustained 245 KB/s, so setup
could never finish on that connection, and the timeout surfaced only as
Node's generic abort message.

Abort a download when no response bytes arrive for 30 s, or when it
outlives a 15-minute overall bound, with a tagged
HopperDownloadTimeoutError that names the URL, the limit reached and the
bytes received. Linux and macOS setup already relay a thrown download
error's message; the Linux publisher now keeps it for release metadata
and public key fetches instead of a generic verification failure.
Metadata and key fetches keep their 10 s bound.

Refs #7077

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(setup): keep the macOS release feed on the metadata limit

The stall-timeout change left the macOS release feed fetch on the
package download bounds. That fetch could then wait up to 15 minutes
instead of the 120 s it had, while the installation guide says release
metadata keeps a 10 s limit. Only the Linux publisher owned that limit.

Move the 10 s metadata limit into HopperDownload.ts beside the package
bounds. The Linux feed and key fetches use it, and the macOS installer
now passes it for its feed fetch through the installer host.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>

* fix(cli): parse environment-serve --port and --host like rea mcp --http (#7092)

* fix(cli): parse environment-serve --port and --host like rea mcp --http

environment-serve declared --port as a number, so incur coerced the flag text with Number(): an empty --port= bound an ephemeral port instead of failing, and 0x1F90 or 1e3 were reinterpreted. --host accepted whitespace. docs/cli.md says these flags resolve like rea mcp --http, which accepts only decimal port text and a non-empty trimmed host. The flags now use the same rules and are refused before binding.

* refactor(cli): reuse the shared MCP listener port and host schemas

* fix(adb): separate unresolvable paths and host capacity in adb spawn failures (#6188)

* fix(adb): name unexecutable adb paths in readiness refusals

When REA_ADB_PATH or the PATH-resolved adb is a directory or a file the
host refuses to execute, the version probe fails at spawn with EACCES or
EPERM. That fell through to the generic "did not report a usable version"
refusal, which named neither the path nor the cause.

Classify those spawn errors as binary_not_executable (provider rejection
executable_missing), name the selected path and errno, and give
remediation for the environment, PATH and host-policy cases. The generic
fallback now names the selected path too.

Fixes #5689

* fix(process): admit synchronous spawn errors as captured failures

Node throws spawn errors other than EACCES, EAGAIN, EMFILE, ENFILE and
ENOENT synchronously from execFile. execFileBytes called execFile inside
its Promise executor, so ENOTDIR or EPERM became a rejection without
capture fields and execFileOutputFailure returned undefined. Consumers
that classify by failure code, such as the adb version probe, could not
see the code, which left the adb EPERM branch unreachable.

Catch synchronous spawn errno errors at the capture boundary and attach
the same empty capture an asynchronous spawn failure carries. Argument
validation errors stay unadmitted.

The adb version probe now reports an ENOTDIR selected path, such as a
REA_ADB_PATH whose parent is a regular file, as a missing binary with
the filesystem error code instead of the generic version refusal.

* fix(process): expose spawn failures from the capture owner

execFileBytes knows when a command never started, but
ExecFileOutputFailure did not say so, so consumers reread raw syscall
and code fields and disagreed. Mark both the synchronous throw and an
asynchronous `spawn <path>` error with `spawnFailed: true`, and set
`path` on the synchronous error to match the asynchronous shape.
Argument validation errors stay unadmitted.

Consumers now use the owner field:

- inspectAdbClient never reports a spawn errno as `exit_code`. ENOENT,
  ENOTDIR, ELOOP and ENAMETOOLONG are `binary_missing`; EACCES and EPERM
  stay `binary_not_executable`; any other spawn errno, such as EINVAL,
  is `binary_not_executable` with a path-naming "could not be started"
  message. Each records `filesystem_error` and `path_source`. EACCES
  remediation also names parent directory search permission, and EPERM
  remediation no longer names host policy as the only cause.
- DarwinProcessRunTokenReader tags every not-started compiler as
  `spawn_failed`, not only ENOENT. EACCES, EPERM and ENOTDIR were
  previously `failed`.
- NativeDmgArtifactReader uses one not-started rule for both
  `processNotStarted` (previously `spawn ` only, so synchronous errors
  were missed) and the ENOENT unavailable/io split (previously any
  `spawn` prefix). Its fake hosts now reject with real execFileOutput
  spawn failures.

* fix(process): classify spawn failures consistently across consumers

Admit synchronous and asynchronous spawn errnos through one helper that
requires an errno and a `spawn` or `spawn <path>` syscall. The
synchronous shape now also gains `spawnargs`, so both shapes match.

Use the owner's `spawnFailed` fact in the remaining consumers:

- inspectAdbClient reports exhausted host capacity (EAGAIN, EMFILE,
  ENFILE, ENOMEM) as a host failure to retry and no longer tells the
  caller to change REA_ADB_PATH. Path-resolution errnos come from a
  predicate the filesystem owner exports.
- runAdbCapture says adb could not be started instead of claiming an
  exit code for a process that never ran.
- The Darwin compiler probe words every spawn_failed outcome as a start
  failure, not a compilation failure.
- Asset catalog inspection treats any assetutil that never started as
  unavailable, not only ENOENT.

* fix(process): report host spawn capacity as a retryable constraint

Spawn errnos that report exhausted host capacity (EAGAIN, EMFILE, ENFILE,
ENOMEM) were classified by adb readiness alone. assetutil reported them as
an unavailable tool, which marked inspect_asset_catalog unavailable in the
session, and adb commands reported them as protocol failures.

Classify them once in the process capture owner and report a retryable
capacity constraint with recovery advice from assetutil and adb commands.

* docs(adb): describe host capacity failures from device operations

* fix(adb): report exhausted host spawn capacity as runtime unavailability

A host that cannot start adb for lack of process, memory or descriptor
capacity is not a configuration problem. Give it its own failure code and
map it to the runtime_unavailable provider rejection instead of
not_configured.

* fix(adb): report probe-time host capacity as a retryable constraint

A device operation whose adb version probe could not start for lack of host
capacity was refused as capability_unavailable without retryable. It now
reports the same retryable capacity constraint as a later adb command.

* fix(adb): keep the selected adb path in probe-time capacity errors

The retryable capacity constraint raised at the version probe dropped the
selected_binary and path_source diagnostics the refusal used to carry.

* refactor(adb): keep the spawn-failure fact to adb consumers

Restore the DMG reader, Darwin compiler probe and assetutil to main. The
capture owner still marks a never-started command, including the spawn
errors Node throws synchronously, and only the adb readiness probe and
adb commands read that fact.

* test(adb): assert spawn-failure facts instead of refusal wording

The new adb tests compared complete refusal sentences and banned an old
phrase. They now check the errno, selected path, path source and the
owner's spawn-failure marker, so wording edits do not break them.

* fix(adb): own one host-capacity remediation and cover the spawn route

The host-capacity constraint and the configuration failure each carried their own remediation sentence. AdbConfiguration now owns the text and both use it, so the constraint's remediation.action equals diagnostics.remediation.

The synchronous spawn-throw test now runs on Node's execFile route and on the owned spawn route adb uses (stopSignal). The provider test covers inspect_adb_client under EMFILE next to list_adb_devices, and the docs say which operations report the retryable capacity constraint and which report runtime_unavailable.

* fix(process): report EMFILE and ENFILE spawn failures without stdio

After an EMFILE or ENFILE spawn failure Node creates no stdio, so the
owned stop-signal route threw a TypeError while reading the streams and
the later spawn error went unhandled. Attach the error listener first and
capture absent streams as empty, so adb reports retryable host capacity.

* fix(adb): keep a completed push when host capacity stops its digest check

A capacity failure starting sha256sum came back as a retryable error even
though adb push had already written the file. Repeating the request with
overwrite=false is then refused because the destination exists.

Return the receipt with an unavailable device digest and name the capacity
failure in its limitations, so the caller is told the transfer is unverified
rather than advised to retry.

* docs(adb): list host capacity as an unverified push case

---------

Co-authored-by: t <t@t>

* test(gdb): expect configurable stream truncation diagnostics (#8965)

* test(gdb): expect configurable stream truncation diagnostics

* build(gdb): include prerequisite duplicate-import repair from #8884

* fix(javascript): resolve module-level Electron options in functions (#8960)

* fix(javascript): resolve module-level Electron options in functions

* fix(javascript): limit module option ancestry walk

* fix(javascript): preserve uncertain captured argument ordering

* fix(javascript): retain hoisted activation uncertainty

* fix(javascript): retain function ownership through loops

* fix(javascript): account for classic global activations

* fix(javascript): bind first activations to lexical identities

* fix(javascript): retain uncertain global activation timing

* fix(javascript): isolate receiver facts and share computed reads

* fix(javascript): retain projected and opaque global activations

* fix(javascript): refresh activation bounds as opaque effects grow

* fix(ci): format website watch directories without changing configuration

* fix(javascript): share source reads across opaque-effect updates

* fix(javascript): retain constructed-code global activation bounds

* fix(javascript): share runtime activation and receiver facts

* fix(javascript): reuse pattern reads and function positions

* fix(javascript): isolate copied paths and preserve runtime reads

* style(javascript): format migrated reference imports

* fix(javascript): normalize constructor activation and receiver effects

* fix(javascript): retain aliased receiver timing and lexical global this

* fix(javascript): preserve effect-free receiver identity across activations

* fix(javascript): retain outer activation and prototype effects

* fix(javascript): index implicit reads without deep path rescans

* fix(javascript): retain aliased prototype and parameter read uncertainty

* fix(javascript): index reflected reads through intrinsic invocation facts

* fix(javascript): retain unresolved global activation reads

* chore: refresh PR branch after review fix publication

* fix(javascript): retain projected receiver reads and cache method facts

* fix(javascript): retain inherited and implicit activation reads

* fix(javascript): retain caller-owned this uncertainty

* fix(javascript): retain reflected reads with unresolved spread arguments

* fix(javascript): preserve enclosing activation for decorators

* fix(javascript): track intrinsic literal prototype effects

* fix(javascript): retain catch activation from implicit reads

* perf(javascript): reuse effective keys for projected reads

* fix(javascript): retain possible caller-owned ambient effects

* fix(uninstall): preserve caller-selected Kuna caches during purge (#7921)

* fix(uninstall): keep a managed cache namespace whole when it holds a caller-selected Kuna cache

purge --purge-data reported a REA_KUNA_CACHE_DIR selection below the default Kuna cache as retained, then removed it together with its parent namespace. A namespace that contains the selection is now retained and reported, so the retained report is true.

Fixes #7344

* fix(uninstall): also keep a namespace equal to, or reached by symlink traversal from, the selected Kuna cache

Compare the namespace and the selection with the same normalization, treat an equal path as contained, and resolve existing paths before planning so a symbolic link followed by .. is judged by where it leads.

* fix(uninstall): judge a selection with parent traversal where the filesystem resolves it

Collapsing .. lexically contradicts the existing rule that parent traversal after a symbolic link leads elsewhere, and broke the retained-through-symlink-traversal test. Compare spellings without .. directly and resolve existing paths for the rest, so the symlink-parent case still purges the default cache while a selection that resolves into a managed namespace is retained.

* fix(uninstall): keep a namespace whole when the selection's location is unknown

A realpath failure other than ENOENT/ENOTDIR (for example EACCES under an
inaccessible parent) no longer counts as "absent". Each namespace that may
contain the caller-selected Kuna cache is retained instead of purged.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>

* fix(changed-behavior): withhold equality for unknowns and contradictions (#7624)

find_changed_behavior derived behavior_status from each process
comparison's aggregate status alone. An unchanged comparison whose
first_divergence was unknown (residual unknowns prevent proving no
divergence) and a consistent contradiction comparison both fell
through to observed_unchanged, with no unresolved finding and no
changed-behavior registry unknown.

An unchanged comparison with unknown divergence now counts as unknown
and adds one comparison-level unresolved first_divergence finding
carrying its reason, as artifact comparisons already do for coverage.
A contradiction status makes the aggregate unknown, and contradiction
dimensions are classified and counted as contradictions instead of
unresolved branches. Complete unchanged comparisons stay
observed_unchanged and changed comparisons with unknown localization
stay observed_changed.

Fixes #7384
Fixes #7383

Co-authored-by: Claude Opus 5.5 <[email protected]>

* fix(process): reject contradictory filesystem capture facts (#7631)

* fix(process): reject captures whose top-level filesystem facts contradict their checkpoints

A process capture records its filesystem state twice: files_before,
files_after and filesystem_effects at the top level, and the initial
and final filesystem_checkpoints. Validation never reconciled the two,
and comparison reads only the checkpoints. A caller-authored capture
recording a created file at the top level while its final checkpoint
was empty was therefore accepted and compared as unchanged.

Semantic validation now requires the initial checkpoint to match
files_before with no effects, and the final checkpoint to match
files_after and filesystem_effects. The native producer builds both
representations from the same snapshots, so produced captures are
unaffected; consistent created-file captures still compare as changed.

Fixes #7371

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* test(process): cover file-list checkpoint mismatches independently of effects

Every rejected case in the checkpoint-consistency test also disagreed on
effects, so removing both file-list comparisons left it passing. Add
initial and final checkpoint cases whose file lists alone disagree while
effects and coverage accounting stay valid.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>

* fix(process): report deadlines expiring during final cleanup (#7635)

* fix(process): report a deadline that elapses during final cleanup as a timeout

runOwnedCommand checked its absolute command deadline before awaiting
process cleanup, but on the ordinary successful path it consulted only
the caller's cancellation signal afterwards. A command whose deadline
expired while successful cleanup was still awaited therefore resolved
as success.

After cleanup, an elapsed deadline now throws OwnedCommandFailure with
reason "timeout" and the completed snapshot, as docs/tool-design.md
requires a final signal check after the last awaited finalization step.
Existing primary failures, caller cancellation and incomplete-cleanup
classification keep their precedence.

Fixes #7369

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* test(process): drive the cleanup-deadline regression with controlled time

The regression test gave output collection 100 ms of real time before
the deadline. On a busy worker the deadline could expire during
collection instead, taking a different failure path and failing the
test even with the fix. Use fake timers, wait until cleanup has
started, then advance past the deadline and release cleanup, so the
ordering no longer depends on worker speed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>

* fix(enhanced): bound batch decompile input size (#5937)

* fix(artifacts): handle early ASAR extraction cancellation (#5672)

* fix(artifacts): own source errors during output setup

* fix(artifacts): join silent source destruction before returning

* fix(artifacts): retain source cleanup failures with primary errors

* fix(artifacts): join source finalization and classify secondary failures

* fix(firmware): separate source cleanup from output rollback

* fix(artifacts): retain live source cleanup dependencies

* fix(artifacts): preserve source failure completion and semantics

* fix(artifacts): retain scope cleanup owners and typed failures

* fix(artifacts): keep cleanup classification and admission recovery

When the operation completed and only scope cleanup failed, keep the cleanup failure's own reason instead of reporting io, matching main (fixes artifactProvider.cleanupRetry). When a typed primary is rebuilt with pending scope cleanup, carry its userMessage and ASAR header admission so callers keep the configured ceiling and setting.

* fix(artifacts): translate plain file-close failures through the reader path

The outer file-cleanup branch rebuilt every ArtifactFileCleanupFailure as a
ProviderAdapterError, so dyld reads lost their io, unavailable, format and
cancelled reasons and their exact file-cleanup cause. Compose separately only
when the failed primary owns its own translation (a domain error, source
failure or scope cleanup failure); raw and reader primaries are already
projected by the file failure with its exact lease.

* fix(artifacts): bound source destruction by cancellation and keep wrapper admission

After cancellation, SafeOutputTree.write could wait indefinitely for a source
whose destruction did not settle, both in the destroy join and in the
iterator's wait for close. Both waits now end at cancellation plus a fixed 1 s
grace, modeled on the process-output closure grace. A destruction that
outlives it is reported as incomplete source cleanup under an exact lease that
releases only once closure is observed without a late error.

ArtifactResourceCleanupFailure and SafeOutputSourceFailure now keep the
primary's admission, matching withCleanup.

* fix(artifacts): keep one settled wait while observing a stuck source

observeClosure raced the same pending close promise against a new timer every
10 ms, so a source whose destruction never settled accumulated one reaction per
poll for as long as it stayed open. Each poll now waits on its own promise that
the close listener or the unref'd timer settles, leaving one pending wait and
one close listener. The lifecycle test also asserts that a late close releases
the retained owner without an explicit retry.

* fix(artifacts): settle pending reads before cancellation

* test(artifacts): observe the consumed ZIP iterator on cancellation

* test(artifacts): validate projected cleanup resources before sorting

* test(mcp): reserve parent capacity for timeout recovery

* fix(artifacts): contain ASAR cancellation before output consumption

* test(artifacts): hash ASAR cancellation fixture bytes

* fix(decompile): retain inventory limitations and selection (#8466)

* fix(decompile): retain inventory limitations with the latest selection

The manifest and decompile index dropped procedure-inventory
limitations, so a directory separated from its Evidence response could
not explain why functions were selected or omitted. Keep those
limitations on the manifest and in a latest_run index record that also
names the filter and counts that produced them. Older runs remain
represented only by their retained function records.

Fixes #8355

* test(decompile): surface export errors and check function caveats

Address review findings on the inventory-limitation regression:

- Throw the typed export error instead of returning from a failed Result.
- Assert historical function records keep their own pseudocode caveats
  and do not receive the latest inventory warnings.

* fix(pyinstaller): leave PE carrier binary members unmarked (#6730)

The Windows bootloader sets no execute bit on extracted ARCHIVE_ITEM_BINARY
members (the S_IXUSR fchmod in pyi_archive.c is #ifndef WIN32). Mark type b
members executable only when the carrier dialect is posix.

* fix(jeb): reconnect after stale MCP sessions (#3754)

* fix(jeb): reconnect after stale MCP sessions

* fix(jeb): preserve failures for overlapping MCP requests

* fix(jeb): make cleanup operations explicit and stabilize timeout fixtures

* fix(setup): flag unresolved migrated Claude Desktop storage (#8971)

* fix(setup): flag unresolved migrated Claude Desktop storage

* fix(setup): preserve Claude MSIX process view when config is unresolved

* fix(enhanced): restore batch input record limit

* docs(tool-design): audit agent workflow guidance (#8439)

* docs(tool-design): audit agent workflow guidance

* docs(tool-design): check deployed guidance versions

* fix(cli): align ADB directory and package query schemas (#8974)

* fix(cli): align ADB directory and package query schemas

* fix(cli): preserve ADB selector correction details

* fix(devicetree): reject duplicate node paths in Evidence (#8970)

* fix(pe): classify valid DOS targets as unsupported (#7530)

fix(pe): classify confirmed DOS targets as unsupported

Signed-off-by: Rudy Celekli <[email protected]>

* fix(native): reject nonzero reserved ELF dynamic symbols (#7528)

fix(native): validate the reserved ELF dynamic symbol

Signed-off-by: Rudy Celekli <[email protected]>

* fix(evidence): traverse large source link sets safely (#8457)

* fix(verification): use inspected Ghidra JDK (#8440)

* fix(mcp): clarify retained dossier handoff in crash prompt (#8437)

* fix(evidence): clarify unsupported view recovery (#8412)

* fix(evidence): clarify unsupported view recovery

* fix(evidence): explain sensitive capture view refusal

* test(ghidra): check regex recovery at its owning boundary (#8578)

* test(ghidra): check structured snapshot profile mismatch (#8536)

* test(ghidra): check retained historical snapshot evidence (#8381)

* test(ghidra): check retained historical snapshot evidence

* test(ghidra): select historical observations by address

* test(cli): cover Frida argument admission and spawn ordering (#3356)

* fix(cli): reject spawn arguments in Frida attach mode

* test(cli): remove redundant Frida recorder assignments

---------

Co-authored-by: Kaoru0822 <[email protected]>

* fix(ghidra): report restricted-host temp write denial on Windows (#8889)

* fix(ghidra): report restricted-host temp write denial on Windows

Add a Windows doctor check that creates and deletes a file under os.tmpdir(), and classify a denied java_home.save in Ghidra startup output with its path, Win32 error 5, and TMP/TEMP remediation. The probe covers REA's own process only; a host that restricts only child processes is reported from the child's output at startup.

Fixes #7075

* fix(ghidra): probe the session root and retain failed probe cleanup

The Windows doctor temp_directory probe now writes under ghidraSessionRoot(), the same root Ghidra session runtimes use, instead of os.tmpdir(), which can differ when the inherited temp path has a dot-prefixed element. A probe file whose removal fails is retained on the doctor resource owner so doctor shutdown retries the removal; the result still reports its path.

* fix(cli): normalize exit status on worker cleanup failure (#8049) (#8050)

* fix(cli): normalize exit status on worker cleanup failure (#8049)

* test(cli): verify SDK output status and finalization rejection on exit

* ci: retrigger checks

---------

Co-authored-by: Sarthak Pandey <[email protected]>

* fix(gdb): keep inferior-tty verification for read-only queries (#5539)

* fix(gdb): keep TTY verification across read-only inferior-tty inspection

The console command path cleared `inferiorTtyVerified` before dispatching
any command whose text merely mentioned `inferior-tty`, so the read-only
`show inferior-tty` query permanently revoked a persistent session's
verified output sink; no in-session path ever restored the flag, and a
later close of a live session failed detach verification with
capability_unavailable.

Exempt the exact read-only query forms (`show inferior-tty` and the MI
`-inferior-tty-show`) from the output-mutation predicate; every other
command mentioning the TTY setting keeps revoking, so mutating commands
still fail closed and detach keeps re-reading the live TTY before
detaching. Cover the query forms, composite commands, and the
inspect-then-close detach path with regression tests.

Fixes #5099

* test(gdb): classify show inferior-tty in the one-shot table and rename the TTY classification table

- Add a 'show inferior-tty' row to the 'classifies one-shot GDB
  commands' table: the read-only query no longer invalidates the detach
  proof, a user-visible one-shot behavior change.
- Rename the 'classifies read-only inferior TTY queries as non-mutating'
  table to 'classifies inferior TTY commands' since two of its rows are
  mutation cases.
- Assert via the existing recorded-commands array that the inspected
  'show inferior-tty' text is actually dispatched, and add an
  execute-path assertion that the MI '-inferior-tty-show' form leaves
  the verified-sink flag intact (defensive exemption: REA's own MI calls
  do not route through this gate).

---------

Co-authored-by: akushonkamen <[email protected]>

* fix(browser): report an out-of-range generated position as a selection fault (#7133)

trace_web_source_location reported a well-formed generated_position past
the end of the selected script as invalid_format on generated_position,
with no bounds, so callers were told their input was malformed and not
which coordinate to change. Report out_of_range on the line or column
that is past the end, with its valid bounds, as export_web_scripts and
inspect_web_network_capture now do for their selections.

The position scan now returns the bound it ran past, so the offset and
the reported limit come from one pass and cannot disagree on line
terminators.

Fixes #7132

* fix(captures): classify unresolvable capture paths as capture_path input (#7814)

* fix(captures): classify unresolvable capture paths as capture_path input

Fixes #7636

* test(captures): cover unresolvable descriptor paths

---------

Co-authored-by: Abelo9996 <[email protected]>
Co-authored-by: morluto <[email protected]>

* fix(apple): preserve exact integer precision with post-table plist objects (#8910)

* fix(javascript): resolve electron findings through aliases of interop results (#8914)

* fix(javascript): resolve electron findings through aliases of interop results

* fix(javascript): resolve interop aliases iteratively and check target mutations

* fix(javascript): retain possible writes through uncertain aliases and guard positive reads

* perf(javascript): cache alias change status across semantic interop lookups

* fix(javascript): transient Windows private-runtime source-transfer (#8769)

* fix(javascript): transient Windows private-runtime source-transfer

Parent source-transfer into the Windows private runtime root failed with
EPERM on path-based exclusive create. Write through a native transient
private-runtime path that closes the handle after flush so per-job rm
still works, and yield around the sync write so abort deadlines can fire.

* fix(javascript): yield timer phase before Windows source-transfer write

* fix(il2cpp): reject module image references outside retained inventory (#8956)

* fix(ida): admit Docker host-gateway names as local MCP hosts (#8932)

Containerized REA reaches an IDA MCP server on the container host through
host.docker.internal (Docker, Podman compatibility) or
host.containers.internal (Podman). The HTTP registration refinement only
admitted localhost, 127.0.0.1 and [::1], so doctor reported the valid
gateway registration as invalid_configuration.

Admit both gateway names in the registration's local-host set. The
refinement still requires http or https, no URL credentials, and refuses
every other host.

Fixes #8925

* fix(mcp): report target after failed open (#8613)

* fix(mcp): report target after failed open

* test(session): cover failed open receipt ownership

* test(session): verify restored target receipt

* test(mcp): retain target on snapshot mismatch

* test(mcp): retain oversized open failure receipts

* test(cli): avoid continuing session receipt in one-shot errors

* fix(mcp): report provider after failed switch

* test(ghidra): verify failed open preserves annotations

* test(mcp): join queued receipt request before cleanup

* test(session): assert open failure receipts in lifecycle suites

* test(session): keep open-failure receipt checks within function limits

Move the provider-collision case and snapshot fixture setup out of the
functions that grew when both failure receipts were asserted.

---------

Co-authored-by: Cursor Agent <[email protected]>

* fix(prompts): include existing static artifact investigation routes (#8972)

* fix(prompts): include existing static artifact investigation routes

* fix(prompts): preserve workflows in existing family scopes

* fix(prompts): require readers for Apple resource routes

* fix(rizin): keep debug targets off the debugger command stream (#8647)

* fix(rizin): keep debug targets off the debugger command stream

A Rizin debug target inherited Rizin's stdin, which is REA's command
stream. Once resumed, a target that reads stdin consumed REA's commands,
so close returned cleanup_incomplete and the one-shot CLI hung.

Route the target's stdin to the caller-selected TTY alongside stdout and
stderr. Before detaching, refuse a target whose stdin is still the command
stream, so a Rizin or backend that ignores the key gets a specific,
retained failure instead of a resumed target and a timeout.

Fixes #8642

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* test(rizin): keep the stdin test launcher's descriptors under dash

The launcher gated its target with `cat "$2" >/dev/null`. dash applies a
simple command's redirection to the shell itself until the command returns,
so on Ubuntu the waiting shell's fd 1 was /dev/null, and that shell is the
target REA reads in /proc. All 8 Linux-gated tests then failed the existing
output-descriptor check. macOS /bin/sh is bash, which redirects only in the
forked child, so this did not show locally.

Gate without any redirection. Production code is unchanged.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* test(rizin): reject unmodeled commands and own cleanup at acquisition

Address review of the stdin regression tests.

The launcher accepted any unknown line, so the marker that followed could
complete it, and assertions only checked that commands were present. An
added `dc` or a repeated `olj` still passed. It now records an unknown line
and exits, and the tests pin the exact command sequence.

Cleanup hung off module state assigned after setup finished, so a failed
setup left its directory and terminal child behind. Each resource now
registers its own finish hook when acquired, using createTestTempDirectory.

Success checks throw the typed error instead of asserting `.ok`, and the
refusal probe no longer sits inside a loop and conditional.

Production code is unchanged.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>

* fix(ci): align tests with current evidence contracts

* feat(native): report binary hardening for ELF, PE and Mach-O (#8524)

* feat(native): report binary hardening for ELF, PE and Mach-O

Documents inspect_binary_hardening and its CLI equivalent, including
per-property states, supporting evidence, read budgets, and one report
per Mach-O slice. Explains that missing protection imports remain
unknown, signature presence does not establish validity, and hardened
runtime requires inspect_signature. Adds hardening links throughout the
documentation. The supplied diff also removes the advanced CodeQL
workflow and configuration, along with snapshot-binding mismatch
guidance.

* fix(native): validate declared payloads and dynamic tables before hardening rows

Run the shared range checks for ELF, read imports from the dynamic tables
rather than section metadata, and keep an unresolved symbol count apart from
an absent table. Check every Mach-O command before building rows and retain
only the offsets the rows need.

Make the verifier's readelf output wide, check the emitted format before
parsing Mach-O, and place the sources before the libraries in the link.

* fix(native): share Mach-O segment admission with hardening reads

A segment with a section count its cmdsize cannot hold passed the hardening
reader and could enable restrict_segment. Move the size, section count and
file range checks to a shared admission that readMachoImage and the hardening
reader both use, and pin the ELF load-size case with a regression test.

* fix(hardening): reject Mach-O section payloads past the slice and empty ELF interpreters

Segment admission never checked each section's payload range, so a
file-backed section past the slice still produced rows. Decode sections with
the section header owner and mark the slice malformed when a payload leaves
it; zero-fill and empty sections own no file bytes and are exempt.

admitInterp accepted a NUL first byte in a longer extent, which counted an
empty interpreter as PIE evidence. It now requires a nonempty path.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01KuWHGvzJkrhCqjtEHUwqyH

* fix(native): read CFG instrumentation from decoded GuardFlags

Use the IMAGE_GUARD_CF_INSTRUMENTED flag that guardFlagRecord already
decodes instead of masking the raw GuardFlags word a second time.

* fix(native): reject ELF table spans a second PT_LOAD remaps

The hardening reader mapped DT_STRTAB, DT_SYMTAB and hash tables through
the one PT_LOAD containing the table's first address. A second PT_LOAD
starting inside the table could map the rest of it from other file bytes
and the reader still reported parsed import evidence.

Cap each mapping at the next PT_LOAD that starts inside it, reject a
requested range that crosses that boundary, and stop hash reads at the
same limit, matching the conflicting-mapping refusal in readElfSymbols.

* fix(native): dedupe ELF note tables only on shared boundaries

The hardening reader dropped any note table whose bytes another table
already covered. A covering SHT_NOTE can hold a non-GNU note whose
descriptor is itself a complete GNU property note, while PT_NOTE names
that inner note directly. Walking only the outer stream missed the GNU
marker and reported CET disabled.

Drop an alias only when it starts and ends on boundaries of admitted
tables, which are note boundaries of the walked streams. Any other
covered alias leaves the feature rows unknown, as a partial overlap
already does.

* fix(native): admit every Mach-O symbol record after a canary match

The Mach-O stack protector scan returned as soon as it found an
undefined ___stack_chk_fail, so a later undefined symbol whose n_strx
lay outside the string table was never checked. The same damaged table
parsed or failed as malformed depending only on record order.

Keep the match and finish the bounded record walk. Records after the
match are only checked against the string table bounds, so their names
are not read or charged to the scan budget. The ELF reader already
admits its whole symbol table before matching names.

* fix(native): accept ELF load aliases with the same file translation

The hardening reader treated every PT_LOAD that starts inside a table,
or that also contains the table's address, as a conflicting mapping.
A load with the same address-to-file translation names the same file
bytes, so tables it overlaps still have one interpretation, and the
canonical ElfLoadMap already merges such spans.

Treat equivalent loads as one interpretation. A load with a different
translation, or the zero-filled tail of an equivalent load, still ends
the usable range, so a remapped table is still rejected.

* fix(native): keep same-span ELF note tables with different alignments unknown

A section and a program header that share endpoints were treated as one
note stream even when their effective note alignments differed. Walking the
same bytes at 4 and 8 can frame different notes, so dropping the second table
could hide a GNU property word and report CET or BTI as disabled.

Require matching effective alignment at both endpoints, and no alignment
change inside the covered span, before treating a table as an alias.
Otherwise mark the feature rows unknown. The fixture's .note.gnu.property
section now declares the class's note alignment, as toolchains emit it.

* refactor(native): read LC_SYMTAB through the shared fixed-word decoder

Move the Mach-O fixed uint32 command layouts out of MachoCommandNames
into domain/apple/machoLoadCommands so there is one owner of those
offsets. MachoCommandNames and the hardening reader now both decode
through it; the hardening reader keeps its own bounds and import policy.

* fix(native): skip Mach-O common definitions in canary imports

A common definition sets N_UNDF|N_EXT like an import but stores its
allocation size in n_value. An object holding only
`int __stack_chk_fail;` therefore reported stack_protector: enabled.

Require a zero n_value before an undefined external counts as an
import, for both nlist widths.

* refactor(native): read linkedit data commands through the shared fixed-word decoder

readMachoLinkeditDataCommand reproduced the dataoff/datasize offsets that
FIXED_WORD_FIELDS already owns. Take the command and decode through
decodeMachoFixedWordFields, as readMachoSymtabCommand does, keeping the
truncation and endianness behavior.

* fix(native): skip empty SHT_NOTE sections in ELF hardening

A zero-sized SHT_NOTE owns no bytes, so the section directory marks it
not file-backed and the hardening reader rejected the whole image as
malformed, while readElfImage accepted the same bytes. Skip empty note
sections before the file-backing check; they have nothing to walk.

* fix(scripts): run hardening verifier commands through the shared owner

The binary hardening verifier started its probes and compiler drivers with
raw spawnSync, which bypassed the src/process/ supervision that AGENTS.md
requires. Every launch now goes through createVerifierCommands, and the
temporary root is removed only after the owner closes cleanly.

* fix(scripts): clean up hardening verifier commands on Ctrl-C

SIGINT or SIGTERM ended Node before withVerifierCleanup ran, so detached
compiler trees and the temporary root could outlive the verifier. The
verifier now turns those signals into an abort that every owned command
receives, so cleanup closes the owner and removes the root before exit.

* fix(scripts): fail the hardening verifier when a signal lands outside a command

Only the owned commands read the interruption signal, so a SIGINT or
SIGTERM during the final cleanup or between commands was recorded and
then dropped, and the verifier exited 0. After cleanup settles, the
verifier now throws the interruption reason. A failure the operation or
its cleanup already raised still leaves first and is not replaced.

* fix(native): fail Mach-O slices with a malformed encryption command

The hardening reader turned every malformed status from the shared
encryption reader into an unknown `encrypted` row, so a short
LC_ENCRYPTION_INFO_64 or an encrypted range past the slice still left the
slice parsed with a full report.

Only two conflicting encryption commands stay an unknown row now. Any other
encryption defect fails the slice like other structural command failures.

* fix(native): ignore STB_LOCAL undefined ELF symbols as imports

A local undefined __stack_chk_fail or __memcpy_chk is never resolved
against another object, so it does not establish an external import.
Skip STB_LOCAL bindings when reading protector signs, and give the
fixture's positive symbols STB_GLOBAL binding.

---------

Co-authored-by: Matt Van Horn <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>

---------

Signed-off-by: Rudy Celekli <[email protected]>
Co-authored-by: Kaoru0822 <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: Edgar Arakelyan <[email protected]>
Co-authored-by: ShuaixinHuang <[email protected]>
Co-authored-by: highmachs <[email protected]>
Co-authored-by: hhang <[email protected]>
Co-authored-by: hhang <[email protected]>
Co-authored-by: Paritosh Lahre <[email protected]>
Co-authored-by: Adolanium <[email protected]>
Co-authored-by: 김준호 <[email protected]>
Co-authored-by: t <t@t>
Co-authored-by: Hasan Zayni <[email protected]>
Co-authored-by: Salman Almutairi <[email protected]>
Co-authored-by: Lucien <[email protected]>
Co-authored-by: RRFZ <[email protected]>
Co-authored-by: 2SinceJuly <[email protected]>
Co-authored-by: Wade <[email protected]>
Co-authored-by: Martin Klein <[email protected]>
Co-authored-by: morluto <[email protected]>
Co-authored-by: Rudy Mizrahi Celekli <[email protected]>
Co-authored-by: xin zhuang <[email protected]>
Co-authored-by: Beqa <[email protected]>
Co-authored-by: NoThing <[email protected]>
Co-authored-by: Sarthak Pandey <[email protected]>
Co-authored-by: akushonkamen <[email protected]>
Co-authored-by: akushonkamen <[email protected]>
Co-authored-by: amyanger <[email protected]>
Co-authored-by: Abel Yagubyan <[email protected]>
Co-authored-by: Abelo9996 <[email protected]>
Co-authored-by: Jiabin Yin <[email protected]>
Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Mohamed Ayoub Eleuch <[email protected]>
Co-authored-by: Kaoru0822 <[email protected]>
Co-authored-by: Matt Van Horn <[email protected]>
Co-authored-by: Matt Van Horn <[email protected]>
Co-authored-by: gensokyo-dev <[email protected]>
2026-11-06 02:06:39 +08:00
Saim ShafiqueandSaim Shafique e5f638310c fix(cli): reject project-scoped Amp registration (#7979)
* fix(cli): reject project-scoped Amp registration

Incur's mcp add parses --no-global but its Amp writer always writes the
global settings file, so a project-scoped Amp request exited 0 reporting
success while changing global config. Fail closed in the entrypoint before
Incur dispatches: project-scoped Amp registration now exits 1 with a
diagnostic and leaves global settings untouched. Global Amp registration
is unchanged.

Fixes #7535

* fix(cli): exempt discovery requests from the Amp project-scope guard

Discovery requests are not registrations: mcp add --no-global with a
discovery flag (--help, -h, --llms, --llms-full, --version, --schema) now
passes through to normal handling instead of exiting 1 with the scope
error, matching the discovery set mcp add itself exempts. Real
project-scoped Amp registrations are still rejected before Incur runs.
Also restores the executable bit on scripts/rea.mjs, and adds launcher
dispatch tests proving the guard rejects a real registration before the
CLI runtime loads and passes discovery requests through.

* fix(cli): share the mcp add discovery classification between guard and routing

The Amp project-scope guard kept its own copy of the mcp add discovery
flags, which could drift from the discovery routing in cli.ts.
classifyMcpAddDiscovery in cliBuiltinArguments.ts is now the single
owner: the guard and the cli.ts mcp add special-casing both consume it,
with bypass/version/schema precedence matching the routing. No behavior
change; covered by classification unit tests and the existing mcp add
discovery serve test.

* fix(cli): emit structured output for Amp project-scope refusal

The Amp project-scope guard wrote a plain message to stderr and left
stdout empty when the caller asked for a structured document (json,
jsonl, yaml), contradicting the failure framing documented for the
output argument. Structured formats now render the refusal through the
same pre-execution error renderer the neighboring output-argument
branch uses, with code AMP_PROJECT_SCOPE_UNSUPPORTED on stdout; every
other format keeps the byte-identical plain stderr message. The guard
still exits 1 before dist/cli-runtime/cli.js is imported.

Co-authored-by: Saim Shafique <[email protected]>

* test(cli): cover Amp project-scope guard through the launcher

The guard tests only called isProjectScopedAmpRegistration, so they
still passed with its call in scripts/rea.mjs removed. Add a launcher
test with an isolated home and working directory that checks the exit
code, the diagnostic, the unchanged global settings, and the absence
of project files for both explicit Amp and default-agent requests.

Co-authored-by: Saim Shafique <[email protected]>

* test(cli): assert Amp project-scope refusal by error code

The --no-global refusal tests locked the full English sentence. Structured output now asserts AMP_PROJECT_SCOPE_UNSUPPORTED and a message field.

* fix(cli): share normalized mcp add agent selection with the scope guard

The project-scope guard scanned raw arguments and only recognized the spaced --agent form, while dispatch expanded --agent= equals forms first: --agent=claude-code was wrongly refused as default Amp, and --agent claude-code --agent=amp slipped Amp past --no-global. The equals expansion and agent scan now live once in cliBuiltinArguments.ts and both callers consume them, so guard and dispatch read the same selection.
2026-11-06 02:01:30 +08:00
Edgar ArakelyanandClaude Opus 5.5 0ac6be1a3d ci(environment): pin the real agentenv lane to agentenv-framework 0.9.1431 (#8865)
* ci(environment): pin the real agentenv lane to agentenv-framework 0.9.1431

The real-agentenv lane, its identity check and the docs pinned
agentenv-framework 0.9.1334, 97 releases behind the current 0.9.1431.
Recorded verification notes keep the versions they ran with; the website
catalogs change only the placeholder comment that shows the version.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(environment): describe 0.9.1431's pulled infra images; keep the site on the release

0.9.1431 pulls its gateway and service-db images from ghcr.io by the
digests its release records and builds one only when that pull fails,
so the lane docs, the export guide and the verifier's comments now say
so, and the mirror step fetches only the bases those builds name. The
public site describes the released package, which still runs 0.9.1334,
so its guide and evidence page keep that version until a release ships
the new pin.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-11-06 01:53:53 +08:00
dd31958743 fix(discovery): advertise attached IDA navigation effects (#7017)
* fix(discovery): advertise attached IDA navigation effects

The canonical tool audit now includes the UI and network effects of the attached IDA legacy profile for the four navigation operations. Session inventory still starts from the provider-neutral evidence base and composes the selected provider descriptors, so headless and native sessions do not inherit attached UI state.

Adds SDK/session boundary coverage for all four operations and preserves attached, headless, and native distinctions.

* fix(discovery): cover IDA provider effects across tools and workflows

* test(discovery): isolate IDA metadata from host tool probes

* test(discovery): compare live identity with session effect bases

* fix(discovery): audit complete IDA provider effect union

* fix(discovery): exclude unreachable provider workflow effects

* fix(discovery): propagate IDA effects through composed tools

* fix(artifacts): deduplicate keyed archive path diagnostics

* test(artifacts): use real bundle in provider binding fixture

* test(mcp): align overview inventory fixtures with row contracts

* fix(javascript): align Electron path and owner identities

Refresh archive close and function CLI fixtures for current admission and Result contracts.

* test: align boundary fixtures with runtime prerequisites

* test(javascript): preserve unknown Electron IPC ownership in fixture

* fix: audit IDA network effects during lifecycle cleanup

* test: align close lifecycle hint with network audit

* style(website): format merged Wrangler watch paths

* fix(discovery): retain retry and cleanup effects

* style(website): drop unrelated Wrangler formatting

The watch_dir reflow is whitespace only. The IDA navigation-effect fix stays.

---------

Co-authored-by: dhruvac29 <[email protected]>
Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
2026-11-06 01:47:56 +08:00
f76be69428 fix(cli): reject non-decimal byte-entropy window options (#8919)
* fix(cli): reject non-decimal byte-entropy window options

* fix(cli): admit artifact numeric options as decimals

Keyed-archive offset and limit, asset-catalog paging, and interface-builder budgets use the same decimal token check as byte-entropy windows. Defaulted fields stay numbers.

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
2026-11-06 01:46:53 +08:00
c1ee8289c9 fix(signals): name the limiter as a possible cause of a failed sigrok version probe (#6395)
* fix(signals): report a failed sigrok version probe as a configuration error

* fix(signals): attribute a failed version probe to the limited probe

* fix(signals): name the limiter as a possible cause of a failed version probe

The version probe runs under the resource limiter, so a nonzero status
can come from prlimit as well as sigrok-cli. Say so in the configuration
refusal instead of blaming sigrok-cli alone, and cover a limiter that
refuses to start the tool for both decode and discovery.

* test(signals): assert limiter probe failures by typed fields

The limiter-refused recording test locked English probe wording. Assert the capability-unavailable code, provider, operation, and captured limiter output instead.

* fix(signals): record an unattributed version-probe status owner

A nonzero resource-limited version probe now reports version_probe_status_owner limiter-or-tool. The limiter-refusal test requires that field, so the old sigrok-cli-only failure no longer passes.

---------

Co-authored-by: Abelo9996 <[email protected]>
Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
2026-11-06 01:46:40 +08:00
560674e2de feat(native): report binary hardening for ELF, PE and Mach-O (#8524)
* feat(native): report binary hardening for ELF, PE and Mach-O

Documents inspect_binary_hardening and its CLI equivalent, including
per-property states, supporting evidence, read budgets, and one report
per Mach-O slice. Explains that missing protection imports remain
unknown, signature presence does not establish validity, and hardened
runtime requires inspect_signature. Adds hardening links throughout the
documentation. The supplied diff also removes the advanced CodeQL
workflow and configuration, along with snapshot-binding mismatch
guidance.

* fix(native): validate declared payloads and dynamic tables before hardening rows

Run the shared range checks for ELF, read imports from the dynamic tables
rather than section metadata, and keep an unresolved symbol count apart from
an absent table. Check every Mach-O command before building rows and retain
only the offsets the rows need.

Make the verifier's readelf output wide, check the emitted format before
parsing Mach-O, and place the sources before the libraries in the link.

* fix(native): share Mach-O segment admission with hardening reads

A segment with a section count its cmdsize cannot hold passed the hardening
reader and could enable restrict_segment. Move the size, section count and
file range checks to a shared admission that readMachoImage and the hardening
reader both use, and pin the ELF load-size case with a regression test.

* fix(hardening): reject Mach-O section payloads past the slice and empty ELF interpreters

Segment admission never checked each section's payload range, so a
file-backed section past the slice still produced rows. Decode sections with
the section header owner and mark the slice malformed when a payload leaves
it; zero-fill and empty sections own no file bytes and are exempt.

admitInterp accepted a NUL first byte in a longer extent, which counted an
empty interpreter as PIE evidence. It now requires a nonempty path.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01KuWHGvzJkrhCqjtEHUwqyH

* fix(native): read CFG instrumentation from decoded GuardFlags

Use the IMAGE_GUARD_CF_INSTRUMENTED flag that guardFlagRecord already
decodes instead of masking the raw GuardFlags word a second time.

* fix(native): reject ELF table spans a second PT_LOAD remaps

The hardening reader mapped DT_STRTAB, DT_SYMTAB and hash tables through
the one PT_LOAD containing the table's first address. A second PT_LOAD
starting inside the table could map the rest of it from other file bytes
and the reader still reported parsed import evidence.

Cap each mapping at the next PT_LOAD that starts inside it, reject a
requested range that crosses that boundary, and stop hash reads at the
same limit, matching the conflicting-mapping refusal in readElfSymbols.

* fix(native): dedupe ELF note tables only on shared boundaries

The hardening reader dropped any note table whose bytes another table
already covered. A covering SHT_NOTE can hold a non-GNU note whose
descriptor is itself a complete GNU property note, while PT_NOTE names
that inner note directly. Walking only the outer stream missed the GNU
marker and reported CET disabled.

Drop an alias only when it starts and ends on boundaries of admitted
tables, which are note boundaries of the walked streams. Any other
covered alias leaves the feature rows unknown, as a partial overlap
already does.

* fix(native): admit every Mach-O symbol record after a canary match

The Mach-O stack protector scan returned as soon as it found an
undefined ___stack_chk_fail, so a later undefined symbol whose n_strx
lay outside the string table was never checked. The same damaged table
parsed or failed as malformed depending only on record order.

Keep the match and finish the bounded record walk. Records after the
match are only checked against the string table bounds, so their names
are not read or charged to the scan budget. The ELF reader already
admits its whole symbol table before matching names.

* fix(native): accept ELF load aliases with the same file translation

The hardening reader treated every PT_LOAD that starts inside a table,
or that also contains the table's address, as a conflicting mapping.
A load with the same address-to-file translation names the same file
bytes, so tables it overlaps still have one interpretation, and the
canonical ElfLoadMap already merges such spans.

Treat equivalent loads as one interpretation. A load with a different
translation, or the zero-filled tail of an equivalent load, still ends
the usable range, so a remapped table is still rejected.

* fix(native): keep same-span ELF note tables with different alignments unknown

A section and a program header that share endpoints were treated as one
note stream even when their effective note alignments differed. Walking the
same bytes at 4 and 8 can frame different notes, so dropping the second table
could hide a GNU property word and report CET or BTI as disabled.

Require matching effective alignment at both endpoints, and no alignment
change inside the covered span, before treating a table as an alias.
Otherwise mark the feature rows unknown. The fixture's .note.gnu.property
section now declares the class's note alignment, as toolchains emit it.

* refactor(native): read LC_SYMTAB through the shared fixed-word decoder

Move the Mach-O fixed uint32 command layouts out of MachoCommandNames
into domain/apple/machoLoadCommands so there is one owner of those
offsets. MachoCommandNames and the hardening reader now both decode
through it; the hardening reader keeps its own bounds and import policy.

* fix(native): skip Mach-O common definitions in canary imports

A common definition sets N_UNDF|N_EXT like an import but stores its
allocation size in n_value. An object holding only
`int __stack_chk_fail;` therefore reported stack_protector: enabled.

Require a zero n_value before an undefined external counts as an
import, for both nlist widths.

* refactor(native): read linkedit data commands through the shared fixed-word decoder

readMachoLinkeditDataCommand reproduced the dataoff/datasize offsets that
FIXED_WORD_FIELDS already owns. Take the command and decode through
decodeMachoFixedWordFields, as readMachoSymtabCommand does, keeping the
truncation and endianness behavior.

* fix(native): skip empty SHT_NOTE sections in ELF hardening

A zero-sized SHT_NOTE owns no bytes, so the section directory marks it
not file-backed and the hardening reader rejected the whole image as
malformed, while readElfImage accepted the same bytes. Skip empty note
sections before the file-backing check; they have nothing to walk.

* fix(scripts): run hardening verifier commands through the shared owner

The binary hardening verifier started its probes and compiler drivers with
raw spawnSync, which bypassed the src/process/ supervision that AGENTS.md
requires. Every launch now goes through createVerifierCommands, and the
temporary root is removed only after the owner closes cleanly.

* fix(scripts): clean up hardening verifier commands on Ctrl-C

SIGINT or SIGTERM ended Node before withVerifierCleanup ran, so detached
compiler trees and the temporary root could outlive the verifier. The
verifier now turns those signals into an abort that every owned command
receives, so cleanup closes the owner and removes the root before exit.

* fix(scripts): fail the hardening verifier when a signal lands outside a command

Only the owned commands read the interruption signal, so a SIGINT or
SIGTERM during the final cleanup or between commands was recorded and
then dropped, and the verifier exited 0. After cleanup settles, the
verifier now throws the interruption reason. A failure the operation or
its cleanup already raised still leaves first and is not replaced.

* fix(native): fail Mach-O slices with a malformed encryption command

The hardening reader turned every malformed status from the shared
encryption reader into an unknown `encrypted` row, so a short
LC_ENCRYPTION_INFO_64 or an encrypted range past the slice still left the
slice parsed with a full report.

Only two conflicting encryption commands stay an unknown row now. Any other
encryption defect fails the slice like other structural command failures.

* fix(native): ignore STB_LOCAL undefined ELF symbols as imports

A local undefined __stack_chk_fail or __memcpy_chk is never resolved
against another object, so it does not establish an external import.
Skip STB_LOCAL bindings when reading protector signs, and give the
fixture's positive symbols STB_GLOBAL binding.

---------

Co-authored-by: Matt Van Horn <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-11-06 01:07:00 +08:00
morluto ec3410998b fix(ci): align tests with current evidence contracts 2026-11-06 00:31:09 +08:00
Kaoru0822andClaude Opus 5.5 fe677f6cf9 fix(rizin): keep debug targets off the debugger command stream (#8647)
* fix(rizin): keep debug targets off the debugger command stream

A Rizin debug target inherited Rizin's stdin, which is REA's command
stream. Once resumed, a target that reads stdin consumed REA's commands,
so close returned cleanup_incomplete and the one-shot CLI hung.

Route the target's stdin to the caller-selected TTY alongside stdout and
stderr. Before detaching, refuse a target whose stdin is still the command
stream, so a Rizin or backend that ignores the key gets a specific,
retained failure instead of a resumed target and a timeout.

Fixes #8642

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* test(rizin): keep the stdin test launcher's descriptors under dash

The launcher gated its target with `cat "$2" >/dev/null`. dash applies a
simple command's redirection to the shell itself until the command returns,
so on Ubuntu the waiting shell's fd 1 was /dev/null, and that shell is the
target REA reads in /proc. All 8 Linux-gated tests then failed the existing
output-descriptor check. macOS /bin/sh is bash, which redirects only in the
forked child, so this did not show locally.

Gate without any redirection. Production code is unchanged.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* test(rizin): reject unmodeled commands and own cleanup at acquisition

Address review of the stdin regression tests.

The launcher accepted any unknown line, so the marker that followed could
complete it, and assertions only checked that commands were present. An
added `dc` or a repeated `olj` still passed. It now records an unknown line
and exits, and the tests pin the exact command sequence.

Cleanup hung off module state assigned after setup finished, so a failed
setup left its directory and terminal child behind. Each resource now
registers its own finish hook when acquired, using createTestTempDirectory.

Success checks throw the typed error instead of asserting `.ok`, and the
refusal probe no longer sits inside a loop and conditional.

Production code is unchanged.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-11-05 18:38:12 +08:00
Mohamed Ayoub Eleuch 845eb54d2b fix(prompts): include existing static artifact investigation routes (#8972)
* fix(prompts): include existing static artifact investigation routes

* fix(prompts): preserve workflows in existing family scopes

* fix(prompts): require readers for Apple resource routes
2026-11-05 18:37:20 +08:00
morlutoandCursor Agent 56be61191e fix(mcp): report target after failed open (#8613)
* fix(mcp): report target after failed open

* test(session): cover failed open receipt ownership

* test(session): verify restored target receipt

* test(mcp): retain target on snapshot mismatch

* test(mcp): retain oversized open failure receipts

* test(cli): avoid continuing session receipt in one-shot errors

* fix(mcp): report provider after failed switch

* test(ghidra): verify failed open preserves annotations

* test(mcp): join queued receipt request before cleanup

* test(session): assert open failure receipts in lifecycle suites

* test(session): keep open-failure receipt checks within function limits

Move the provider-collision case and snapshot fixture setup out of the
functions that grew when both failure receipts were asserted.

---------

Co-authored-by: Cursor Agent <[email protected]>
2026-11-05 18:35:22 +08:00
Beqa 3395c9e6b3 fix(ida): admit Docker host-gateway names as local MCP hosts (#8932)
Containerized REA reaches an IDA MCP server on the container host through
host.docker.internal (Docker, Podman compatibility) or
host.containers.internal (Podman). The HTTP registration refinement only
admitted localhost, 127.0.0.1 and [::1], so doctor reported the valid
gateway registration as invalid_configuration.

Admit both gateway names in the registration's local-host set. The
refinement still requires http or https, no URL credentials, and refuses
every other host.

Fixes #8925
2026-11-05 18:34:28 +08:00
Jiabin Yin da79ea0baa fix(il2cpp): reject module image references outside retained inventory (#8956) 2026-11-05 18:33:36 +08:00
Beqa 4362a0848e fix(javascript): transient Windows private-runtime source-transfer (#8769)
* fix(javascript): transient Windows private-runtime source-transfer

Parent source-transfer into the Windows private runtime root failed with
EPERM on path-based exclusive create. Write through a native transient
private-runtime path that closes the handle after flush so per-job rm
still works, and yield around the sync write so abort deadlines can fire.

* fix(javascript): yield timer phase before Windows source-transfer write
2026-11-05 18:32:43 +08:00
highmachs 7b6fc6f13d fix(javascript): resolve electron findings through aliases of interop results (#8914)
* fix(javascript): resolve electron findings through aliases of interop results

* fix(javascript): resolve interop aliases iteratively and check target mutations

* fix(javascript): retain possible writes through uncertain aliases and guard positive reads

* perf(javascript): cache alias change status across semantic interop lookups
2026-11-05 18:31:52 +08:00
highmachs 281dbc8514 fix(apple): preserve exact integer precision with post-table plist objects (#8910) 2026-11-05 18:30:59 +08:00
027c768f07 fix(captures): classify unresolvable capture paths as capture_path input (#7814)
* fix(captures): classify unresolvable capture paths as capture_path input

Fixes #7636

* test(captures): cover unresolvable descriptor paths

---------

Co-authored-by: Abelo9996 <[email protected]>
Co-authored-by: morluto <[email protected]>
2026-11-05 18:30:07 +08:00
amyanger a9febababc fix(browser): report an out-of-range generated position as a selection fault (#7133)
trace_web_source_location reported a well-formed generated_position past
the end of the selected script as invalid_format on generated_position,
with no bounds, so callers were told their input was malformed and not
which coordinate to change. Report out_of_range on the line or column
that is past the end, with its valid bounds, as export_web_scripts and
inspect_web_network_capture now do for their selections.

The position scan now returns the bound it ran past, so the offset and
the reported limit come from one pass and cannot disagree on line
terminators.

Fixes #7132
2026-11-05 18:29:14 +08:00
akushonkamenandakushonkamen 0714eba505 fix(gdb): keep inferior-tty verification for read-only queries (#5539)
* fix(gdb): keep TTY verification across read-only inferior-tty inspection

The console command path cleared `inferiorTtyVerified` before dispatching
any command whose text merely mentioned `inferior-tty`, so the read-only
`show inferior-tty` query permanently revoked a persistent session's
verified output sink; no in-session path ever restored the flag, and a
later close of a live session failed detach verification with
capability_unavailable.

Exempt the exact read-only query forms (`show inferior-tty` and the MI
`-inferior-tty-show`) from the output-mutation predicate; every other
command mentioning the TTY setting keeps revoking, so mutating commands
still fail closed and detach keeps re-reading the live TTY before
detaching. Cover the query forms, composite commands, and the
inspect-then-close detach path with regression tests.

Fixes #5099

* test(gdb): classify show inferior-tty in the one-shot table and rename the TTY classification table

- Add a 'show inferior-tty' row to the 'classifies one-shot GDB
  commands' table: the read-only query no longer invalidates the detach
  proof, a user-visible one-shot behavior change.
- Rename the 'classifies read-only inferior TTY queries as non-mutating'
  table to 'classifies inferior TTY commands' since two of its rows are
  mutation cases.
- Assert via the existing recorded-commands array that the inspected
  'show inferior-tty' text is actually dispatched, and add an
  execute-path assertion that the MI '-inferior-tty-show' form leaves
  the verified-sink flag intact (defensive exemption: REA's own MI calls
  do not route through this gate).

---------

Co-authored-by: akushonkamen <[email protected]>
2026-11-05 18:28:22 +08:00
NoThingandSarthak Pandey fbfb1b768c fix(cli): normalize exit status on worker cleanup failure (#8049) (#8050)
* fix(cli): normalize exit status on worker cleanup failure (#8049)

* test(cli): verify SDK output status and finalization rejection on exit

* ci: retrigger checks

---------

Co-authored-by: Sarthak Pandey <[email protected]>
2026-11-05 18:27:29 +08:00
Beqa 08a923f722 fix(ghidra): report restricted-host temp write denial on Windows (#8889)
* fix(ghidra): report restricted-host temp write denial on Windows

Add a Windows doctor check that creates and deletes a file under os.tmpdir(), and classify a denied java_home.save in Ghidra startup output with its path, Win32 error 5, and TMP/TEMP remediation. The probe covers REA's own process only; a host that restricts only child processes is reported from the child's output at startup.

Fixes #7075

* fix(ghidra): probe the session root and retain failed probe cleanup

The Windows doctor temp_directory probe now writes under ghidraSessionRoot(), the same root Ghidra session runtimes use, instead of os.tmpdir(), which can differ when the inherited temp path has a dot-prefixed element. A probe file whose removal fails is retained on the doctor resource owner so doctor shutdown retries the removal; the result still reports its path.
2026-11-05 17:52:25 +08:00
xin zhuangandKaoru0822 c7fce66035 test(cli): cover Frida argument admission and spawn ordering (#3356)
* fix(cli): reject spawn arguments in Frida attach mode

* test(cli): remove redundant Frida recorder assignments

---------

Co-authored-by: Kaoru0822 <[email protected]>
2026-11-05 17:38:14 +08:00
morluto 0aaa582977 test(ghidra): check retained historical snapshot evidence (#8381)
* test(ghidra): check retained historical snapshot evidence

* test(ghidra): select historical observations by address
2026-11-05 17:37:29 +08:00
morluto 15107aa81b test(ghidra): check structured snapshot profile mismatch (#8536) 2026-11-05 17:36:46 +08:00
morluto 2be2d006b8 test(ghidra): check regex recovery at its owning boundary (#8578) 2026-11-05 17:36:02 +08:00
morluto ab9f7235a3 fix(evidence): clarify unsupported view recovery (#8412)
* fix(evidence): clarify unsupported view recovery

* fix(evidence): explain sensitive capture view refusal
2026-11-05 17:35:19 +08:00
morluto b8963c1808 fix(mcp): clarify retained dossier handoff in crash prompt (#8437) 2026-11-05 17:34:33 +08:00
morluto 9eff4f7265 fix(verification): use inspected Ghidra JDK (#8440) 2026-11-05 17:33:48 +08:00
morluto 6f3f06a891 fix(evidence): traverse large source link sets safely (#8457) 2026-11-05 17:33:04 +08:00
Rudy Mizrahi Celekli 7504f2ddfc fix(native): reject nonzero reserved ELF dynamic symbols (#7528)
fix(native): validate the reserved ELF dynamic symbol

Signed-off-by: Rudy Celekli <[email protected]>
2026-11-05 17:32:20 +08:00
Rudy Mizrahi Celekli 21ad5b1f67 fix(pe): classify valid DOS targets as unsupported (#7530)
fix(pe): classify confirmed DOS targets as unsupported

Signed-off-by: Rudy Celekli <[email protected]>
2026-11-05 17:31:35 +08:00
ShuaixinHuang 6fceda83d5 fix(devicetree): reject duplicate node paths in Evidence (#8970) 2026-11-05 17:30:50 +08:00
ShuaixinHuang 1dc83b160d fix(cli): align ADB directory and package query schemas (#8974)
* fix(cli): align ADB directory and package query schemas

* fix(cli): preserve ADB selector correction details
2026-11-05 17:30:06 +08:00
morluto 65986d0a0f docs(tool-design): audit agent workflow guidance (#8439)
* docs(tool-design): audit agent workflow guidance

* docs(tool-design): check deployed guidance versions
2026-11-05 17:28:48 +08:00
morluto 146064c2e9 fix(enhanced): restore batch input record limit 2026-11-05 17:20:05 +08:00
ShuaixinHuang 3484432fb1 fix(setup): flag unresolved migrated Claude Desktop storage (#8971)
* fix(setup): flag unresolved migrated Claude Desktop storage

* fix(setup): preserve Claude MSIX process view when config is unresolved
2026-11-05 17:14:19 +08:00
Martin Klein d854fb155a fix(jeb): reconnect after stale MCP sessions (#3754)
* fix(jeb): reconnect after stale MCP sessions

* fix(jeb): preserve failures for overlapping MCP requests

* fix(jeb): make cleanup operations explicit and stabilize timeout fixtures
2026-11-05 17:05:29 +08:00
Wade 9317c7d422 fix(pyinstaller): leave PE carrier binary members unmarked (#6730)
The Windows bootloader sets no execute bit on extracted ARCHIVE_ITEM_BINARY
members (the S_IXUSR fchmod in pyi_archive.c is #ifndef WIN32). Mark type b
members executable only when the carrier dialect is posix.
2026-11-05 17:05:12 +08:00
2SinceJuly fc20aebb94 fix(decompile): retain inventory limitations and selection (#8466)
* fix(decompile): retain inventory limitations with the latest selection

The manifest and decompile index dropped procedure-inventory
limitations, so a directory separated from its Evidence response could
not explain why functions were selected or omitted. Keep those
limitations on the manifest and in a latest_run index record that also
names the filter and counts that produced them. Older runs remain
represented only by their retained function records.

Fixes #8355

* test(decompile): surface export errors and check function caveats

Address review findings on the inventory-limitation regression:

- Throw the typed export error instead of returning from a failed Result.
- Assert historical function records keep their own pseudocode caveats
  and do not receive the latest inventory warnings.
2026-11-05 17:04:55 +08:00
RRFZ 1f14d648fa fix(artifacts): handle early ASAR extraction cancellation (#5672)
* fix(artifacts): own source errors during output setup

* fix(artifacts): join silent source destruction before returning

* fix(artifacts): retain source cleanup failures with primary errors

* fix(artifacts): join source finalization and classify secondary failures

* fix(firmware): separate source cleanup from output rollback

* fix(artifacts): retain live source cleanup dependencies

* fix(artifacts): preserve source failure completion and semantics

* fix(artifacts): retain scope cleanup owners and typed failures

* fix(artifacts): keep cleanup classification and admission recovery

When the operation completed and only scope cleanup failed, keep the cleanup failure's own reason instead of reporting io, matching main (fixes artifactProvider.cleanupRetry). When a typed primary is rebuilt with pending scope cleanup, carry its userMessage and ASAR header admission so callers keep the configured ceiling and setting.

* fix(artifacts): translate plain file-close failures through the reader path

The outer file-cleanup branch rebuilt every ArtifactFileCleanupFailure as a
ProviderAdapterError, so dyld reads lost their io, unavailable, format and
cancelled reasons and their exact file-cleanup cause. Compose separately only
when the failed primary owns its own translation (a domain error, source
failure or scope cleanup failure); raw and reader primaries are already
projected by the file failure with its exact lease.

* fix(artifacts): bound source destruction by cancellation and keep wrapper admission

After cancellation, SafeOutputTree.write could wait indefinitely for a source
whose destruction did not settle, both in the destroy join and in the
iterator's wait for close. Both waits now end at cancellation plus a fixed 1 s
grace, modeled on the process-output closure grace. A destruction that
outlives it is reported as incomplete source cleanup under an exact lease that
releases only once closure is observed without a late error.

ArtifactResourceCleanupFailure and SafeOutputSourceFailure now keep the
primary's admission, matching withCleanup.

* fix(artifacts): keep one settled wait while observing a stuck source

observeClosure raced the same pending close promise against a new timer every
10 ms, so a source whose destruction never settled accumulated one reaction per
poll for as long as it stayed open. Each poll now waits on its own promise that
the close listener or the unref'd timer settles, leaving one pending wait and
one close listener. The lifecycle test also asserts that a late close releases
the retained owner without an explicit retry.

* fix(artifacts): settle pending reads before cancellation

* test(artifacts): observe the consumed ZIP iterator on cancellation

* test(artifacts): validate projected cleanup resources before sorting

* test(mcp): reserve parent capacity for timeout recovery

* fix(artifacts): contain ASAR cancellation before output consumption

* test(artifacts): hash ASAR cancellation fixture bytes
2026-11-05 17:04:37 +08:00
Lucien 585fe56712 fix(enhanced): bound batch decompile input size (#5937) 2026-11-05 16:57:52 +08:00
Salman AlmutairiandClaude Opus 5.5 fc3ff7b833 fix(process): report deadlines expiring during final cleanup (#7635)
* fix(process): report a deadline that elapses during final cleanup as a timeout

runOwnedCommand checked its absolute command deadline before awaiting
process cleanup, but on the ordinary successful path it consulted only
the caller's cancellation signal afterwards. A command whose deadline
expired while successful cleanup was still awaited therefore resolved
as success.

After cleanup, an elapsed deadline now throws OwnedCommandFailure with
reason "timeout" and the completed snapshot, as docs/tool-design.md
requires a final signal check after the last awaited finalization step.
Existing primary failures, caller cancellation and incomplete-cleanup
classification keep their precedence.

Fixes #7369

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* test(process): drive the cleanup-deadline regression with controlled time

The regression test gave output collection 100 ms of real time before
the deadline. On a busy worker the deadline could expire during
collection instead, taking a different failure path and failing the
test even with the fix. Use fake timers, wait until cleanup has
started, then advance past the deadline and release cleanup, so the
ordering no longer depends on worker speed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-11-05 16:57:34 +08:00
Salman AlmutairiandClaude Opus 5.5 25923d372c fix(process): reject contradictory filesystem capture facts (#7631)
* fix(process): reject captures whose top-level filesystem facts contradict their checkpoints

A process capture records its filesystem state twice: files_before,
files_after and filesystem_effects at the top level, and the initial
and final filesystem_checkpoints. Validation never reconciled the two,
and comparison reads only the checkpoints. A caller-authored capture
recording a created file at the top level while its final checkpoint
was empty was therefore accepted and compared as unchanged.

Semantic validation now requires the initial checkpoint to match
files_before with no effects, and the final checkpoint to match
files_after and filesystem_effects. The native producer builds both
representations from the same snapshots, so produced captures are
unaffected; consistent created-file captures still compare as changed.

Fixes #7371

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* test(process): cover file-list checkpoint mismatches independently of effects

Every rejected case in the checkpoint-consistency test also disagreed on
effects, so removing both file-list comparisons left it passing. Add
initial and final checkpoint cases whose file lists alone disagree while
effects and coverage accounting stay valid.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-11-05 16:57:16 +08:00
Salman AlmutairiandClaude Opus 5.5 e1094313f5 fix(changed-behavior): withhold equality for unknowns and contradictions (#7624)
find_changed_behavior derived behavior_status from each process
comparison's aggregate status alone. An unchanged comparison whose
first_divergence was unknown (residual unknowns prevent proving no
divergence) and a consistent contradiction comparison both fell
through to observed_unchanged, with no unresolved finding and no
changed-behavior registry unknown.

An unchanged comparison with unknown divergence now counts as unknown
and adds one comparison-level unresolved first_divergence finding
carrying its reason, as artifact comparisons already do for coverage.
A contradiction status makes the aggregate unknown, and contradiction
dimensions are classified and counted as contradictions instead of
unresolved branches. Complete unchanged comparisons stay
observed_unchanged and changed comparisons with unknown localization
stay observed_changed.

Fixes #7384
Fixes #7383

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-11-05 16:56:58 +08:00
Hasan ZayniandClaude Opus 5.5 c1df51c7cb fix(uninstall): preserve caller-selected Kuna caches during purge (#7921)
* fix(uninstall): keep a managed cache namespace whole when it holds a caller-selected Kuna cache

purge --purge-data reported a REA_KUNA_CACHE_DIR selection below the default Kuna cache as retained, then removed it together with its parent namespace. A namespace that contains the selection is now retained and reported, so the retained report is true.

Fixes #7344

* fix(uninstall): also keep a namespace equal to, or reached by symlink traversal from, the selected Kuna cache

Compare the namespace and the selection with the same normalization, treat an equal path as contained, and resolve existing paths before planning so a symbolic link followed by .. is judged by where it leads.

* fix(uninstall): judge a selection with parent traversal where the filesystem resolves it

Collapsing .. lexically contradicts the existing rule that parent traversal after a symbolic link leads elsewhere, and broke the retained-through-symlink-traversal test. Compare spellings without .. directly and resolve existing paths for the rest, so the symlink-parent case still purges the default cache while a selection that resolves into a managed namespace is retained.

* fix(uninstall): keep a namespace whole when the selection's location is unknown

A realpath failure other than ENOENT/ENOTDIR (for example EACCES under an
inaccessible parent) no longer counts as "absent". Each namespace that may
contain the caller-selected Kuna cache is retained instead of purged.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-11-05 16:56:12 +08:00
ShuaixinHuang a2134b3a74 fix(javascript): resolve module-level Electron options in functions (#8960)
* fix(javascript): resolve module-level Electron options in functions

* fix(javascript): limit module option ancestry walk

* fix(javascript): preserve uncertain captured argument ordering

* fix(javascript): retain hoisted activation uncertainty

* fix(javascript): retain function ownership through loops

* fix(javascript): account for classic global activations

* fix(javascript): bind first activations to lexical identities

* fix(javascript): retain uncertain global activation timing

* fix(javascript): isolate receiver facts and share computed reads

* fix(javascript): retain projected and opaque global activations

* fix(javascript): refresh activation bounds as opaque effects grow

* fix(ci): format website watch directories without changing configuration

* fix(javascript): share source reads across opaque-effect updates

* fix(javascript): retain constructed-code global activation bounds

* fix(javascript): share runtime activation and receiver facts

* fix(javascript): reuse pattern reads and function positions

* fix(javascript): isolate copied paths and preserve runtime reads

* style(javascript): format migrated reference imports

* fix(javascript): normalize constructor activation and receiver effects

* fix(javascript): retain aliased receiver timing and lexical global this

* fix(javascript): preserve effect-free receiver identity across activations

* fix(javascript): retain outer activation and prototype effects

* fix(javascript): index implicit reads without deep path rescans

* fix(javascript): retain aliased prototype and parameter read uncertainty

* fix(javascript): index reflected reads through intrinsic invocation facts

* fix(javascript): retain unresolved global activation reads

* chore: refresh PR branch after review fix publication

* fix(javascript): retain projected receiver reads and cache method facts

* fix(javascript): retain inherited and implicit activation reads

* fix(javascript): retain caller-owned this uncertainty

* fix(javascript): retain reflected reads with unresolved spread arguments

* fix(javascript): preserve enclosing activation for decorators

* fix(javascript): track intrinsic literal prototype effects

* fix(javascript): retain catch activation from implicit reads

* perf(javascript): reuse effective keys for projected reads

* fix(javascript): retain possible caller-owned ambient effects
2026-11-05 16:52:19 +08:00
ShuaixinHuang ce07d358cd test(gdb): expect configurable stream truncation diagnostics (#8965)
* test(gdb): expect configurable stream truncation diagnostics

* build(gdb): include prerequisite duplicate-import repair from #8884
2026-11-05 16:49:48 +08:00
김준호andt 92f332652d fix(adb): separate unresolvable paths and host capacity in adb spawn failures (#6188)
* fix(adb): name unexecutable adb paths in readiness refusals

When REA_ADB_PATH or the PATH-resolved adb is a directory or a file the
host refuses to execute, the version probe fails at spawn with EACCES or
EPERM. That fell through to the generic "did not report a usable version"
refusal, which named neither the path nor the cause.

Classify those spawn errors as binary_not_executable (provider rejection
executable_missing), name the selected path and errno, and give
remediation for the environment, PATH and host-policy cases. The generic
fallback now names the selected path too.

Fixes #5689

* fix(process): admit synchronous spawn errors as captured failures

Node throws spawn errors other than EACCES, EAGAIN, EMFILE, ENFILE and
ENOENT synchronously from execFile. execFileBytes called execFile inside
its Promise executor, so ENOTDIR or EPERM became a rejection without
capture fields and execFileOutputFailure returned undefined. Consumers
that classify by failure code, such as the adb version probe, could not
see the code, which left the adb EPERM branch unreachable.

Catch synchronous spawn errno errors at the capture boundary and attach
the same empty capture an asynchronous spawn failure carries. Argument
validation errors stay unadmitted.

The adb version probe now reports an ENOTDIR selected path, such as a
REA_ADB_PATH whose parent is a regular file, as a missing binary with
the filesystem error code instead of the generic version refusal.

* fix(process): expose spawn failures from the capture owner

execFileBytes knows when a command never started, but
ExecFileOutputFailure did not say so, so consumers reread raw syscall
and code fields and disagreed. Mark both the synchronous throw and an
asynchronous `spawn <path>` error with `spawnFailed: true`, and set
`path` on the synchronous error to match the asynchronous shape.
Argument validation errors stay unadmitted.

Consumers now use the owner field:

- inspectAdbClient never reports a spawn errno as `exit_code`. ENOENT,
  ENOTDIR, ELOOP and ENAMETOOLONG are `binary_missing`; EACCES and EPERM
  stay `binary_not_executable`; any other spawn errno, such as EINVAL,
  is `binary_not_executable` with a path-naming "could not be started"
  message. Each records `filesystem_error` and `path_source`. EACCES
  remediation also names parent directory search permission, and EPERM
  remediation no longer names host policy as the only cause.
- DarwinProcessRunTokenReader tags every not-started compiler as
  `spawn_failed`, not only ENOENT. EACCES, EPERM and ENOTDIR were
  previously `failed`.
- NativeDmgArtifactReader uses one not-started rule for both
  `processNotStarted` (previously `spawn ` only, so synchronous errors
  were missed) and the ENOENT unavailable/io split (previously any
  `spawn` prefix). Its fake hosts now reject with real execFileOutput
  spawn failures.

* fix(process): classify spawn failures consistently across consumers

Admit synchronous and asynchronous spawn errnos through one helper that
requires an errno and a `spawn` or `spawn <path>` syscall. The
synchronous shape now also gains `spawnargs`, so both shapes match.

Use the owner's `spawnFailed` fact in the remaining consumers:

- inspectAdbClient reports exhausted host capacity (EAGAIN, EMFILE,
  ENFILE, ENOMEM) as a host failure to retry and no longer tells the
  caller to change REA_ADB_PATH. Path-resolution errnos come from a
  predicate the filesystem owner exports.
- runAdbCapture says adb could not be started instead of claiming an
  exit code for a process that never ran.
- The Darwin compiler probe words every spawn_failed outcome as a start
  failure, not a compilation failure.
- Asset catalog inspection treats any assetutil that never started as
  unavailable, not only ENOENT.

* fix(process): report host spawn capacity as a retryable constraint

Spawn errnos that report exhausted host capacity (EAGAIN, EMFILE, ENFILE,
ENOMEM) were classified by adb readiness alone. assetutil reported them as
an unavailable tool, which marked inspect_asset_catalog unavailable in the
session, and adb commands reported them as protocol failures.

Classify them once in the process capture owner and report a retryable
capacity constraint with recovery advice from assetutil and adb commands.

* docs(adb): describe host capacity failures from device operations

* fix(adb): report exhausted host spawn capacity as runtime unavailability

A host that cannot start adb for lack of process, memory or descriptor
capacity is not a configuration problem. Give it its own failure code and
map it to the runtime_unavailable provider rejection instead of
not_configured.

* fix(adb): report probe-time host capacity as a retryable constraint

A device operation whose adb version probe could not start for lack of host
capacity was refused as capability_unavailable without retryable. It now
reports the same retryable capacity constraint as a later adb command.

* fix(adb): keep the selected adb path in probe-time capacity errors

The retryable capacity constraint raised at the version probe dropped the
selected_binary and path_source diagnostics the refusal used to carry.

* refactor(adb): keep the spawn-failure fact to adb consumers

Restore the DMG reader, Darwin compiler probe and assetutil to main. The
capture owner still marks a never-started command, including the spawn
errors Node throws synchronously, and only the adb readiness probe and
adb commands read that fact.

* test(adb): assert spawn-failure facts instead of refusal wording

The new adb tests compared complete refusal sentences and banned an old
phrase. They now check the errno, selected path, path source and the
owner's spawn-failure marker, so wording edits do not break them.

* fix(adb): own one host-capacity remediation and cover the spawn route

The host-capacity constraint and the configuration failure each carried their own remediation sentence. AdbConfiguration now owns the text and both use it, so the constraint's remediation.action equals diagnostics.remediation.

The synchronous spawn-throw test now runs on Node's execFile route and on the owned spawn route adb uses (stopSignal). The provider test covers inspect_adb_client under EMFILE next to list_adb_devices, and the docs say which operations report the retryable capacity constraint and which report runtime_unavailable.

* fix(process): report EMFILE and ENFILE spawn failures without stdio

After an EMFILE or ENFILE spawn failure Node creates no stdio, so the
owned stop-signal route threw a TypeError while reading the streams and
the later spawn error went unhandled. Attach the error listener first and
capture absent streams as empty, so adb reports retryable host capacity.

* fix(adb): keep a completed push when host capacity stops its digest check

A capacity failure starting sha256sum came back as a retryable error even
though adb push had already written the file. Repeating the request with
overwrite=false is then refused because the destination exists.

Return the receipt with an unavailable device digest and name the capacity
failure in its limitations, so the caller is told the transfer is unverified
rather than advised to retry.

* docs(adb): list host capacity as an unverified push case

---------

Co-authored-by: t <t@t>
2026-11-05 15:40:32 +08:00
Adolanium 0686ea60f6 fix(cli): parse environment-serve --port and --host like rea mcp --http (#7092)
* fix(cli): parse environment-serve --port and --host like rea mcp --http

environment-serve declared --port as a number, so incur coerced the flag text with Number(): an empty --port= bound an ephemeral port instead of failing, and 0x1F90 or 1e3 were reinterpreted. --host accepted whitespace. docs/cli.md says these flags resolve like rea mcp --http, which accepts only decimal port text and a non-empty trimmed host. The flags now use the same rules and are refused before binding.

* refactor(cli): reuse the shared MCP listener port and host schemas
2026-11-05 15:39:46 +08:00
Paritosh LahreandClaude Opus 5.5 5fd325066d fix(setup): replace the fixed Hopper download deadline with a stall timeout (#7150)
* fix(setup): replace the fixed Hopper download deadline with a stall timeout

Hopper package downloads aborted after a fixed 120 s, which was also the
largest deadline the download owner accepted. The current 32 MB Linux
package needs about 132 s at the reporter's sustained 245 KB/s, so setup
could never finish on that connection, and the timeout surfaced only as
Node's generic abort message.

Abort a download when no response bytes arrive for 30 s, or when it
outlives a 15-minute overall bound, with a tagged
HopperDownloadTimeoutError that names the URL, the limit reached and the
bytes received. Linux and macOS setup already relay a thrown download
error's message; the Linux publisher now keeps it for release metadata
and public key fetches instead of a generic verification failure.
Metadata and key fetches keep their 10 s bound.

Refs #7077

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(setup): keep the macOS release feed on the metadata limit

The stall-timeout change left the macOS release feed fetch on the
package download bounds. That fetch could then wait up to 15 minutes
instead of the 120 s it had, while the installation guide says release
metadata keeps a 10 s limit. Only the Linux publisher owned that limit.

Move the 10 s metadata limit into HopperDownload.ts beside the package
bounds. The Linux feed and key fetches use it, and the macOS installer
now passes it for its feed fetch through the installer host.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-11-05 15:39:03 +08:00
hhangandhhang 8e3d794834 fix(gpu): distinguish ELF string scan limits from corruption (#8955)
Co-authored-by: hhang <[email protected]>
2026-11-05 15:38:19 +08:00
hhangandhhang 0dcb456bbe fix(environment): retain ID ordering with missing clocks (#8957)
* fix(environment): retain ID ordering with missing clocks

* fix(environment): compare known creation clocks across gaps

---------

Co-authored-by: hhang <[email protected]>
2026-11-05 15:37:35 +08:00
highmachs 66d55c3bbb fix(native): declare temporary filesystem writes for inspect_macho (#8959) 2026-11-05 15:36:51 +08:00
ShuaixinHuang 347dc9a32c docs(mcp): explain PowerShell launch environment scope (#8961)
* docs(mcp): explain PowerShell launch environment scope

* docs(mcp): qualify saved setup selections and OpenCode environment
2026-11-05 15:36:07 +08:00
ShuaixinHuang 455d7953ec docs(testing): explain npm launcher selection in PowerShell (#8962) 2026-11-05 15:35:20 +08:00
ShuaixinHuang f6f565093f docs(debugger): document configurable session limits (#8963)
* docs(debugger): document configurable session limits

* docs(debugger): clarify GDB control command deadlines

* docs(debugger): qualify configurable GDB output budget

* docs(debugger): clarify one-shot CLI settings scope

* docs(debugger): preserve repository line endings
2026-11-05 15:34:34 +08:00
ShuaixinHuang 4bd58d577c fix(contracts): align managed example MethodDef scope (#8966) 2026-11-05 15:33:49 +08:00
ShuaixinHuang 97fe4535f6 fix(electron): reject non-directory scenario roots before launch (#8967) 2026-11-05 15:33:04 +08:00
Edgar ArakelyanandClaude Opus 5.5 970969cb53 feat(desktop): capture and export apps installed from a downloaded .deb (#8954)
* feat(desktop): capture a Linux desktop application in use

Add `capture-desktop-application`, a CLI-only, Linux and X11 capture that
launches an installed desktop application, watches it while it is used and
writes capture Evidence plus SHA-256 addressed content:

- inventory: desktop entry, launch command, executable, package and its
  apt sources (or snap, flatpak, AppImage), Electron markers and candidate
  state directories;
- session: the launched process tree, client windows from xprop with the
  first application window time and a window match, and files held open;
- state: a stat walk of the home directory before launch, at checkpoints
  read from stdin, at stop and after exit, with created, modified, deleted
  and retyped paths and the content of changed files and state directories.

It refuses non-Linux hosts, missing DISPLAY or xprop, a non-empty output
folder and an application whose window is already open, and signals only
processes it started.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): own launched processes, confirm walk coverage and keep cancelled captures

- Launch through the owned provider path with a run token; release with
  SIGTERM, then the token-verified group cleanup, after every session.
  Processes without the token are never signalled; each records whether it
  carries it, and the session records whether cleanup was verified.
- Report creations and deletions only where a walk listed the nearest
  existing directory; count the rest as unconfirmed.
- Retain a file only when it is still the walked version, through owned
  no-follow reads; charge the budget only for new content; create files
  exclusively in a private output folder owned by the caller.
- Walk content roots outside the home directory, compare paths in their
  physical spelling, and run executables from the caller's directory.
- Refuse when the window list cannot be read, re-evaluate window ownership
  each sample, handle one queued request per sample with a bounded stdin
  queue, and write the capture with end reason `cancelled` on interruption.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): keep owned cleanup failures and fresh digests

- Allow a capture cancelled before any requested checkpoint to hold only the
  baseline.
- Retry an incomplete stop once; if cleanup is still unverified, write the
  capture and fail with the run token and process group as cleanup
  resources.
- Classify path changes after a walk (ENOTDIR and other selected-file
  errors) as changed_since_walk instead of failing the capture.
- Treat a non-directory ancestor as proof that nothing existed beneath it.
- Report a before digest only for the file version it was read from.
- Derive stderr_truncated from stderr's own byte counts.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* wip(desktop): state view over a capture folder

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): keep failed file-handle closes owned for retry

Content reads pass retainFailedClose, so a handle whose close fails stays
owned by the content store; the capture retries those closes after it is
written and fails with the output folder as the cleanup resource if one
still cannot be closed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* feat(desktop): read a captured application's state

Add inspect-desktop-application-state, a CLI command that reads a capture
folder into the application's state: every retained or changed file with
its role and reasons, a typed JSON view of each version (SQLite through the
shared worker, dconf through a private dconf dump, JSON, XML, key files,
text) and the kinds of change between versions (lines, values, keys, rows).

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): report process and handle cleanup failures together

When both the launched processes and a retained file's handle remain
unresolved after the capture is written, one cleanup error now lists both
reasons and both resource sets.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* feat(desktop): export a captured application as an agentenv computer app

Add export-desktop-application, which writes an agentenv bundle with one
computer_app environment from a capture folder and its state view: the
apt package, a boot launch matched on the captured window class with a
window ready check, owned paths from the state roles, a seed of the
baseline files, data hooks that call agentenv's add/get/reset helpers
with a typed JSON view on get, a skill, a README and a deploy smoke task.
The desktop CLI commands now load their services lazily.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): keep state views faithful to the capture

- Read key files, XML attributes, dconf groups and SQLite tables into
  dictionaries without inherited properties, so captured keys such as
  __proto__ stay data.
- Version databases with their write-ahead log as of each checkpoint, and
  stage only digest-verified bytes for the SQLite and dconf readers.
- Detect the format per version, report format changes, keep JSON null and
  empty containers as values, keep XML text and preserved whitespace, and
  mark truncated views (including XML depth) and comparisons of them.
- Number versions and changes by checkpoint index, decide baseline
  presence from the captured type, and keep tagged SQLite integers, text
  bytes and blobs distinct.
- Carry the capture's limitations and omitted content forward, match state
  directories by their home path, index changes once per capture, keep
  failed handle closes owned, and finish the CLI through the shared owner.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): keep coverage, logs and handles honest in the state view

- Read coverage flags only from formats whose readers write them, so JSON
  fields named truncated stay data.
- Fail a SQLite version whose write-ahead log existed but was not retained
  or does not match its digest, instead of reading the database alone.
- Close retained content on every exit, including cancellation, and report
  a close that still fails together with the primary failure.
- Finish the CLI with the canonical operation name.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): export only what the computer can install and seed

- Refuse packages not from the Ubuntu 24.04 archive, since the computer
  installs by name from that archive.
- Start the launch command in the captured working directory.
- Respell the home path only in files the state view read as text.
- Report files that were not retained instead of dropping them silently.
- Load captured dconf keys for apps that own no files.
- Keep home-level files as owned paths; rename reserved Python class names.
- Mark truncated SQLite and text views in the class's get view, and read
  file types the way the state view does.
- Release the output tree through a resource scope, and read seeds through
  an owner closed on every exit.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): keep staged folders owned, symlinked state roots and XML values

- Stage database bytes in a private runtime root released through a
  resource scope, so a failed removal keeps its owner and is reported with
  the read's outcome.
- Report cleanup facts and owners from desktop failures instead of
  wrapping them as a plain adapter error.
- Record the state directories as walked, in their physical spelling, and
  match roles against both spellings, so files in a symlinked state
  directory are not mistaken for selected documents.
- Keep whitespace-only XML leaf values; drop only formatting between
  elements.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): read XML in the exported app the way the state view does

Keep whitespace-only leaf values and xml:space="preserve" text in the
generated get view; drop only formatting between elements.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): release retained handles with their owners, read older captures

- Release retained-content handles whose close failed through a resource
  scope on every exit; a release that fails again reports the exact handle
  owners. A read that finished keeps its bytes when only its close failed.
- Accept captures written before state directories were recorded, and say
  in the state view's limitations that symlinked state directories may be
  misclassed for them.
- Compute each XML element's whitespace context once instead of scanning
  siblings for every text run.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): boot without the working folder, bound XML, accept mirrors

- Try the captured working directory only when an argument is relative,
  and start the app even when that folder is missing.
- Cut the generated XML view at REA's node and depth limits, and keep one
  unreadable file from failing data/get.
- Accept Ubuntu 24.04 mirrors, not only the canonical hosts; PPAs and
  other repositories are still refused.
- Read seeds through the retained-content owner's new signature.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): state view review round

- Stage a retained -journal beside its database so the SQLite worker
  accepts an inactive journal and refuses a hot one; a -wal or -journal
  that existed but was not retained (including one omitted at the
  baseline) makes the version unreadable.
- Keep tables that read when another fails, and let cancellation and
  unfinished cleanup propagate instead of becoming view errors.
- Read text as a key file only when every line parses as one.
- Rank a file named in a window title or selected by the caller as a
  document even when the application holds it open; match titles on
  file-name boundaries; leave the display name out of dconf matching.
- Bound JSON and key file views (4 MiB, 500 levels, exact numbers) and
  report too-large, missing and altered retained content separately.
- Add no version or change while a database stays absent.
- Read staged databases in place instead of copying them again.
- Give Evidence the same limitations as the result.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): export review round

- Load captured dconf keys without D-Bus: install dconf-cli, compile the
  existing and captured key files into the user database, and warn on
  failure instead of stopping the server.
- Start relative-argument launches in the captured working directory,
  the home included, since the framework launches from the app folder;
  respell the home itself and option values under it.
- Read views in a thread, skip files over 64 MiB, align the generated
  readers with the state view's, and close SQLite connections.
- Never own the home or a shared base folder; map a symlinked state
  directory's files back to their home spelling.
- Report omitted files and unretained opened folders.
- Name the module so it cannot shadow another, and keep the class off
  the module's own names; derive names the bundle check accepts.
- Write seeds as they are read, and remove an unpublished bundle on
  failure.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): capture review round

- Ask the launch process group and token carriers to exit before
  killing them, on errors too; report incomplete cleanup with its facts.
- Count descendants of the launch as started by the capture even when
  their environment cannot be read; treat a reused pid as a new process.
- Follow symlinked desktop entries; name a flatpak's state after its
  application id; match flatpak windows by name only.
- Stop on end of input; refuse empty, duplicate and reserved checkpoint
  names; require whitespace after the checkpoint keyword.
- Stay on each walk root's device, and never walk or retain `/` or a
  directory containing the home; say so in the limitations.
- Refuse a window manager that does not publish _NET_CLIENT_LIST.
- Remove retained content when a capture fails before it is written.
- Skip reading a file larger than the remaining content budget.
- Record apt's archive origin for each package source.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): read an unset client list as no windows when supported

xfwm4 leaves _NET_CLIENT_LIST unset while it manages no windows, so the
capture refused an ordinary desktop with nothing open. Read
_NET_SUPPORTED with the list: an unset list means no windows when the
window manager supports it, and the capture is refused only when it
does not. `xprop`'s "no such atom" output now reads as unset too.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): accept packages by apt's archive origin

Accept a package source when apt reports its origin as Ubuntu for a
noble suite, so mirrors count and PPAs or other repositories that merely
have "ubuntu" in their URL do not. Captures made before origins were
recorded fall back to Canonical's own archive hosts. Seed reads report
why retained content could not be used.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* feat(desktop): capture and export apps from vendor apt repositories

A closed-source app installed from its vendor's apt repository, such as
Typora, can now be captured and exported as a computer app.

- The capture records each non-Ubuntu repository listing the installed
  version (uri, suite, components from apt's sources files, one-line or
  deb822) and the key that signs it: the signed-by key, or the trusted
  key that gpgv shows verifies the repository's signed index. The key
  is retained with the capture.
- The capture records the packages owning the libraries the executable
  loads (DT_NEEDED with RUNPATH and the linker cache, through REA's ELF
  readers), since a vendor package may not depend on them.
- The export declares a verified vendor repository as an AptSource with
  its key, installs a vendor package's library packages, and adds
  --no-sandbox to an Electron app's launch, since a container denies
  Chromium's sandbox.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): report a locked database at once in data/get

A database the running app holds locked made each data/get wait sqlite's
five-second busy timeout before reporting it. Record the Typora run in an
agentenv computer in the verification.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* perf(desktop): detect key files without splitting the whole text

Read lines one at a time and stop at the first that a key file view
would drop, instead of splitting a file of up to 64 MiB into arrays
before its format is known.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): keep symlinked homes and state folders under the home in exports

- Respell a path under its state folder's logical spelling only when that
  spelling is under the captured home, so a symlinked HOME keeps its
  files; pair walked and logical state folders only when the capture
  lists the same number, and match the innermost folder first.
- When two files map to one home path, keep the one present before the
  capture and name the other in the limitations.
- Read key files in the generated data/get with the state view's rule,
  checked against it by running the helper under python3.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): end captures on zombies and keep cleanup owners

- A zombie or dead process no longer keeps the session open.
- Remove only the partial capture file this run created.
- Keep the exact owners of handles and processes whose cleanup failed on
  the returned error, so a retry can release them; test the published
  capture's cleanup-failure branch with mixed outcomes and retries.
- Release the application once even when the exit checkpoint fails.
- A cancellation before the capture is written removes what it retained.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): vendor package review round

- Match apt sources through `apt-get indextargets`, which covers flat `./`
  and `/` suites and gives each release file and sources entry.
- Accept a key whose signature verifies even when the release carries
  another signature without a key here; keep only the verifying key from
  a keyring holding several (OpenPGP packet splitter).
- Install the package from its repository's release (`name/<release>`),
  declare one source per URI and suite, support only the capture host's
  architecture, and refuse Ubuntu Pro repositories.
- List only library packages the computer can install; walk libraries only
  for vendor installs, with RPATH inherited as ld.so does, normalized
  paths, and DT_NEEDED bounded while reading.
- Put --no-sandbox after the program an env wrapper launches; keep the
  gpgv folder's owner when its removal fails; respect --max-file-bytes for
  keys; wait 0.25 s on a busy SQLite database in data/get.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(desktop): keep the vendor spec's gpgv command on one line

A code span broken across lines left `<binary keyring>` for VitePress to
read as an unclosed HTML tag, so the docs build failed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* feat(desktop): add a validated task to exported desktop applications

Each capture can now yield one agentenv task in its exported bundle. An
agent writes the task (a prompt plus declarative checks over the app's
data/get views); REA validates it and stays model-free.

- `describe-desktop-application-task` summarises a capture's net change
  per file (role, format, views before and after) and drafts checks.
- Checks: file presence, lines present/absent/counted, JSON-pointer values,
  SQLite rows and counts. REA keeps a task only if a check fails on the
  seed and every check passes on the captured end state; a file that was
  not retained at the baseline counts as absent, as the computer starts.
- `export-desktop-application --task` writes tasks/NAME.json (deploy,
  agent, skills, recording, prompt, verifier), a compiled verify.py that
  reads data/get, and an eval. A boundary test runs the verifier under
  python3 and checks it agrees with REA's evaluator on every check kind.
- Docs, spec and skill guidance, with the solve-rate recipe.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* test(desktop): keep the seed test in its own block

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(desktop): record the task verifier driven in an agentenv computer

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(desktop): record the Typora task run in an agentenv computer

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): keep task checks bounded, complete and attributable

- line_count counts lines holding a substring (contains) instead of
  matching a regular expression, which could stall the verifier on
  agent-written text and read differently in Python.
- value_equals of a whole object or array is undecided on a truncated
  view.
- The draft keeps a database whose rows changed under an unchanged hash,
  and a file whose truncated views match while it changed.
- A task file that cannot be read keeps the reader's detail, and a failed
  cleanup keeps the read failure as its primary.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): leave a file with one version out of the task draft

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(desktop): update the task verification's test counts

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(desktop): record the task solve rate with Claude Code and Sonnet 5.5

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* feat(desktop): capture and export apps installed from a downloaded .deb

capture-desktop-application --package-file reads the file's control fields
with dpkg-deb and records its SHA-256, size and dependencies when it holds the
installed package; the file itself is never kept. The library walk now also
runs for local installs.

export-desktop-application --package-url declares agentenv's
Download(url, sha256) for that file, its dependencies and library packages as
apt packages, and a run script that installs it with dpkg.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): seed app-named dotfiles and respell UTF-16 seeds

The capture now treats dotfiles in the home named after the application
(.varicad-cfg.ini, .notesrc) as state roots, so a file the app only reads
during the capture is still seeded. The exported seeding respells the
captured home in UTF-16 text files with a byte-order mark, keeping the mark.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): hold readiness until the app's own window shows

An exported app is ready only once a window of its class that the taskbar
lists is shown, so a splash screen of the class, which skips the taskbar, no
longer counts as the app being up.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(desktop): record VariCAD captured, exported and run in an agentenv computer

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): keep exported views strict JSON for infinite and inexact numbers

The exported data/get spells infinite and negative-zero SQLite reals as REA's
SQLite reader does ("Infinity", "-Infinity", "-0"), and refuses a JSON file
holding a number that reading would change (1e999, integers past 2^53, -0,
rounded fractions) with the state view's own error, instead of a view with
an infinity in it.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): keep package-file facts tied to one file and URLs unechoed

- A --package-url refusal never repeats the URL, and credentials are checked
  before the scheme.
- The package file's identity must hold between dpkg-deb's read and the hash.
- A failed close keeps its cleanup owner even when the capture is cancelled.
- Readiness also ignores windows typed as splash screens.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): record database logs and keep state changes distinct

- A selected path that is not a directory brings its -wal and -journal as
  content roots, so the capture records whether they exist and retains them.
- The state view treats a file as absent only under recorded content roots
  and refuses a database whose log presence is unknown.
- JSON changes list containers too, so an array that becomes an object with
  the same members is a change.
- Key file entries escape the key as a JSON pointer segment, so [a/b] c and
  [a] b/c stay apart.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): keep watching through unread samples and late workers

- An unreadable window list is unknown, not zero windows, and is named in
  the limitations.
- Before ending the session, live run-token carriers no parent link reached
  are tracked; an unreadable process table leaves the exit unknown.
- An env prefix's assignments find the program and name its XDG folders.
- flatpak run options that take a value are skipped with it, -- is honoured
  and a /arch/branch suffix is dropped from the id.
- State directories include the entry's full id.
- Path options refuse empty values; roots count toward the entry limit.
- A capture file whose close failed stays owned and is retried on discard.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): bound apt sources, keep older captures and cleanup owners

- Sources files parse into one stanza per line or deb822 paragraph with its
  URIs and suites kept as lists, so no URI x suite product is built.
- Repository architectures and release are optional, so captures written
  before they were recorded parse; the export treats them as unrestricted
  and unknown.
- Library sources are matched against every index of a declared
  repository's URI, suite and components.
- Two failures side by side keep both causes and both cleanup owners, and a
  host file whose close fails reports a lease on its handle.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): match escaped key file entries and suggest containers through members

- The draft's key file lookup uses the state view's escaped `group/key`, so a
  key holding `/` or `~` still gets its suggestion.
- JSON and XML changes now list containers too; the draft suggests an added
  or removed container through its listed members, and a container only
  when none of its members is listed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): read relative PATH entries from the launch folder

A program, or an empty or relative PATH entry, is resolved from the folder the
program starts in, as the launch does, so `env PATH=. app` with the entry's
Path still finds the app.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* perf(desktop): compare JSON views on one shared path

JSON and XML changes are found by walking both trees with one shared pointer
path, building a pointer only for a listed value, so memory follows depth
rather than every container's full path. A pointer over 4096 characters is
counted but not listed. A 256-level document with 4000-character keys went
from about 251 MiB of heap to under 1 MiB.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): keep JSON Lines as text in the exported reader

The exported reader parses the whole text before an inexact number makes it
JSON with an error, so JSON Lines with an integer above 2^53 stay text as in
REA. Over-long integer spellings are unsafe without converting them. The
guide no longer claims large integers keep their value.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): keep the state-path help free of dash-led words

The CLI alias check reads `-wal` and `-journal` in help text as short flags,
so the help names the write-ahead log and rollback journal instead.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): never record or export apt source credentials

- Source URIs lose any user name and password as they are parsed, the way apt
  splits them, and so do index-target URIs and descriptions and the sources
  apt-cache policy lists, so matching works whether or not apt prints them.
- A repository whose entry held credentials is marked
  `uri_credentials_removed`, and the export refuses it, or an older capture
  whose URI still holds them, with a reason that names no URL.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): look programs up without normalizing their paths

Program paths and PATH entries are joined rather than resolved, so `..` after
a symbolic link means what it means to the launch. The lookup moves to its own
module.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): note inexact JSON numbers with a flag, not a list

The exported reader only needs to know whether any number was inexact, so it
keeps a boolean instead of every rejected spelling.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): refuse older captures that still hold apt credentials

A capture written before credentials were removed counted the credentialed
repository's libraries as installable, so the export refuses it whole and asks
for a new capture rather than declaring packages no source offers. Recorded
policy sources are redacted in export messages too.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* test(desktop): keep the credential fixture on an example host

A login on a real host reads as a secret to the commit scanner, so the older
capture's fixture uses apt.example.com like the other credential tests.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): tidy looked-up paths and search relative entries only for env

Empty and `.` segments are dropped from a looked-up program path, which keeps
`/snap/bin/` entries recognisable as snaps, while `..` is still left for the
kernel. Relative and empty PATH entries are searched only in the PATH an `env`
prefix sets, which travels with the exported launch; the host's own PATH does
not.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): keep excluded and unwalked database logs unknown

The capture records the paths every walk left out, and the state view treats
a log as absent only under content roots the baseline walked completely and
outside those exclusions, so an excluded or unvisited log leaves the database
unread rather than read without it.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): judge JSON numbers after duplicate keys settle

The exported reader marks an inexact number instead of flagging it during the
parse, then looks for marks in the parsed value, so a number a later duplicate
key overwrote no longer refuses the file, as in REA.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): redact a repository's policy source when it names it

An older capture whose policy source matched no sources entry kept that text
whole, and the export named the repository by it; the export now names it
without any user name or password.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): never signal the launch group on unknown membership

When the current read cannot observe a carrier's process group, the carrier
is signalled on its own instead of letting an older process-table row grant
a group-wide SIGTERM.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): look programs up in the launch's order and refuse host-relative finds

The lookup searches PATH as the launch does, an empty entry meaning the
launch folder, so inspection and launch name the same program. A program found
only through a relative or empty entry of the host's own PATH is refused,
since the exported computer would not have it; an env prefix's PATH travels
with the command.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): count unchecked paths and keep logs behind them unknown

Each walk counts the paths it could not stat and the directories on another
device it did not enter, and the checkpoint records it as `unchecked_paths`.
The state view treats a log as absent only when the baseline left nothing
unchecked, so a log whose stat failed stays unknown.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* perf(desktop): scan parsed JSON with one iterator per level

The check for marked numbers walks the parsed value with an iterator per
nesting level instead of copying each container's children; a million-entry
array went from about 15.7 MiB to 8.1 MiB of peak traced memory.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): start a relative program from its captured folder

An exported launch whose program path is relative, such as `./app` from an
entry's Path, now changes to the captured working directory first, as it
already did for relative arguments.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-11-05 15:14:04 +08:00
morlutoandCursor Agent 71f680d7c1 feat(mcp): page retained instruction references (#8594)
* feat(mcp): page retained instruction references

* test(mcp): verify oversized instruction reference recovery

* test(mcp): follow instruction reference continuations

* test(mcp): validate continuation source kind

---------

Co-authored-by: Cursor Agent <[email protected]>
2026-11-05 04:30:52 +08:00
morlutoandCursor Agent 1951cdc980 fix(mcp): expose bounded parameter type view (#8782)
* fix(mcp): expose bounded parameter type view

* fix(mcp): qualify value-flow view availability

---------

Co-authored-by: Cursor Agent <[email protected]>
2026-11-05 04:26:19 +08:00
morlutoandCursor Agent 9ef70696dd fix(ghidra): expose bit-field base type identity (#8602)
* fix(ghidra): expose bit-field base type identity

* test(ghidra): verify bit-field type handoff

* test(ghidra): verify big-endian bit-field handoff

* fix(ghidra): bind bit-field facts to snapshot profile

---------

Co-authored-by: Cursor Agent <[email protected]>
2026-11-05 04:03:43 +08:00
morlutoandCursor Agent f1819f5cb2 fix(test): load real Frida verifier address dependency (#8596)
Co-authored-by: Cursor Agent <[email protected]>
2026-11-05 04:02:09 +08:00
morlutoandCursor Agent 2b740cab36 fix(native): preserve function byte source qualifications (#8606)
* fix(native): preserve function byte source qualifications

* fix(native): recover oversized inline matching sources

* test(native): verify matching byte source recovery

---------

Co-authored-by: Cursor Agent <[email protected]>
2026-11-05 04:01:16 +08:00
morlutoandCursor Agent 4ec2726b1b fix(kuna): clarify failed export record availability (#8789)
* fix(kuna): clarify failed export record availability

* test(kuna): cover failed export cleanup retry

---------

Co-authored-by: Cursor Agent <[email protected]>
2026-11-05 04:00:22 +08:00
morlutoandCursor Agent 982e750a70 fix(ghidra): bind existing MIPS projects to target metadata (#8605)
Co-authored-by: Cursor Agent <[email protected]>
2026-11-05 03:35:21 +08:00
morlutoandCursor Agent 4afb6b79ae fix(mcp): guide retained evidence input recovery (#8761)
* fix(mcp): guide retained evidence input recovery

* style(mcp): format catalog and server types

* test(mcp): verify HTTP capacity recovery guidance

* test(mcp): model catalog recorder imports in cleanup fixtures

record-mcp-catalog.mjs loads tool contracts and the Evidence-input
contract before it registers cleanup owners. The cleanup fixture still
sentineled only the previous imports, so those loads failed before any
owner wrote cleanup.jsonl. Track the script's read() calls and replace
the new modules with the empty contract list this fault injection uses.

---------

Co-authored-by: Cursor Agent <[email protected]>
2026-11-05 03:34:26 +08:00
morlutoandCursor Agent 93295c3df3 fix(mcp): report selection after failed target opens (#8726)
* fix(mcp): report selection after failed target opens

* fix(mcp): retain resource root selection in open failures

* test(session): assert selection during cleanup failures

* test(mcp): delay failed open response across target switch

* test(session): distinguish no selection from pending cleanup

* test(session): assert selections during cleanup and cancellation

* test(mcp): join the failed-open gate before restoring its spy

The snapshot-switch gate waited only on its acknowledgement, so a request
that settled early hung until the runner timeout, and a later assertion
failure restored the spy while that request was still in flight. Race the
acknowledgement and join the launched open in finally before mockRestore.

---------

Co-authored-by: Cursor Agent <[email protected]>
2026-11-05 03:34:03 +08:00
morlutoandCursor Agent f1622fc8b4 feat(native): page retained feature traces (#8585)
* feat(native): page retained feature traces

* fix(snapshot): retain linked source evidence for replay

* test(snapshot): verify linked foreign evidence stays scoped

* docs(mcp): clarify imported residual questions

* docs(mcp): guide trace reference classification

* fix(cli): report result sources when Evidence publication fails

A trace keeps linked sources on the result. Use those records for the
undelivered source closure as well as the bundle, so a refused output
does not report them as not captured.

* ci: rerun pull request checks

The merge of origin/main did not start the CI workflow for this head.

---------

Co-authored-by: Cursor Agent <[email protected]>
2026-11-05 03:26:31 +08:00
morlutoandCursor Agent 1ff5a655bc fix(native)!: preserve value-flow absence semantics (#8785)
* fix(native)!: preserve value-flow absence semantics

* test(native): compare value-flow reasons with their owners

Stop freezing the unrecorded and Kuna unavailable sentences. Assert the
procedure-scoped unknown and that an explicit provider reason is preserved.

---------

Co-authored-by: Cursor Agent <[email protected]>
2026-11-05 02:32:15 +08:00
morlutoandCursor Agent dd4b83c220 fix(ghidra)!: search adjacent initialized memory blocks (#8840)
* fix(ghidra)!: search adjacent initialized memory blocks

BREAKING CHANGE: Ghidra analysis snapshots from earlier byte-search policies are refused; reopen the target and create a new snapshot. Historical Evidence remains importable.

* test(ghidra): supervise cross-block fixture process

* docs(ghidra): clarify contiguous search range

* test(ghidra): verify search metadata and cleanup

---------

Co-authored-by: Cursor Agent <[email protected]>
2026-11-05 02:31:32 +08:00
morluto d896ab7bd7 fix(mcp)!: distinguish unavailable batch pseudocode (#8737)
* fix(mcp)!: distinguish unavailable batch pseudocode

* test(mcp): preserve historical batch error details

* test(mcp): retain unavailable items in batch recovery

* test: update Ghidra batch unavailable expectation
2026-11-05 01:51:38 +08:00
morluto 30228c9a5f fix!: guide snapshot inputs to the restore workflow (#8867)
BREAKING CHANGE: Snapshot-shaped bundle import failures use invalid_request rather than evidence_integrity_mismatch.
2026-11-05 01:50:53 +08:00
morluto 6df2ac1d7e fix(evidence): report partial workflow question publication (#8848)
* fix(evidence): report partial workflow question publication

* fix: project nested recovery for caller lifetime
2026-11-05 01:49:36 +08:00
morluto b97023809f feat(native): retain decompile export source facts (#8805)
* feat(native): retain decompile export source facts

* test(native): verify historical export source facts

* test(native): retain source after export conflict
2026-11-05 01:48:53 +08:00
morluto 5a1f75461a fix(mcp): guide address context recovery (#8766)
* fix(mcp): guide function comment recovery from address context

* fix(mcp): retain specific comment refusal recovery

* docs(mcp): advertise paged function comments

* fix(mcp): preserve address facet recovery

* test(mcp): verify session-specific facet recovery

* test(mcp): extract address recovery fixture

* fix(mcp): direct provider recovery to complete status

* fix(mcp): qualify address recovery across providers

* fix(mcp): make coordinate recheck actionable

* test(mcp): correct address recovery fixtures

* test(ghidra): verify address comment recovery
2026-11-05 01:48:08 +08:00
morluto 5b82a253dc fix(evidence): link snapshot replays to original observations (#8734)
* fix(evidence): link snapshot replays to original observations

* style(tests): format snapshot replay assertions

* test(evidence): cover replay parent metadata refresh

* test(evidence): surface snapshot replay failures

* test(mcp): check replay source after cleanup recovery
2026-11-05 01:47:24 +08:00
morluto 1a9aa1296b fix(kuna): recover overflow when project can answer (#8747)
* fix(kuna): recover overflow when project can answer

* fix(kuna): provide actionable CLI overflow recovery

* test(kuna): cover shared overflow cleanup

* test(kuna): cover cancellation during overflow cleanup

* fix(kuna): make overflow recovery usable after handoff
2026-11-05 01:46:30 +08:00
morluto 9196c30f6e fix(ghidra): guide packed program import recovery (#8820)
* fix(ghidra): guide packed program import recovery

* test(ghidra): verify packed import recovery facts
2026-11-05 01:45:22 +08:00
morluto fdfc210879 fix(native): guide unreadable journal recovery (#8809) 2026-11-05 01:44:34 +08:00
morluto eac28b2047 fix(xrefs): guide symbol lookup recovery (#8753)
* fix(xrefs): guide ambiguous name selection

* fix(xrefs): guide missing symbol lookup

* fix(xrefs): name available symbol recovery paths

* fix(xrefs): qualify exported address coordinates

* fix(xrefs): point to procedure address lookup

* fix(xrefs): keep CLI recovery focused

* test(xrefs): cover CLI ambiguity recovery
2026-11-05 01:43:45 +08:00
morluto 9a79ee8e9e fix(kuna): clarify single-function fallback evidence (#8742)
* fix(kuna): describe fallback evidence without guessing project state

* fix(kuna): qualify export capacity guidance by source
2026-11-05 01:43:00 +08:00
morluto 51e9027d58 fix(session): preserve availability recovery guidance (#8764) 2026-11-05 01:42:16 +08:00
morluto 78d7bf3ea0 docs(native): clarify memory and file byte sources (#8829)
* docs(native): clarify memory and file byte sources

* docs(native): qualify Ghidra fixup coverage
2026-11-05 01:41:39 +08:00
morluto 9031adf0b0 fix(ci): own source-map captures and honor browser startup budget 2026-11-05 01:38:33 +08:00
RRFZ a1660bc016 fix(pe): reject certificates whose signature algorithms disagree (#8922)
* fix(pe): reject certificates whose signature algorithms disagree

* test(pe): keep the signature algorithm case apart from the version case
2026-11-05 01:18:40 +08:00
Entrpiandmorluto 0a99c2a580 fix(artifacts): give Godot PCK and Node SEA readers the ESP member lifecycle (#8395)
* feat(artifacts): inventory and extract ESP32 firmware images

ESP32-family app and bootloader images (magic 0xE9) had no reader, so
open_binary refused OTA downloads, app partitions and bootloaders.

Add the esp-image container format behind inspect_artifact and
extract_artifact. A pure decoder reads each byte once and keeps only
fixed-size fields. Derived facts cover the chip, from a table cited to
ESP-IDF v6.1; segment classification as ESP-IDF's should_map and
should_load; checksum and appended SHA-256 results; app and bootloader
descriptors; Secure Boot v2 signature blocks, observed and unverified;
and undescribed trailing byte ranges. Truncation and undescribed bytes
make coverage unknown. ESP8266 images are refused with a specific
reason, and ciphertext stays unrecognized.

Add the format to every target, Evidence, snapshot, live-summary and
artifact vocabulary, document it, and add an optional verify:esp-images
lane that compares esptool-built images with esptool image-info and
espsecure signature-info-v2.

Refs #3615

* test(artifacts): run the ESP image lane on esptool v5.6.0

The lane compared descriptor fields only when esptool image-info printed
one, so a descriptor that REA reports and esptool does not print passed
without comparison. Compare the descriptor kind in both directions.

Record that the lane passed for all 13 images with esptool and espsecure
v5.6.0, the version the layout facts cite, as well as with 4.12.0.

* refactor(artifacts): define ESP image layout sizes once

The decoder and the derived facts each defined the segment-header,
appended-digest and flash-sector sizes, the erased-byte scan and the
offset formatter. Define them once with the other fixed-layout structures
and import them in both places. The signature slot count is the one
constant that drives the slot scan.

Correct the observedEnd comment: it is the end of the last chunk read.

* fix(artifacts): report ESP-IDF's segment length limit by chip

ESP-IDF's verify_segment_header rejects a segment of 16 MiB or more
unless the chip maps flash through 32-bit cache addresses
(SOC_SPI_MEM_SUPPORT_CACHE_32BIT_ADDR_MAP: ESP32-S3, P4, C5, C61, H4 and
S31 in v6.1). Record that capability in the chip table and add a
limitation for such segments; unknown chips get none.

`loaded` repeats should_load. It does not mean the bootloader accepts the
segment: verify_load_addresses still rejects one outside the chip's RAM,
which REA does not model. Say so in the docs, the skill reference and the
inventory limitation.

* test(artifacts): check ESP lane tools first and verify extraction

The lane started work before checking its tools, so a missing RISC-V
target, ld.lld, esptool or espsecure surfaced as a raw spawn or command
error. Check each one before building and name the variable to set. Warn
when esptool is not v5.6.0, the release the layout facts cite.

Assert each segment's class for the ESP32-C3 app, its variants and the
bootloader, as the Xtensa image already did. Extract the app and the
signed image through the CLI and compare every file with the bytes at
esptool's segment offsets or in the appended signature sector; the
source's SHA-256 must be unchanged and equal the reported root_sha256.

* docs(artifacts): state what the ESP image lane does not establish

The Verification section listed the lane's checks but not its gaps: only
ESP32-C3 and ESP32 images come from a real producer, no real project
build, OTA download or flash dump is inspected, no signature is verified,
and the lane does not run in CI.

Record the timing of the final lane run on esptool v5.6.0.

* fix(artifacts): compare ESP segment declarations

Segment entries hash only their data, so compare_artifacts reported a
segment that moved to another load address with the same bytes as
unchanged; only the image itself changed. Compare each segment's
load_addr and data_len as its declaration, as Node SEA and PyInstaller
members are compared, and count a changed declaration as a metadata
change, also when content is unavailable.

Header offsets are layout and the classification is derived, so neither
is compared.

* test(artifacts): run ESP lane commands as owned process trees

The lane ran its tools through execFile. A timeout killed only the direct
child, so a linker or assembler that Clang or GCC had started could keep
running while the lane removed its workspace. Run every command through
createVerifierCommands, check the tools inside withVerifierCleanup, and
remove the workspace only after commands.close() has stopped every tree;
a failed close keeps the workspace.

Only a nonzero exit now counts as a refusal or an expected esptool
failure; timeouts and spawn failures propagate. Supervising each command
roughly doubles the lane's run time.

* test(artifacts): cancel ESP decoding after its first decoded chunk

The cancellation test requested cancel or close on setImmediate, which
can land while the file is still being opened and checked, so it never
showed that decoding had started. It also passed against a reader that
ignored both requests while decoding. Acknowledge the decoder's first
chunk, then cancel or close, join the operation, and assert that no
later chunk reaches the decoder and that the reader ends closed.

* docs(artifacts): record which ESP metadata comparison ignores

Segments are compared by their declaration. The image's and the
signature blocks' format_metadata are not: every field comes from their
own bytes, which content comparison already covers, and a block's
image_digest_matches follows the image's content.

* feat(artifacts): inventory and extract ESP flash layouts

A whole-flash dump of an ESP32-family chip was read as its bootloader
image, with the partition table, apps and data left as undescribed bytes,
or refused when it began with erased flash. Read it as an esp-flash-image
container instead.

The partition table is decoded as esp_partition_table_verify accepts it
and is found at the caller's partition_table_offset, at the default
0x8000, or by scanning 0x1000-aligned sectors for a table whose MD5
verifies, which the inventory reports as derived. The bootloader is
located at its chip's ROM bootloader offset. Each partition is an entry
with its declaration in flat format_metadata; the bootloader and app
partitions carry ESP image facts from the existing streaming decoder; the
otadata records are reported with a selection derived without rollback or
anti-rollback, and the running partition stays unknown. One unreadable
partition does not hide the others, and extraction writes every partition,
nvs_keys included, as ordinary data.

Flash layouts are detected before single ESP images. The scan runs only
when an ESP image sits at a ROM bootloader offset, and is refused when a
bootloader at another chip's offset shows that the dump does not start at
flash address 0. open_binary gains flash_offset and partition_table_offset,
which require format esp-flash-image and are part of the snapshot target
identity; the CLI takes them with --target-format esp-flash-image.
Comparison treats a partition's label, type, subtype, range and flags as
its declaration.

* test(artifacts): verify ESP flash layouts with gen_esp32part, merge-bin and otatool

Add the optional verify:esp-flash lane. It builds default and relocated
partition tables with ESP-IDF's gen_esp32part.py, assembles dumps with
esptool merge-bin, including one written from flash 0x1000, and writes
OTA records with otatool through flash_file_tool.py, which replaces
parttool's esptool calls with reads and writes of the dump file.

It compares REA's bootloader and app facts with esptool image-info, every
partition with gen_esp32part.py's decoding of the extracted table, and the
OTA records and derived slot with otatool read_otadata after each switch.
It also checks extracted bytes and source integrity, the scan and caller
table offsets, flash_offset on the 0x1000 dump, a corrupted app beside
valid partitions and a truncated dump. ESP32 linker scripts place the
conformance bootloader and app in ESP32 address ranges.

* fix(artifacts): read ESP flash layouts as the bootloader does

Read the partition table as bootloader_utility_load_partition_table
does: the last factory, otadata and same-subtype OTA declaration wins,
and a limitation names repeats. Plan each otadata record within its
partition and the dump. An otadata partition smaller than two sectors
selects none, the bootloader's INVALID_INDEX; a record the dump lacks
leaves the selection unknown while the other record stays reported. The
fallbacks follow the bootloader's indexes: ota_0-fallback and
factory-fallback name no partition when the table declares none, and the
assumptions add GPIO holds and deep-sleep wake.

Refuse a dump whose bootloader contradicts its origin before the default
table offset is tried, whether flash_offset was omitted or given, and keep
the ESP image reader's ESP8266 refusal. A table never lies inside the
located bootloader image, so single app images are no longer scanned. An
explicit esp-flash-image selection is decided before other container
signatures, and the native commands' --target-format no longer takes it.
Say when no bootloader confirms the default origin.

Exempt the primary bootloader and partition_table entries of ESP-IDF 5.3
from placement limits, report every region inside a longer one, and name
partitions by index when labels repeat ignoring case. Keep an undecodable
label's bytes in label_hex and compare them. Word undescribed ranges as
starting at a byte that is not erased, and treat a short erased region as
erased.

Add an MCP compare_artifacts test for partition declarations, reader
identity and close tests, scan edge cases, the 0x2000 ROM offset and a
literal otatool CRC.

* test(artifacts): verify ESP primary layout entries and the otadata fallback

Build a table with the primary bootloader and partition_table entries
that gen_esp32part.py fills from --primary-bootloader-offset, and check
that REA reads it with complete coverage, no overlap or misplacement
limitation, and a table that agrees with gen_esp32part's decoding. Erase
the otadata with otatool after the slot switches and check the records
against read_otadata and the factory fallback.

* refactor(artifacts): share positional entry lifecycle

The ESP image and ESP flash-layout readers carried the same 138 lines of
lifecycle code. PositionalArtifactReader now owns it: one preparation
under the reader's scope, a carrier recheck before each traversal,
refusal of forged entries, range streams that check their length and
recheck the carrier when they end, and a close that retires open
streams before it releases the carrier.

Each reader keeps its location, decoding, entries and metadata, and
returns its entries and coverage from prepareEntries(). Entries carry a
required byte range, which removes the unreachable null-range checks in
open(). Failure reasons, messages, order and cancellation do not change.

The Godot PCK reader keeps its own copy. It reports a member that ends
early as a format failure, where the ESP readers report integrity, and
the helper cannot express that without an option. The Node SEA reader
has the same difference.

* fix(artifacts): report Godot PCK and Node SEA members that end early as integrity

Behavior change: a Godot PCK or Node SEA member stream that delivers
fewer bytes than its entry declares now fails with reason "integrity"
and the message "<label> entry ended early: <path>". Before, it failed
with reason "format" and "<label> is malformed: member ended early:
<path>". Callers see the category integrity_mismatch instead of
execution_failure. The remediation text does not change.

Only a carrier that changes after open() rechecks its identity reaches
this path. The directory was valid when the inventory was taken, so a
short member means that the carrier changed, not that it is malformed.
The identity recheck that runs next also reports integrity, and the ESP
and ASAR readers already report a short member as integrity.

The new tests end the admitted descriptor's reads inside a member after
open() and check the reason and message for each reader.

* refactor(artifacts): move Godot PCK and Node SEA onto the positional lifecycle

The Godot PCK and Node SEA readers carried their own copies of the
positional entry lifecycle. They now extend PositionalArtifactReader and
keep only their location, decoding, entries and metadata.
prepareEntries() returns the entries and the coverage that each reader
reported before. Godot reports unknown coverage with one limitation for
an encrypted or sparse-bundle directory. Node SEA reports complete
coverage with its fuse contradiction as a limitation.

The base class takes two rules from these readers:

- open() refuses an encrypted entry as unavailable, after the
  forged-entry check and before the carrier recheck: "Encrypted <label>
  member is unsupported: <path>". ESP entries are never encrypted.
- Traversal yields to the event loop every 256 entries, starting before
  the first entry. The ESP readers gain one yield before their first
  entry. This changes scheduling only.

Failure reasons, messages, order and cancellation do not change. A
member that ends early has failed as integrity since the previous
commit. Godot's directory parse still checks only cancellation, so close
does not stop it early. Node SEA's close still drops an admitted decode
that no completed preparation released. Each reader now stores its
entries only after preparation succeeds; a failed preparation never
served them before either. PositionalArtifactEntry requires a byte
range, so the unreachable null-range checks in open() are gone.

* fix(artifacts): keep ESP flash coverage complete for a dump cut short

An esptool merge-bin dump ends after the last part it wrote, usually inside
its last partition. Such a partition made the layout's coverage unknown, so
artifact comparison of two merge-bin dumps reported every unchanged
partition as unknown.

The partition table names every partition whatever the file's length, so a
partition cut short or left out by the end of the file hides no entry. As
the ESP image reader does for a cut-short segment, its entry holds the bytes
the file has and an entry limitation gives the declared size; a partition
with no bytes stays in partitions_outside_dump. Coverage stays complete
unless undescribed bytes remain or no bootloader is located. An MCP
compare_artifacts test of two dumps that end inside ota_0 now classifies
every partition, and fails on the old rule.

* fix(artifacts): report written bytes in ESP partitions, erased slots included

An app slot is erased for ESP-IDF's bootloader when its image header bytes
are erased flash, and REA reported only that status. A slot can still hold
data further in: a dump read from a device held a complete older app image
1 MiB into an OTA slot reported as erased, with no limitation.

Each partition entry now records where the bytes the dump holds are
written, relative to the partition: written_offset, the first byte that is
not 0xFF, written_end, just past the last, and written_bytes, how many are
not 0xFF. They are null for a partition outside the dump. The status stays
erased, which is the bootloader's view, and a limitation names an erased
app slot that holds written bytes and says they are not read as an image.
The reader reads every partition byte once more to find them.

* fix(artifacts): derive the ESP boot selection for every partition table

The boot selection lived inside otadata, so a table without an otadata
partition, the common factory-only layout, had none. ESP-IDF's
bootloader_utility_get_selected_boot_partition returns the factory index
when the table declares no otadata partition.

Move it to a top-level boot_selection that every layout has. Its basis
names the bootloader's branch: otadata, factory-fallback and ota_0-fallback
from the records, no-otadata for a table without otadata, none for the
bootloader's INVALID_INDEX, and unknown when the dump lacks a record the
bootloader reads, which replaces the null selection. running_partition
moves with it, and otadata keeps its partition index and records. The
assumptions name what each branch leaves out: no records assumption
without otadata, and none for none or unknown. The real-tool lane checks
no-otadata on a table gen_esp32part built without otadata.

* fix(artifacts): say when an ESP image is a flash dump read from 0x1000

An ESP32 dump read from flash 0x1000, as read-flash 0x1000 or merge-bin
--target-offset 0x1000 writes it, opened automatically as its bootloader
image with a megabyte of undescribed bytes and no next probe. The flash
reader refuses that origin, but nothing told an agent which flash_offset to
pass.

espFlashDumpOrigin owns the inference: an image of a chip whose ROM loads
the bootloader from flash R, at file offset F, is the bootloader of a dump
read from R - F when a partition table sits where that origin puts 0x8000.
The ESP image reader asks it for a non-app image at file offset 0, so the
result does not depend on how the reader was created, and records
flash_dump {flash_offset, table_file_offset}, null otherwise, with a
limitation that says to reopen the file as esp-flash-image with that
flash_offset. inspect_artifact proposes the same open_binary call as a next
probe, and the flash reader's misplaced-origin refusal names the offset.

* perf(artifacts): summarize nested ESP images in the flash layout summary view

The summary view returned an ESP flash layout's root facts whole, so each
app partition and the bootloader carried every segment and signature
block, about half of a dump's summary.

The summary view now gives each nested image a summary instead of its
facts: status, chip, entry point, segment count, load ranges (each mapped,
loaded or unclassified segment's address and declared length), the
checksum and appended SHA-256 statuses, coverage and limitations. Complete
detail keeps the full facts, every segment included, and the retained
Evidence is unchanged. On a 16 MiB device dump the summary's root falls
from 5,141 to 2,657 bytes, and the summary Evidence from 10,038 to 7,554.

* test(artifacts): expect the misplaced image the ESP bootloader search reports

8a748d3cc added misplacedImage to the bootloader search, which the
locator uses to name the origin a misplaced bootloader implies, but one
test still compared the search result without it. Expect the image's
chip and file offset, and no image for an unknown chip.

* fix(artifacts): give every ESP partition from the dump's start an entry

After ecbc1a772 kept coverage complete for a dump cut short, a partition
wholly past the end of the file still had no entry, only a
partitions_outside_dump row. Comparing a merge-bin dump with a full device
read then reported that partition as added or removed, as if one table
did not declare it.

Every partition from the dump's first byte on is now an entry holding the
bytes the file has: none for one past the end, with a limitation saying
the file holds none of its bytes, no image facts and null written fields.
partitions_outside_dump keeps only partitions that begin before the dump's
first byte, and coverage is unknown when one does, since it has no entry.
Two dumps that lack the same partition now compare it as unchanged, and a
dump against a longer one reports the partition as changed; an MCP test
of a merge-bin dump against a full one checks that no path is added or
removed.

Unlike the ESP image reader, which marks a truncated image's coverage
unknown because later segments and signature blocks may be hidden, the
flash reader can list every partition from the table whatever the file's
length; only the entry limitation follows the image reader.

* fix(artifacts): select no ESP app from a partition table the bootloader rejects

boot_selection followed bootloader_utility_get_selected_boot_partition but
not the table check before it. For a table whose MD5 does not match, or
that has a second MD5 entry, esp_partition_table_verify fails, the
bootloader selects no app and resets. REA still derived a slot from the
otadata records, although the table's own limitation said the bootloader
rejects it.

The selection now has basis none, with no assumptions, for a table the
decoder marks invalid. It also follows two more branches of the
bootloader: an otadata partition at offset 0, which the bootloader takes
for none, selects the factory index as no-otadata, and a table without an
OTA app selects its fallback even when the dump lacks a record, since the
bootloader then ignores the records.

The bootloader also rejects a table whose partitions end past the flash
size in its header. REA does not model that check; the format guide says
so.

* fix(artifacts): require the ESP dump origin's table to follow the image

espFlashDumpOrigin named a later flash origin whenever a partition table
decoded where that origin puts 0x8000. Bytes inside the image's own
segments could be read that way, so an ESP32 bootloader whose segment data
held table-shaped bytes at file offset 0x7000 was reported as a dump read
from 0x1000.

The table must now start at or after the image's end, the rule the locator
already applies to the default table. The ESP image reader passes the
image_end it decoded, or the file size for a truncated image; the flash
reader's refusal passes the end of the misplaced image's segments. The
bootloader search drops its misplaced flag, which only restated
misplacedImage, and the next probe formats offsets with offsetText.

Reader tests cover the 0x1000 and 0x2000 ROM offsets, a standalone
bootloader, bytes that begin like a table, an app with a table at 0x7000
and a table inside a segment; without the boundary check, three of them
fail.

* fix(artifacts): give the ESP flash summary its own kind and each image's identity

The summary view's ESP flash root kept kind esp-flash-image, so a consumer
bound to kind could not tell an image's summary from its full facts, and
the advertised schema held two objects with the same kind const. It is now
esp-flash-image-summary.

The per-image summary also dropped the descriptor, which is what tells two
app slots apart, and named each image only by its partition index, which
the summary view, having no entry rows, could not resolve. Each nested
image now carries its entry path from the layout plan, in both views. The
summary adds the descriptor kind with its identifying fields (an app's
project_name, version, idf_ver, secure_version and app_elf_sha256; a
bootloader's version, idf_ver and secure_version), the signature block
count and each load range's classification. Its enums and fields derive
from the schemas that own them.

An MCP test reads a dump with an app in ota_0 and an erased ota_1 that
holds older bytes: it checks both summaries, that the summary's inventory
Evidence is the one the complete view returns with full facts, and the
erased slot's written range.

* test(artifacts): cover cancel and close during the ESP written-bytes scan

The written-bytes pass reads every byte of each partition the dump holds,
checking that the reader is open before each chunk, but only app decoding
had a cancellation and close test. A data partition, which only that pass
reads, now holds 9 MiB; once the scan has examined its first chunk, a
cancel rejects preparation with the caller's reason and a close with
unavailable, and the scan examines no further chunk.

The erased-slot test now asserts the whole set of erased-slot limitations,
so factory, ota_0 and nvs, which the bootloader reads or which are not app
slots, are shown to have none.

* docs(artifacts): re-time the ESP flash lane and state only observed causes

The lane ran again after its assertions changed; both esptool runs passed,
and the timed run's figures replace the earlier ones. The erased-slot
paragraph no longer names a cause for the older bytes, which REA cannot
observe, and the skill reference states that every table has a
boot_selection in its own sentence, with the none basis for a table the
bootloader rejects.

* test(artifacts): open an ESP partition past the end through the positional reader

A partition that lies past the end of a dump is an entry with no bytes,
and the merge with the positional lifecycle gives it the empty range at
the end of the file. The inventory tests listed that entry but never
opened it. Opening it through the shared reader now yields no bytes, and
its range is the file's size with a declared size of 0.

* fix(artifacts): retire ESP flash members as the ESP image reader does

#7498's maintainer commits made the ESP image reader keep a member owned
until its consumer closes it, retire fully buffered bytes when
cancellation arrives after production, and refuse to finish an inventory
paused across close. The ESP flash reader was copied from the earlier
reader. Main's shared stored-member lifecycle cases failed two of eight
against it: a paused inventory completed after close, and buffered member
bytes stayed readable after cancellation.

Give the flash reader the same lifecycle and run the shared cases against
it.

* docs(artifacts): re-time the ESP flash lane after merging main

The lane passed again with esptool v5.6.0 and 4.12.0 on the merge of upstream main. Record the v5.6.0 run's time and peak resident set.

* fix(artifacts): read ESP image Evidence recorded before flash_dump

The flash layout work made `flash_dump` a required field of the
`esp-image` root metadata. Evidence recorded before it has no such
field, so newer REA rejected records that were valid when they were
made: `compare_artifacts` and `inspect_analysis_view` reported
`evidence_integrity_mismatch` for them.

The field is now optional when read. A record without it parses as it
is, so the graph digest it commits to still matches; nothing fills the
field in. The image reader still writes it for every image, and the
inspection probe reads a missing field as no hint.

A regression test recommits an image inventory without the field and
passes it inline to `compare_artifacts` and `inspect_analysis_view`.
Against a server built with the required field, `compare_artifacts`
rejects it.

* fix(artifacts): stop the ESP flash gap scan when the reader closes

The scan for the first written byte in each undescribed gap checked
only the caller's signal. `close()` marks the reader closed and waits
for preparation, so it waited while the rest of a long erased gap was
read.

The gap scan now takes the reader's open check, as the written-bytes
scan and the image decoder do, and checks it before each chunk. Tests
cancel or close the reader after the scan's first chunk of a 9 MiB
erased gap and confirm that no later chunk is examined.

* fix(artifacts): name ESP partitions with Windows device labels by index

The safe-label rule accepted labels such as `NUL`, `con` or `COM1`, so
extraction wrote `partitions/NUL.bin`. Windows reserves those names
before any extension, and ordinary Windows tools cannot use such a
file.

A label that is a Windows device name (`CON`, `PRN`, `AUX`, `NUL`,
`COM0`-`COM9` and `LPT0`-`LPT9`, any case, alone or before a dot) now
takes the existing index fallback. The partition metadata and the
entry's source path keep the label.

* fix(artifacts): read ESP flash partitions' written bytes in one pass

Each partition got its own scan of the bytes the dump holds, so
partitions declared over the same bytes read them again and again.
Overlaps only add limitations, so a crafted table reached this normally:
94 overlapping partitions over a 32 MiB dump would examine about 3 GiB
before the first entry.

The reader now reads the bytes the partitions cover once. Partition
boundaries cut them into segments; one pass counts each segment's
written bytes and bounds, and each partition's extent is the sum of the
segments inside it. Every partition keeps its own declaration, extent
and limitations. The pass checks the reader's open state before each
chunk, as the per-partition scan did.

A test declares 60 partitions over one MiB, 40 over all of it and 20
over its first half, and checks each partition's extent and the
overlap limitations. The reader reads 1,113,256 bytes of the
1,114,112-byte dump; one scan per partition read 52,493,480.

* test(artifacts): fail the ESP lanes on failed commands and refusals

`otatool` turned a nonzero exit into returned text, which its callers
discarded, and `inspect()` returned `{ refused }`, which the flash lane
never checked. A failed command then surfaced later as a slot
assertion, an output-parser error or a TypeError, hiding its cause.

Commands the lanes need to succeed now reject with the output they
exited with: `otatool` and `inspect()` do so through a shared
`failedCommand`. An expected refusal is opt-in: `refusal()` returns
REA's output and fails the lane when REA opens the file. The ESP image
lane's two ESP8266 cases use it.

Checked by calling `otatool` on a file without a table, `inspect()` on a
file REA refuses and `refusal()` on a file REA opens; each rejects with
its cause.

* fix(artifacts): require a partition entry in an ESP partition table

The decoder counted the slot, not the partitions, so a lone MD5 entry
over the empty prefix, followed by the terminator, decoded as a table
with no partitions. Default and caller-selected detection then accepted
that empty layout, although the guide requires one partition.

A table now needs one partition entry before its terminator. An MD5
entry alone is refused as `entry 1 at 0x20 ends the table before any
partition entry`. The cases cover the decoder and both automatic and
caller-selected location.

* fix(artifacts): decode an image shared by ESP app partitions once

Each app partition got its own decode, so app partitions declared over
the same bytes decoded and hashed the same image again and again, the
same pattern the written-bytes scan had. With 60 app partitions over
one 960 KiB image the reader read 64,029,736 bytes of a 1,114,112-byte
dump.

Regions with the same file range now share one decode; each keeps its
own copy of the facts. Partitions with different ranges still decode
separately, because truncation can change the facts: at most one decode
per partition the table holds, each within its partition.

A test declares 60 app partitions over one image, 40 holding all of it
and 20 cutting it short, and expects three decodes: the bootloader, the
whole image and the cut one. The 60-partition dump now reads
2,161,864 bytes, as a dump with one app partition does.

* test(artifacts): run the shared member lifecycle cases on Godot PCK and Node SEA

The Godot PCK and Node SEA readers now extend PositionalArtifactReader,
whose member lifecycle comes from #7498's ESP reader. Main's shared
stored-member cases ran only for the ESP and uImage readers. Run them for
these two readers as well.

On main `6e9ee97a6`, both readers fail two of the eight cases: a paused
inventory finishes after `close()`, and member bytes buffered before
cancellation stay readable. On the positional base, all eight pass.

---------

Co-authored-by: morluto <[email protected]>
2026-11-05 00:54:53 +08:00
5990ff0324 feat(artifacts): read WeChat mini-program wxapkg containers (#8148)
* feat(artifacts): read WeChat mini-program wxapkg containers

Add a read-only reader for plaintext wxapkg packages: the 0xBE/0xED
18-byte big-endian header, the bounded name/offset/size member table,
and verbatim member bytes at absolute carrier offsets.

- Members retain declared names, absolute offsets and sizes, so
  source_location reports real carrier positions.
- V1MMWX PC-client packages are detected as encrypted coverage with
  null member facts; extraction refuses with a typed error and no
  decryption is attempted.
- Bounded table and directory budgets are admitted before allocation;
  malformed marks, truncated tables, invalid UTF-8 names, member
  extents inside the header or beyond the file, and counts that cannot
  fit their table are typed format or limit failures.
- Member reads stream in bounded 64 KiB chunks with carrier freshness
  checks; the reader joins outstanding streams before close and bounds
  active streams and repeated member I/O.

Refs #8121.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* fix(artifacts): bound wxapkg members to the declared data region

Normalize the two indexLength conventions before the table bounds check
and the table read. Member ranges must then lie in that declared data
region, which has to fit in the file.

Record wxapkg format metadata as not compared: header facts repeat the
package bytes, and entry source paths keep a producer root prefix.

* fix(artifacts): choose the wxapkg index after member ranges fit

Four trailing bytes made the rows-only layout end on the file and dropped
the count-inclusive layout. Keep every admitted layout until the parsed
rows and member ranges select one.

---------

Co-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Cursor Agent <[email protected]>
2026-11-05 00:52:20 +08:00
morlutoandCursor Agent 63b66fa498 fix(search): clarify regex diagnostics and recovery (#8776)
* fix(ghidra): identify invalid regex patterns

* docs(native): clarify regex dialects in search contracts

* fix(ghidra): guide regex syntax recovery

* fix(search): guide regex worker recovery

* fix(search): preserve regex parser diagnostics

* test(search): update regex boundary expectations

* refactor(search): remove unused regex error hook

---------

Co-authored-by: Cursor Agent <[email protected]>
2026-11-05 00:20:01 +08:00
morlutoandCursor Agent 52bca6de41 fix(native): retain value trace source evidence (#8384)
* fix(native): retain value trace source evidence

* fix(native): build trace error evidence only on failure

* test(mcp): expect unowned value trace refusal

* test(mcp): avoid freezing trace error wording

* test(native): keep value-trace stop coverage with source evidence

Main's stop and continuation cases now meet the trace's evidence-owner
precondition: direct calls supply a source id, and a completed MCP trace
runs in a session that can retain it.

---------

Co-authored-by: Cursor Agent <[email protected]>
2026-11-05 00:19:50 +08:00
ShuaixinHuang 25b62db838 test(firmware): cover S-record high address workflows (#8897) 2026-11-05 00:07:29 +08:00
김준호 30a11af923 fix(browser)!: keep the Chromium sandbox for REA-launched browsers (#8738)
* fix(browser)!: keep the Chromium sandbox for REA-launched browsers

Playwright appends --no-sandbox unless a launch passes
chromiumSandbox: true. Neither the browser scenario launch nor the native
module resolver passed it, so every REA-launched Chromium ran without its
sandbox and REA_BROWSER_NO_SANDBOX had no effect.

Both launch sites now take the sandbox decision from ChromiumSandbox.ts:
off on Android or when REA_BROWSER_NO_SANDBOX=true, on otherwise. When
Chromium reports that it cannot start its sandbox, the launch failure's
remediation names the opt-out.

Fixes #8722

BREAKING CHANGE: hosts where Chromium cannot create its sandbox, such as
root users and Linux containers without user namespaces or a setuid
sandbox, now fail to launch until REA_BROWSER_NO_SANDBOX=true is set.

* fix(browser): name the sandbox opt-out on any sandboxed launch failure

Chromium reports an unusable sandbox through several unrelated startup
messages, including a misconfigured setuid helper and a refused namespace
move, and Playwright recognizes only some of them. Matching individual
messages missed real failures, so a sandboxed launch failure now always
names REA_BROWSER_NO_SANDBOX=true as a conditional remediation and the
retained startup diagnostics show whether the sandbox was the cause.

* fix(browser): keep default recovery for resolver failures after launch

The module resolver's outer catch also covers context, navigation and
evaluation failures after the browser launched. Attaching the sandbox
remediation there replaced their default recovery guidance. Attach the
shared launch remediation only when a sandboxed launch itself fails.

* docs(browser): note the Android sandbox exception for module traces
2026-11-04 23:54:29 +08:00
星闌 b1b6cb64c2 fix(analysis-view)!: distinguish unavailable native collections (#8291) 2026-11-04 23:53:45 +08:00
Entrpi c13dd36d95 fix(artifacts): decide how compare_artifacts compares every format_metadata kind (#8231)
* feat(artifacts): inventory and extract ESP32 firmware images

ESP32-family app and bootloader images (magic 0xE9) had no reader, so
open_binary refused OTA downloads, app partitions and bootloaders.

Add the esp-image container format behind inspect_artifact and
extract_artifact. A pure decoder reads each byte once and keeps only
fixed-size fields. Derived facts cover the chip, from a table cited to
ESP-IDF v6.1; segment classification as ESP-IDF's should_map and
should_load; checksum and appended SHA-256 results; app and bootloader
descriptors; Secure Boot v2 signature blocks, observed and unverified;
and undescribed trailing byte ranges. Truncation and undescribed bytes
make coverage unknown. ESP8266 images are refused with a specific
reason, and ciphertext stays unrecognized.

Add the format to every target, Evidence, snapshot, live-summary and
artifact vocabulary, document it, and add an optional verify:esp-images
lane that compares esptool-built images with esptool image-info and
espsecure signature-info-v2.

Refs #3615

* test(artifacts): run the ESP image lane on esptool v5.6.0

The lane compared descriptor fields only when esptool image-info printed
one, so a descriptor that REA reports and esptool does not print passed
without comparison. Compare the descriptor kind in both directions.

Record that the lane passed for all 13 images with esptool and espsecure
v5.6.0, the version the layout facts cite, as well as with 4.12.0.

* refactor(artifacts): define ESP image layout sizes once

The decoder and the derived facts each defined the segment-header,
appended-digest and flash-sector sizes, the erased-byte scan and the
offset formatter. Define them once with the other fixed-layout structures
and import them in both places. The signature slot count is the one
constant that drives the slot scan.

Correct the observedEnd comment: it is the end of the last chunk read.

* fix(artifacts): report ESP-IDF's segment length limit by chip

ESP-IDF's verify_segment_header rejects a segment of 16 MiB or more
unless the chip maps flash through 32-bit cache addresses
(SOC_SPI_MEM_SUPPORT_CACHE_32BIT_ADDR_MAP: ESP32-S3, P4, C5, C61, H4 and
S31 in v6.1). Record that capability in the chip table and add a
limitation for such segments; unknown chips get none.

`loaded` repeats should_load. It does not mean the bootloader accepts the
segment: verify_load_addresses still rejects one outside the chip's RAM,
which REA does not model. Say so in the docs, the skill reference and the
inventory limitation.

* test(artifacts): check ESP lane tools first and verify extraction

The lane started work before checking its tools, so a missing RISC-V
target, ld.lld, esptool or espsecure surfaced as a raw spawn or command
error. Check each one before building and name the variable to set. Warn
when esptool is not v5.6.0, the release the layout facts cite.

Assert each segment's class for the ESP32-C3 app, its variants and the
bootloader, as the Xtensa image already did. Extract the app and the
signed image through the CLI and compare every file with the bytes at
esptool's segment offsets or in the appended signature sector; the
source's SHA-256 must be unchanged and equal the reported root_sha256.

* docs(artifacts): state what the ESP image lane does not establish

The Verification section listed the lane's checks but not its gaps: only
ESP32-C3 and ESP32 images come from a real producer, no real project
build, OTA download or flash dump is inspected, no signature is verified,
and the lane does not run in CI.

Record the timing of the final lane run on esptool v5.6.0.

* fix(artifacts): compare ESP segment declarations

Segment entries hash only their data, so compare_artifacts reported a
segment that moved to another load address with the same bytes as
unchanged; only the image itself changed. Compare each segment's
load_addr and data_len as its declaration, as Node SEA and PyInstaller
members are compared, and count a changed declaration as a metadata
change, also when content is unavailable.

Header offsets are layout and the classification is derived, so neither
is compared.

* test(artifacts): run ESP lane commands as owned process trees

The lane ran its tools through execFile. A timeout killed only the direct
child, so a linker or assembler that Clang or GCC had started could keep
running while the lane removed its workspace. Run every command through
createVerifierCommands, check the tools inside withVerifierCleanup, and
remove the workspace only after commands.close() has stopped every tree;
a failed close keeps the workspace.

Only a nonzero exit now counts as a refusal or an expected esptool
failure; timeouts and spawn failures propagate. Supervising each command
roughly doubles the lane's run time.

* test(artifacts): cancel ESP decoding after its first decoded chunk

The cancellation test requested cancel or close on setImmediate, which
can land while the file is still being opened and checked, so it never
showed that decoding had started. It also passed against a reader that
ignored both requests while decoding. Acknowledge the decoder's first
chunk, then cancel or close, join the operation, and assert that no
later chunk reaches the decoder and that the reader ends closed.

* docs(artifacts): record which ESP metadata comparison ignores

Segments are compared by their declaration. The image's and the
signature blocks' format_metadata are not: every field comes from their
own bytes, which content comparison already covers, and a block's
image_digest_matches follows the image's content.

* fix(artifacts): decide declaration comparison for every metadata kind

compare_artifacts listed the format_metadata kinds it compared by hand,
so a new kind went uncompared without notice; ESP segment load addresses
were first missed this way. One table beside the schemas now gives every
kind a declaration projection or the reason comparison uses none of its
facts, and a kind without an entry does not compile. Consumers read one
result per pair instead of naming formats.

- Godot PCK entries stay uncompared. Godot looks members up by the
  simplified path without res://, which logical_path holds apart from
  Unicode normalization. Godot 4.3 exports store res:// and 4.4.1
  exports do not, so comparing source_path would mark every member
  changed. The reader admits only the encryption flag, which the
  occurrence's encrypted field records.
- A changed PyInstaller declaration is now a metadata change also when
  the member's content is unavailable, as for ASAR, Node SEA and ESP
  segments. It was unknown before, for no recorded reason.
- Root kinds and ESP signature blocks stay uncompared: their fields come
  from their own bytes, which content comparison covers.
- A declaration that only one side records now adds one generic
  limitation for every compared kind. Only Node SEA added one before,
  with its own text.

* fix(artifacts): keep content comparison for metadata records with content

The record rule compared two metadata occurrences by their declaration
alone. A hand-built record that names content, with an equal declaration,
then hid a content change; PyInstaller records already did so before the
table. The rule now needs both records without an artifact_id, which is
every metadata record a reader emits.

Review changes in the same area:

- The unknown-declaration limitation also covers declarations of different
  kinds and non-container observations, and tests compare it with its
  constant.
- docs/godot-pck.md gives each Godot version's lookup path, the backslash
  case, and that a member that becomes encrypted compares unknown. The
  table points to it instead of restating it.
- The PyInstaller regression moves to an acceptance test whose MCP client
  validates the advertised output schema, and the domain tests keep one
  representative case per behavior.

* fix(artifacts): compare encryption changes when content is unavailable

An encrypted member has no content. Comparison then took the
content-unavailable fallback, which counted only ASAR storage and format
declarations, so a Godot PCK, ZIP or PYZ member that became encrypted
compared unknown although both sides record `encrypted`. The record rule
compared node-less metadata records by format declaration alone, so a PYZ
namespace record whose encryption flipped compared unchanged.

`OccurrenceMetadata` now owns the encryption comparison beside ASAR
storage and the format declaration. Every occurrence records `encrypted`,
so a pair's comparison is never unknown. The fallback, the record rule and
the metadata dimension read it there; `metadataChanged` no longer compares
`encrypted` itself. The record rule compares all of `OccurrenceMetadata`:
a changed value is changed in metadata, with availability when another is
unknown.

Decision: occurrence limitations still do not count for node-less
records. Readers derive them from the declared facts (the PYZ caveat
follows `encrypted`), and their wording can change between REA versions
while the record stays the same. The fallback does not compare them
either.

Behavior changes:
- A pair with unavailable content whose `encrypted` differs is changed
  [metadata, availability], not unknown [availability].
- A node-less record whose `encrypted` differs is changed [metadata], not
  unchanged.
- A hand-built node-less record that records ASAR storage now compares
  it; no reader emits such records.

Remaining:
- The fallback still ignores `entry_kind` and `executable`, which readers
  also declare, so a file that becomes a symlink, or a mode change on an
  encrypted ZIP member, still compares unknown. `executable` has its own
  dimension, so its fix needs content-free checks split from node checks.
  Each is its own behavior decision.
- Node-bearing pairs still compare limitation wording; records do not.
  Whether limitation wording is ever a comparison fact needs one decision.

* fix(artifacts): compare entry kind and executable when content is unavailable

When either side lacked verified content, comparison took a fallback that
counted only ASAR storage, encryption and format declarations. It compared
`entry_kind` only in the metadata check of pairs with content, and
`executable` only beside the node checks. A ZIP member that became a
symlink, whose target REA does not read, and an encrypted ZIP member whose
Unix mode gained an execute bit therefore compared unknown, although
readers declare both fields on every occurrence.

`OccurrenceMetadata` now owns the entry kind comparison beside encryption;
it is never unknown. Comparison checks fall into two groups: declared facts
that need no content (`executable` and the metadata declarations), and
facts that need both content nodes (content, kind, format, size, relations
and node metadata). The fallback, the record rule and pairs with content
share the declared checks and one rule for unknown declarations. Kind and
format stay content checks: without content they are guesses from the path.

Behavior changes:
- Unless both sides have content, a pair whose entry kind differs is
  changed [metadata, availability], not unknown [availability].
- Such a pair whose `executable` differs is changed [executable,
  availability], with metadata when another declared fact also differs.
- A hand-built node-less record whose `executable` differs is changed
  [executable]. No reader emits such records.

Remaining: declared sizes and integrity digests still compare only through
observed bytes, so they do not count when content is unavailable.

* fix(artifacts): never mark a symlink executable

On POSIX hosts the directory reader recorded a symlink's own permission
bits as `executable`. Linux reports 0777 for every symlink and macOS
usually 0755, so directory, app bundle and DMG symlinks read true. zip.js,
the PyInstaller reader and the Windows branch record false. The previous
commit compares `executable` without content, so a symlink that two
readers or two hosts record differently compared changed in `executable`,
and a non-executable file that became a symlink also reported `executable`.

`hostExecutableMarking` now gives a symlink no marking on any host:
running a symlink runs its target, and the link's own bits do not control
that.

Behavior change: directory, app bundle and DMG inventories record
`executable: false` for symlinks. Occurrence IDs do not change, but the
graph digests and manifest IDs of inventories with symlinks do. Comparing
an inventory stored before this change with a new one therefore reports
changed [executable, availability] once for each such symlink.

* fix(artifacts): stop comparing limitation wording

Pairs with content compared node and occurrence limitations as metadata,
while node-less records, the content-unavailable fallback and inventory
limitations did not. Limitations explain what an observation could
establish. Readers mostly derive them from fields that comparison already
reads, from other occurrences or from what REA could read, and their
wording changes between REA versions. So adding `A` beside an unchanged
ZIP member `a` marked `a` changed in metadata through its case-collision
note, and an older and a newer inventory of equal bytes could differ
wherever a reader reworded a note.

No comparison path reads limitations now. Inventory limitations still
appear in the result, and occurrence limitations stay in the inventory
Evidence. Readers write no node limitations, so only hand-built inventories
compared them.

Behavior change: for a pair with content, limitation text no longer adds
metadata. A pair that differs only there compares unchanged, or unknown
[availability] when a declaration is unknown.

Remaining: a few facts appear only in limitation text, or also in fields
that comparison does not read, so their changes are no longer reported
while the compared bytes stay equal: an ASAR block integrity table or an
unsupported integrity declaration, a Godot member's all-zero MD5, a ZIP
name's leading `./` (also in `source_path`), and a PyInstaller PYZ image
digest (also in `pyz_sha256`; the archive root still reports the change).
Each needs a field or a comparison decision of its own if it matters.

* fix(artifacts): compare declared digests when content is unavailable

Without observed bytes on both sides, comparison ignored the containers'
declared integrity claims. Two encrypted Godot PCK members therefore
compared unknown even when their declared MD5s showed that the script had
changed. Godot's exporter stores the MD5 of a member's original bytes, so
the MD5 does not depend on the encryption IV. This comes from the 4.3,
4.4.1 and 4.7.1 exporter sources; no real encrypted export was compared.

When either side has no content, claims of one algorithm that differ now
make the path changed in metadata and availability. Equal claims add no
change, because they are unverified, and observed bytes still decide when
both sides have them. Godot PCK and ASAR readers record such claims.

For a readable member, a directory that declares or omits an MD5 only
decides whether REA can check its bytes, so such a member compares
unchanged. An encrypted member with an MD5 on only one side stays unknown.

Remaining: declared sizes still compare only through observed bytes.

* docs(artifacts): check the contract's compared kinds against the table

docs/mcp-contracts.md named the format_metadata kinds that
compare_artifacts compares in prose, by hand, so a kind added to the
declaration table could go unnamed. The integration branch already omits
esp-flash-partition, which the ESP flash layout branch adds to the table.

The paragraph now names each compared kind by its identifier. A
conformance test requires it to name exactly the kinds whose table entry
has a declaration, which artifactFormatMetadata.ts exports as
COMPARED_FORMAT_METADATA_KINDS, and no kind that comparison ignores.

* test(artifacts): share the comparison acceptance MCP fixture

Six artifact comparison acceptance tests each copied the same MCP
steps: open_binary, inspect_artifact, the inventory_artifact substep
Evidence, then compare_artifacts. The coverage test repeated the
inspect steps three times and kept its own inventory lookup.

tests/fixtures/artifactComparisonMcp.ts now owns these steps on the
schema-validating connectLocalToolsMcp client.
connectArtifactComparisonMcp() returns call, inspect(path) and
compare(left, right). inventoryEvidence() reads the inventory Evidence
that an inspection retains, from MCP or CLI output. Each test still
writes its own authored input and keeps its expectations.

The coverage lookup matched evidence.operation, not the substep
operation. The inspection schema fixes both to inventory_artifact, so
both select the same substep.

The plist acceptance test does not change. It compares the inspection
Evidence itself across CLI and MCP and passes the comparison Evidence
to verify_reconstruction and find_changed_behavior.

No assertion or scenario changes.

* fix(artifacts): leave .NET bundle member declarations uncompared

Upstream main compares no .NET bundle member facts. Comparing `file_type`
changed `compare_artifacts` output beyond this branch's fixes, so the kind
stays uncompared until that is decided. A changed manifest still shows as a
content change of the host.

* fix(artifacts): leave HEX region declarations uncompared

The comparison table compared a region's start_address and length. A
region's path names its start and end addresses, and its length follows
from them, so two regions that pair at one path always declare the same
facts: the entry could never find a change. It only made a region with
equal bytes unknown when the other side recorded no metadata, such as a
directory of extracted regions. The kind is not compared, as on main.

The contract paragraph also stops naming .NET bundle members, which
3e51a7760 left uncompared.

* test(artifacts): compare node-less records of any compared kind

The record rule follows the comparison table, not PyInstaller, but only
PyInstaller records exercised it. An ESP segment record shows both
outcomes: an equal declaration compares unchanged and a moved one changed
in metadata, without the availability that the fallback for unread
content would add.

* test(artifacts): compare a node-less record's recorded ASAR storage

The record rule compares ASAR storage beside the declaration and
`encrypted`, but no test recorded it on a metadata record. A changed
value is changed in metadata, and a value that only one side records
is unknown in availability, as for any occurrence.

* test(artifacts): compare UnityFS member declarations through MCP

The table compares a UnityFS member's declared source_path and raw
flags, but no comparison test covered the kind, so dropping either field
from the projection left every test passing. Bundles with equal member
bytes now show both: other flags, or a stored path in decomposed form,
compare changed in metadata, and reordered members, whose index and
stream offset are layout, compare unchanged.

* test(artifacts): use the shared comparison fixture for UnityFS

The UnityFS comparison test arrived from the bottom branch with its own
copy of the open_binary, inspect_artifact and compare_artifacts steps,
which tests/fixtures/artifactComparisonMcp.ts owns here. It now uses
connectArtifactComparisonMcp() like the other comparison acceptance
tests. No assertion changes.

* test(artifacts): find the contract's compared kinds by marker

The docs conformance test found its paragraph by an exact English phrase,
so rewording "also compares" failed it while the named kinds stayed
right. The paragraph now sits between BEGIN and END markers, as the
allocation report's sections do, and the test reads the kinds between
them. It still requires exactly the kinds whose table entry has a
declaration.
2026-11-04 23:53:01 +08:00
morlutoandCursor Agent 89317a39fa fix(javascript): remove duplicate childNodes import (#8886)
* fix(javascript): remove duplicate childNodes import

The squash of #7967 re-added the import already introduced in afc18131f,
so typecheck failed with TS2300.

* test(gdb): expect the command-output cap limitation

The retained-record limitation now names the command-output setting.
The coverage assertion still required the previous fixed-cap sentence.

* test(cli): keep rejected mcp add values off Amp settings

mcp add now rejects a flag in the --command value position. The Amp
registration test still treated that invocation as a successful selection.

---------

Co-authored-by: Cursor Agent <[email protected]>
2026-11-04 20:43:04 +08:00
morluto 2d16a40a49 fix(ghidra): report selected project identity failures (#8379)
* fix(ghidra): report selected project identity failures

* fix(ghidra): retain refusal until client shutdown
2026-11-04 19:58:50 +08:00
Dhruvilkumar Ashokbhai Chodvadiyaanddhruvac29 85be0f70a6 fix(artifacts): guide retries for symlinked files (#8725)
* fix(go): suggest resolved path for rejected symlinks

* test(go): cover unresolved symlink recovery paths

* fix(go): omit schema recovery for resolved symlinks

* fix(artifacts): share resolved-path recovery across inspectors

* fix(sqlite): retain resolved database path through snapshot reads

* fix(captures): retain symlink retry guidance

* fix(artifacts): preserve retry hints through redaction and selected reads

---------

Co-authored-by: dhruvac29 <[email protected]>
2026-11-04 19:58:06 +08:00
Ha1baraA1 bd7002b441 fix(cli): export source Evidence for native value traces (#8297)
* fix(cli): export source Evidence for native value traces

* fix(cli): export source Evidence for native value traces

* fix(cli): preserve trace completeness in summaries
2026-11-04 19:56:50 +08:00
064bb3f713 fix(firmware): reject staged files cut off at the per-file limit (#4430)
* fix(firmware): keep producer reports writable under small staging budgets

extract_firmware passed max_output_bytes straight to prlimit --fsize. RLIMIT_FSIZE
applies to every regular file the producer writes, including the mandatory
report.json and unblob.log outside the staging tree, so a staging budget smaller
than the report truncated it and failed the extraction even when every staged
file fit. Derive the per-file ceiling as the larger of the staging budget and the
8 MiB report budget; polling and the final inventory still enforce the aggregate
staging budget.

Fixes #3888

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(firmware): keep files stopped at the size ceiling over budget

The per-file RLIMIT_FSIZE ceiling equalled the largest size its readers
accept, and both the staging inventory and the report reader accept a
file of exactly their budget. A write the kernel stopped at the ceiling
therefore left a truncated file that passed as complete. Raise the
ceiling by one byte so such a file exceeds its budget and is rejected.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(firmware): report the effective file-size ceiling on limit failures

The exit path re-derived the staging budget for SIGXFSZ and process-limit
recovery, so a small-budget run killed at the 8 MiB + 1 ceiling reported
file_size_bytes 64 and said the cap was max_output_bytes. Pass the
ceiling runFirmwareCommand installs and describe how it is derived.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(firmware): reject staged files cut off at the per-file limit

RLIMIT_FSIZE stops a write at the limit without extending the file, and
the staging inventory admits a file of exactly max_output_bytes. With a
budget of 8 MiB or more the producer limit equalled the budget, so a
staged file the kernel cut short published as a complete extraction;
an inherited ceiling below the budget did the same.

Derive the per-run limit once in firmwareFileSizeLimit as one byte above
the largest budget, still capped by the inherited ceiling, so a file
stopped at the limit exceeds its budget. When the inherited ceiling
removes that byte, both staging inventories refuse a regular file that
reaches the installed limit with a nonretryable file-size constraint
naming the inherited limit.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(firmware): derive limit adjustability from the per-run limit

The one byte of headroom makes the per-run file limit equal an
inherited ceiling of exactly max_output_bytes + 1, but adjustability
still compared that ceiling with the raw budget. A SIGXFSZ then advised
raising max_output_bytes, which the same ceiling clamps back to the
same limit.

Decide adjustability in firmwareFileSizeAdjustable from
firmwareFileSizeLimit itself: recommend a larger budget only when it
raises that limit.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* test(firmware): compare file-size recovery with its owners

The SIGXFSZ adjustability cases and the inherited-ceiling workflow matched
fragments of remediation prose, so a harmless rewrite would fail them.
Compare the emitted guidance with firmwareSignalLimitError for the expected
branch, check that the two branches stay distinct, and compare the inventory
refusal with firmwareFileSizeLimitReached. The selected limit, resource and
retryability stay asserted as structured facts.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: gensokyo-dev <[email protected]>
2026-11-04 19:56:05 +08:00
ShuaixinHuang 33e0bf101a fix(adb): admit completed path probes after daemon startup (#8858)
* fix(adb): admit completed path probes after daemon startup

* test(adb): preserve startup probe and digest evidence
2026-11-04 19:55:13 +08:00
Wenyu Chiou e416ce1983 fix(cli): preserve Markdown validation error cells (#7978)
fix(cli): preserve markdown validation error cells
2026-11-04 19:54:29 +08:00
김준호 61bb02d3df fix(artifacts): keep inventorying an ASAR whose member declares an invalid size (#8719)
* fix(artifacts): keep inventorying an ASAR whose member declares an invalid size

A member whose declared size is not a non-negative safe integer, such as
Figma Desktop's .codesign placeholder (size -1000), made readAsarListing
throw and failed inventory of the whole archive without naming the entry.

Listing now keeps such a member with a null size and a limitation naming
the parsed value, as it already keeps an unreadable offset. Opening a
packed member with a null size raises ArtifactEntryContentUnavailable, so
inventory records it as unavailable and continues with the other members.
ElectronFileScope keeps the strict size rule.

Member metadata that still stops inventory now names the member and the
rejected fields instead of reporting a generic malformed archive.

Fixes #8715

* fix(artifacts): bound and preserve ASAR listing diagnostics

Quote an unsafe path component or a string size through a 256-unit
prefix plus its length, so one oversized header key cannot inflate the
failure or the retained limitation. Render numeric sizes with String so a
size that JSON.parse turned into Infinity is reported as Infinity rather
than null.

* fix(artifacts): keep ASAR listing diagnostics exact and located

Report a numeric size beyond the safe-integer range as having no
retained exact value instead of the number JSON.parse rounded or
overflowed, name object and array sizes correctly, and interpret each
declared size once without validating absent directory sizes.

Quote member and parent paths through the bounded source quote, name the
rejected root fields as member failures already do, and append the
archive path to listing failures so a nested archive stays identifiable.

* test(artifacts): match ASAR listing archive paths that contain spaces
2026-11-04 19:53:45 +08:00
JANG BYUNGKUNandgensokyo-dev abc647bf6c feat(desktop): suggest managed members for Unity script assemblies (#5511)
* feat(desktop): suggest managed members for Unity predefined script assemblies

Unity Mono triage listed only inspect_managed_artifact probes, mostly for
engine and framework assemblies. Assembly-CSharp.dll and
Assembly-CSharp-firstpass.dll directly in a managed directory with a
UnityEngine reference witness now get an inspect_managed_members probe,
ordered before every managed artifact probe.

* docs(desktop): describe Unity Mono managed member probes

* refactor(desktop): read Unity managed directory from the witness fact

* refactor(desktop): reuse the managed directory index for predefined assemblies

* feat(desktop): point Unity member probes at summary delivery first

* test(desktop): no member probes without a Unity reference witness

* docs(desktop): trim Unity member probe note

* feat(desktop): suggest managed members in Unity IL2CPP backup folders

Unity IL2CPP builds write <Product>_BackUpThisFolder_ButDontShipItWithYourGame
next to the player. When shipped, its Managed/ holds pre-IL2CPP assemblies.
A path convention now records the folder on Assembly-CSharp*.dll directly in
its Managed/, including when the admitted root is the folder itself, and
triage offers inspect_managed_members for those files. Correspondence to
the shipped native code stays unknown.

* docs(desktop): note IL2CPP backup folder member probes

* docs(desktop): list IL2CPP metadata versions 24 and 27 for the metadata probe

* style(desktop): merge stacked doc comments on desktopPathConventions

* test(desktop): cover member probes in a macOS bundle managed directory

* docs(desktop): note that member probes need the game folder as root

* test(desktop): no member probe when the root is the Unity data folder

* test(desktop): check the data-folder root still gets managed probes

* test(desktop): check the IL2CPP backup convention against the probe bound

---------

Co-authored-by: gensokyo-dev <[email protected]>
2026-11-04 19:53:00 +08:00
Christopher Pruijsen 398eb0d1f1 feat(setup): register LM Studio during setup (#8611)
* feat(setup): register LM Studio during setup

Register rea in mcp.json in the LM Studio home through a new lm_studio
client. The home follows LM Studio's own lookup, read-only: the path in
~/.lmstudio-home-pointer, else an existing ~/.cache/lm-studio, else
~/.lmstudio. An unreadable or relative pointer reports a path error
instead of a guessed home.

LM Studio parses mcp.json with strict JSON.parse, so comments, trailing
commas and a BOM are rejected before any backup or write. The entry
carries command, args and env with no type or timeout. Local models have
small context windows, so the registration selects
REA_MCP_INPUT_SCHEMA_PROFILE=compact unless a profile is set explicitly,
and doctor reports an entry without a profile as stale. One predicate now
owns that default for Kimi Code and LM Studio.

* fix(doctor): resolve provider budgets for LM Studio as setup does
2026-11-04 19:52:16 +08:00
morluto 103684bce1 fix(ghidra): guide recovery from unresolved procedure selectors (#8470)
* fix(ghidra): guide recovery from missing procedures

* fix(ghidra): preserve mapped offset lookup recovery

* fix(ghidra): distinguish unmapped procedure offsets

* fix(ghidra): guide ambiguous selector recovery

* fix(ghidra): clarify fresh import excludes project edits
2026-11-04 19:51:27 +08:00
hhangandhhang ebea0f009f fix(snapshot): retain one-shot Ghidra pseudocode results (#8089)
* fix(snapshot): retain one-shot Ghidra pseudocode results

* test(snapshot): cover evidence-only pseudocode migration

---------

Co-authored-by: hhang <[email protected]>
2026-11-04 19:50:39 +08:00
Akshad Jogi 4d2aff2ec6 fix(mcp): report session tool target requirements accurately (#7340)
* fix(mcp): report session tool target requirements accurately

- Declare requiresActiveTarget on inspect_address_context in sessionToolContracts.ts so closed-session discovery stops advertising an address query that fails with target_unavailable.

- Route session-kind tools through targetAvailability in CapabilityInventory.ts before the available fallback, and report target_unsupported with mode-level remediation for get_navigation_context and inspect_address_context on artifact and archive targets.

- Keep each navigation mode's missing_operations and remediation aligned with the target restriction so recommendations name the target instead of a provider.

* test(mcp): cover closed-session and artifact target requirements

- Add CapabilityInventory controls for closed-session address and explicit-path APK availability, artifact and archive restrictions with per-mode navigation flags, and structured reasons instead of pinned remediation sentences.

- Add a production stdio MCP regression in sessionTargetRequirements.test.ts that observes both binary_session entries before and after the calls, the target_unavailable address error, the successful unsigned-ZIP APK call, and the sibling navigation prerequisite.
2026-11-04 19:49:40 +08:00
Mohamed Ayoub Eleuch 09c889b3b5 test(javascript): cover Request endpoint identity and provenance (#6365)
fix(javascript): resolve inline Request URLs in fetch calls
2026-11-04 19:48:56 +08:00
Abel YagubyanandAbelo9996 1dc9e8774d fix(cli): keep marker-only Markdown paragraph facts literal (#7813)
Fixes #7634

Co-authored-by: Abelo9996 <[email protected]>
2026-11-04 19:48:12 +08:00
Beqa 7570aabd9d docs(skill): route SPA passive capture through allowed_origins (#7252)
Default page-origin scope excludes cross-origin APIs. Point agents at
JavaScript analysis for API-origin discovery, require page origin plus API
origins in allowed_origins (list replaces default), and bump the packaged
skill to version 60.
2026-11-04 19:47:28 +08:00
Lewis WangandClaude Opus 5.5 b9138a1261 fix(native): select versioned arm64e slices for codesign by family name (#8713)
* fix(native): select versioned arm64e slices for codesign by family name

Current clang marks arm64e slices with pointer-authentication ABI
version 1 (subtype 0x81000002, lipo name arm64e.v1). The codesign
selector table accepted only ABI version 0, so inspect-signature failed
with capability_unavailable whenever such a slice was selected, for
example on any universal arm64e + x86_64 binary built today.

codesign has no versioned name: -a arm64e.v1 is "unknown architecture
name", and -a arm64e resolves to the first FAT slice with the arm64e
base subtype, whatever its ABI version. A selector is therefore exact
only for that first slice. Accept any versioned pointer-authentication
ABI for arm64e, and return a selector only when the slice is the first
in table order to carry that name; a later slice sharing it stays
unknown instead of being inspected through the earlier one. This also
removes the separate duplicate-name check from signature coverage.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(native): keep arm64e selectors unknown behind unmapped variants

codesign resolves a name to the first FAT slice with the same CPU type and
base subtype, so an earlier capability variant whose own name is unknown may
shadow a later arm64e slice. Compare identities rather than successful name
mappings.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-11-04 19:45:46 +08:00
吴佳浩Alben 54d819f382 fix(javascript): emit no timer for a literal non-function callback (#8044)
* fix(javascript): emit no timer for a literal non-function callback

Node validates the timer callback before creating a handle:

    if (typeof callback !== 'function')
      throw new ERR_INVALID_ARG_TYPE('callback', 'function', callback);

The throw is synchronous, before any timer exists, but the analysis reported a
`schedule` operation for such calls.

Measured on Node 24.22.1:

    setTimeout("code", 0)    -> THROWS ERR_INVALID_ARG_TYPE
    setTimeout(`code`, 0)    -> THROWS ERR_INVALID_ARG_TYPE
    setTimeout(1, 0)         -> THROWS ERR_INVALID_ARG_TYPE
    setTimeout(1n, 0)        -> THROWS ERR_INVALID_ARG_TYPE
    setTimeout(true, 0)      -> THROWS ERR_INVALID_ARG_TYPE
    setTimeout(null, 0)      -> THROWS ERR_INVALID_ARG_TYPE
    setTimeout(void 0, 0)    -> THROWS ERR_INVALID_ARG_TYPE
    setTimeout([], 0)        -> THROWS ERR_INVALID_ARG_TYPE
    setTimeout({}, 0)        -> THROWS ERR_INVALID_ARG_TYPE
    setTimeout()             -> THROWS ERR_INVALID_ARG_TYPE
    setImmediate("code")     -> THROWS ERR_INVALID_ARG_TYPE
    setInterval("code", 0)   -> THROWS ERR_INVALID_ARG_TYPE
    --- accepted by Node ---
    setTimeout(function(){}, 0) -> OK
    setTimeout(() => {}, 0)     -> OK
    setTimeout(fn, 0)           -> OK
    setTimeout(class {}, 0)     -> OK (fails only when the class is invoked)

So a schedule is dropped only for an exactly readable literal that is not a
function, or for a missing argument. An identifier, a call result or a spread may
still be a function at runtime and keeps its schedule.

* chore: retrigger title and readiness checks after the title was corrected

* fix(javascript): scope the timer callback rule to Node's timers module

greptile flagged two problems with the first version.

P1 - browser timers disappeared. The rule was applied to bare globals, but
browser and Electron renderer sources reach this analysis without a Node/browser
distinction, and there `setTimeout("refresh()", 0)` is a real timer: the host
evaluates the string. The rule now applies only when the timer is imported from
`node:timers`, where that form cannot exist. A bare global schedule keeps its
fact whatever argument 0 holds.

P2 - unused import. `readExactJavaScriptLiteral` was imported but never used; the
helper reads shapes through `unwrapJavaScriptExpression`. Removed.

Measured on Node 24.22.1 (the throw is what makes a node:timers schedule
impossible; the bare-global case is intentionally left alone):

    import { setTimeout } from "node:timers";
    setTimeout("code", 0)  -> THROWS ERR_INVALID_ARG_TYPE
    setTimeout(null, 0)    -> THROWS ERR_INVALID_ARG_TYPE
    setTimeout(fn, 0)      -> OK
    --- bare global, host may be a browser ---
    setTimeout("code", 0)  -> kept as a schedule

24 cases cover both sides plus the cancellation link.

* fix(javascript): reject every non-function global and unary timer callback

Reviewing the timer callback guard against Node 24.22.1 found two gaps. Both are
in the same "a call that always throws must not emit a resolved fact" family.

**An unmodified global that is not a function was accepted.** The guard read
literal nodes only, so a bare global identifier passed through even though Node
rejects it before creating a handle:

    setTimeout(undefined)  -> THREW ERR_INVALID_ARG_TYPE
    setTimeout(NaN)        -> THREW ERR_INVALID_ARG_TYPE
    setTimeout(Infinity)   -> THREW ERR_INVALID_ARG_TYPE

`isUnmodifiedGlobalReference` keeps a shadowed binding out of the rule, so a
local `const NaN = () => {}` keeps its schedule.

**Only `void` was treated as a non-function unary operator.** Every unary operator
yields a primitive, and Node rejects a primitive callback:

    setTimeout(!0)         -> THREW ERR_INVALID_ARG_TYPE
    setTimeout(~0)         -> THREW ERR_INVALID_ARG_TYPE
    setTimeout(-1)         -> THREW ERR_INVALID_ARG_TYPE
    setTimeout(+"1")       -> THREW ERR_INVALID_ARG_TYPE
    setTimeout(typeof 1)   -> THREW ERR_INVALID_ARG_TYPE

A class expression is still kept, because it passes Node's own `typeof` check and
only fails when invoked.

Fifteen regressions cover the rejected forms plus arrow, identifier, class,
spread and shadowed-global cases; ten fail on the previous commit.

* fix(javascript): let sloppy eval shadow the non-function global callback

Review finding. The new global check read `undefined`, `NaN` and `Infinity` as
unmodified globals, but sloppy `eval` can introduce a local function under one of
those names, which then shadows the global. Measured on Node 24.22.1 in script
scope:

    function g() { eval("var undefined = () => {}"); setTimeout(undefined, 0); }
    -> schedules a timer
    function g() { eval("var NaN = () => {}"); setTimeout(NaN, 0); }
    -> schedules a timer

`semanticOpaqueNameMayHaveChanged` now has to agree that no visible activation can
introduce such a name before the global reading is conclusive. In a module the
same `eval` is strict and introduces no outer binding, so the global reading still
holds and the timer is still dropped.

Three regressions cover the sloppy forms, and a fourth pins the no-eval cases as
still dropped. The three sloppy cases fail without the guard.
2026-11-04 19:45:02 +08:00
Ace 848f165417 feat(harmony): disclose unclassified inventory files (#4968)
* feat(harmony): disclose unclassified inventory files

* test(harmony): let createEvidence own JSON admission in the partial-inventory case

* test(harmony): extract the projection assertion from the archive journey

* docs(harmony): qualify the unclassified disclosure rules and pin exact-case manifests

* test(harmony): replace limitation prose with structured coverage facts

The partial-inventory control froze the sentence "Source inventory coverage is
partial; absence is unknown." verbatim. docs/testing.md asserts structured
limit facts instead of freezing explanatory sentences and puts documentation
wording in review, so the sentence is no longer substring-matched.

`coverage.status` is a disjunction of independent causes in this projection:
incomplete inventory scope, unavailable content, and an exceeded bridge budget.
Asserting only `status: "partial"` left that attribution unstated, so the two
other causes are now pinned as well. `unavailable_components` being empty and
`bridge_candidate_coverage` being complete with zero candidates make the partial
result attributable to the missing occurrence alone.

Also drops an `artifact_id` comparison that could not fail: the fixture's four
files carry the same bytes, the preceding assertion already pins that shared
digest, and `artifact_id` is derived from the digest. Distinct paths sharing
content stay covered by the path array and the independent digest expectations.

Narrows a comment that claimed exact-case matching for every name in the
control; only the manifest names are case-sensitive, because the App Pack child
suffix match is case-insensitive.

* docs(harmony): name the disclosure location for a nested child package

The unclassified-disclosure paragraph used "there" to refer back to a clause
that had just named `app_pack_children`, so a reader could take a nested `.hap`
or `.hsp` member of a HAP or HSP root to be reported as an App Pack child. It is
reported in `unclassified_components`: the child list is populated only for an
App Pack root. Names the location explicitly.
2026-11-04 19:44:18 +08:00
吴佳浩Alben b668cea2af fix(javascript): emit no fs acquisition for a literal non-flag string (#8125)
* fix(javascript): emit no fs acquisition for a literal non-flag string

`fs.open` and `fs.openSync` validate a literal flag before touching the
filesystem:

    if (!isFd(flags) && !FLAGS_SET.has(flags))
      throw new ERR_INVALID_ARG_VALUE('flags', flags, ...);

The throw is synchronous, before a descriptor exists, but the analysis reported
an `acquire` operation for those calls.

Measured on Node 24.22.1 against a file that does not exist, so each valid flag
is exercised without an EEXIST masking it:

    ACCEPTED as a flags string:
      a a+ as as+ ax ax+ r r+ rs rs+ sa sa+ sr sr+ w w+ wx wx+ xa xa+ xw xw+
    REJECTED with ERR_INVALID_ARG_VALUE:
      "" A R W rb wb s s+ x r,x

A Python-style `"rb"`/`"wb"` is the common case: those are valid in Python and
fatal at the Node call site.

`FS_OPEN_FLAGS` in `javascriptStaticAnalysisHelpers.ts` already holds exactly
those 22 accepted flags. It is now exported and reused so the accepted set has
one owner. A numeric flag is a platform bitmask and a computed value may still be
valid, so only an exactly readable non-flag string is dropped.

`fs.createReadStream` keeps its acquisition: it does not validate the flag
synchronously, it returns a stream and reports the failure on the error event.

* chore: retrigger title and readiness checks after the title was corrected

* fix(javascript): reject every fs flag literal Node rejects

The first version only decided string flags. Node's check covers every flag
shape, and each one below throws `ERR_INVALID_ARG_VALUE` or `ERR_OUT_OF_RANGE`
synchronously, before a descriptor exists:

    openSync(p, 1.5)      -> THREW ERR_OUT_OF_RANGE
    openSync(p, -1)       -> THREW EINVAL
    openSync(p, -1.5)     -> THREW ERR_OUT_OF_RANGE
    openSync(p, 1e400)    -> THREW ERR_OUT_OF_RANGE
    openSync(p, NaN)      -> THREW ERR_OUT_OF_RANGE
    openSync(p, Infinity) -> THREW ERR_OUT_OF_RANGE
    openSync(p, 0n)       -> THREW ERR_INVALID_ARG_VALUE
    openSync(p, true)     -> THREW ERR_INVALID_ARG_VALUE
    openSync(p, false)    -> THREW ERR_INVALID_ARG_VALUE
    openSync(p, {})       -> THREW ERR_INVALID_ARG_VALUE
    openSync(p, [])       -> THREW ERR_INVALID_ARG_VALUE
    --- accepted ---
    openSync(p, 0) / 1 / 65536 / +5   -> ok
    openSync(p) / undefined / null    -> ok
    openSync(p, "r") / "wx" / "sr"    -> ok

`NaN` and `Infinity` are identifiers rather than literals, so they are decided
only through `isUnmodifiedGlobalReference`; a shadowed binding keeps the
acquisition.

* fix(javascript): judge an fs flag by its value, and ignore a shifting spread

Two P1 findings on the first version.

**A unary operator was judged by its operand.** `openSync(p, void 1.5)` passes
`undefined`, so Node applies the default read flag, but the branch saw a
fractional operand and dropped a real acquisition. `openSync(p, -0)` is the valid
zero bitmask and was also dropped. `staticFlagBits` now evaluates the operator:

    openSync(p, void 1.5)   -> ok      (keeps the acquisition)
    openSync(p, void 0)     -> ok
    openSync(p, -0)         -> ok
    openSync(p, -0.0)       -> ok
    openSync(p, +0)         -> ok
    openSync(p, ~-1)        -> ok
    openSync(p, -1)         -> THREW EINVAL
    openSync(p, -1.5)       -> THREW ERR_OUT_OF_RANGE
    openSync(p, +1.5)       -> THREW ERR_OUT_OF_RANGE
    openSync(p, !0)         -> THREW ERR_INVALID_ARG_VALUE
    openSync(p, ~0)         -> THREW EINVAL
    openSync(p, typeof 1)   -> THREW ERR_INVALID_ARG_VALUE

**A spread before the flag slot shifts the runtime position.** With
`args = [path, "w"]`, `openSync(...args, "0600")` opens with mode `"0600"`, but
argument 1 is then the mode rather than the flag. The check now keeps the
acquisition whenever argument 0 is a spread, because the flag it inspects is not
provably the flag at runtime.

* fix(javascript): leave a signed fs bitmask undecided

greptile caught that the flag check rejected every negative integer, but Node
accepts some signed bitmasks. Measured on Node 24.22.1 against an existing file:

    openSync(p, -2147483648)   -> ok
    openSync(p, -1073741824)   -> ok
    openSync(p, -1)            -> THREW EINVAL
    openSync(p, -2)            -> THREW EINVAL
    openSync(p, 2147483647)    -> THREW EINVAL
    openSync(p, 2147483648)    -> THREW ERR_OUT_OF_RANGE
    openSync(p, -2147483649)   -> THREW ERR_OUT_OF_RANGE

So the sign does not decide: some negative values open a descriptor and others do
not, and which is which is a platform bitmask question this analysis cannot
settle. The check now rejects only a non-integer or a value outside the signed
32-bit range, and leaves an in-range negative undecided — the same choice
`greptile` asked for, on the principle that a call which may succeed must keep
its acquisition.

`!`, `typeof` and `delete` are still rejected, because those yield a boolean or a
string regardless of the operand.

* fix(javascript): read a no-expression template literal flag

Review finding. A no-expression template literal is exactly readable, so it is the
same flag string Node receives, but the guard only read `StringLiteral`:

    fs.openSync(p, ` + "`rb`" + `)   -> THREW ERR_INVALID_ARG_VALUE
    fs.openSync(p, ` + "`r`" + `)    -> ok

The flag slot now accepts an interpolation-free template; an interpolated template
stays undecided.

Four regressions cover both directions; two fail without the change.
2026-11-04 19:43:33 +08:00
ShuaixinHuang 65311d4992 fix(process): preserve literal temporary-root absence text (#8859) 2026-11-04 19:42:49 +08:00
김준호 066a23475c fix(artifacts): classify bundle executables by their own name (#7365)
* fix(artifacts): classify bundle executables by their own name

A directory bundle is selected for its executable, so the executable's
canonical name classifies it; the bundle directory spelling is not an
artifact alias. Bundle facts now reach artifact, archive and database
targets so the admitted-source digest binds to the executable.

Fixes #7362

* fix(artifacts): share the artifact identity across every artifact route

The trailing-archive fallback also carries the bundle facts now, so a
bundle whose executable is a script stub with an appended ZIP keeps its
digest bound to the executable.

* fix(artifacts): name the classification spelling at the resolver boundary

The resolver already knows whether a bundle directory or a file was
selected, so it hands the executable or the caller's spelling to the
reader instead of inferring it from the Info.plist. Every admitted route
now shares one identity, and the .app bundle operations accept a bundle
whatever its executable admitted as.

* fix(artifacts): route bundle keyed archive reads by bundle facts

A bundle whose executable is a plist now carries bundleInfoPlist, so
inspect_keyed_archive must read bundle-relative archives instead of the
standalone route that compares the selected bundle directory with the
executable.

* refactor(artifacts): build the selected ESP flash identity from the shared owner

The selected ESP flash route from main built path, source path and digest
inline. Use the resolver's shared identity so it carries the same facts as
every other admitted target.
2026-11-04 19:42:04 +08:00
Beqa 2f5c0155dc fix(web): omit importer integrity for the trusted resolver stub (#8693)
* fix(web): omit importer integrity for the trusted resolver stub

Import-map integrity that covers the selected importer was applied to REA's
replacement resolver script. Strip only those integrity entries from the
installed map so native resolution can proceed while preserving dependency
integrity and the caller-selected map identity.

Fixes #8544

* fix(web): keep __proto__ import-map integrity keys on a null prototype
2026-11-04 19:41:11 +08:00
Beqa 54146627f0 fix(cli): emit compact JSON validation errors for demangle-swift (#7622)
* fix(cli): emit compact JSON validation errors for demangle-swift

Piped and redirected --json argument diagnostics went through Incur's
always-indented Formatter, bypassing REA's destination-aware writer.
Buffer non-TTY SDK JSON like JSONL and rewrite through writeJsonOutput.

* fix(cli): keep token-count for piped SDK JSON documents

Compact JSON rewrite for non-TTY SDK output returned early before
token controls, so --llms/--schema --format json --token-count printed
the document instead of its count.

* fix(cli): do not token-count non-document JSON captures

Completion hooks and other non-JSON --json pipe output must stay verbatim;
token-count on non-JSON SDK captures applies only with sdkDocumentControls.

* test(cli): assert demangle JSON validation by code, not wording
2026-11-04 19:40:27 +08:00
吴佳浩Alben f285fd4f82 fix(javascript): emit no child process for a literal non-object options slot (#8019)
* fix(javascript): emit no child process for a literal non-object options slot

Node validates the child_process options object before creating anything, so a
literal primitive or a literal array in that slot throws ERR_INVALID_ARG_TYPE
synchronously and no child process exists. The analysis still reported a complete
spawn for them.

Measured on Node 24.22.1:

    spawn(cmd, "status")        -> THROWS ERR_INVALID_ARG_TYPE
    spawn(cmd, [], "pipe")      -> THROWS ERR_INVALID_ARG_TYPE
    spawn(cmd, [], null)        -> THROWS ERR_INVALID_ARG_TYPE
    spawn(cmd, [], [])          -> THROWS ERR_INVALID_ARG_TYPE
    execFile(cmd, "status")     -> THROWS ERR_INVALID_ARG_TYPE
    fork(worker, "status")      -> THROWS ERR_INVALID_ARG_TYPE
    fork(worker, [], "pipe")    -> THROWS ERR_INVALID_ARG_TYPE
    --- accepted by Node ---
    spawn(cmd, args, undefined) -> SPAWNED
    spawn(cmd, args, {})        -> SPAWNED
    execFile(cmd, [], null)     -> SPAWNED
    fork(worker, [], null)      -> SPAWNED
    exec(cmd, "inherit")        -> SPAWNED

The asymmetries matter: `spawn` rejects `null` and exempts only `undefined`,
while `fork` and `execFile` skip any nullish options, and `exec` spreads its
options and validates none of them. Drop a candidate only when the options slot
is a provable literal primitive or array, so an unresolved or non-literal value
stays a candidate.

`spawn(cmd, [], null)` is the sharpest case: it was indistinguishable from the
valid `undefined` form, including a resolved connects-stdio relation, for a call
that dies before any child exists.

* chore: retrigger title and readiness checks after the title was corrected

* fix(javascript): reject literal options Node shall reject, and drop a stray file

Two review findings on #8019.

greptile: `rejectsOptionsShape` still kept `spawn("tool", [], 0n)`, `-0` and
`1e400`, which Node also rejects before creating a child
(`ERR_INVALID_ARG_TYPE: The "options" argument must be of type object`). Those
forms are exactly the ones `readExactJavaScriptLiteral` refuses to price — a
BigInt, a non-finite numeric literal and a negated numeric literal — so read the
shape directly instead of widening the shared exact-value reader. `NaN` is an
identifier rather than a literal, so it still needs scope resolution and is left
as a documented gap.

Also remove `.rea-hopper-stage-YOryZ6/`, a local Hopper installation stub that an
`add -A` swept into the commit. It is unrelated to this change.

* fix(javascript): keep a void options slot, reject ! and ~

greptile flagged that `isUnpricedPrimitiveLiteral` dropped `spawn("tool", [], void 0)`,
`fork(..., void 0)` and `execFile(..., void 0)`. It checked the unary operand without
the operator, but `void <expr>` evaluates to `undefined`, which is the one options
value Node accepts in place of an object.

Measured on Node 24.22.1:

    spawn(cmd, [], void 0) -> SPAWNED
    spawn(cmd, [], void 5) -> SPAWNED
    spawn(cmd, [], -0)     -> THROWS ERR_INVALID_ARG_TYPE
    spawn(cmd, [], -5)     -> THROWS ERR_INVALID_ARG_TYPE
    spawn(cmd, [], +5)     -> THROWS ERR_INVALID_ARG_TYPE
    spawn(cmd, [], !0)     -> THROWS ERR_INVALID_ARG_TYPE
    spawn(cmd, [], ~0)     -> THROWS ERR_INVALID_ARG_TYPE

So the operator now decides: `void` keeps the spawn, `!` and `~` reject whatever
their operand, and `+`/`-` reject only when the operand is a numeric literal.

* fix(javascript): reject a non-finite global in the child-process options slot

An unshadowed `NaN` or `Infinity` in the options slot is a number, and Node's
options check rejects a number exactly as it rejects any other primitive. The
guard read only literal nodes, so the global identifier passed through. Measured
on Node 24.22.1:

    spawn("e", NaN)        -> THREW ERR_INVALID_ARG_TYPE
    spawn("e", Infinity)   -> THREW ERR_INVALID_ARG_TYPE
    execFile("e", NaN)     -> THREW ERR_INVALID_ARG_TYPE
    fork("x.js", NaN)      -> THREW ERR_INVALID_ARG_TYPE
    --- accepted ---
    exec("e", NaN)         -> ok, because exec validates no options
    spawn("e", undefined)  -> ok, the one accepted global
    spawn("e", null)       -> ok
    spawn("e", []) / ({})  -> ok

`isUnshadowedGlobal` keeps a local binding named `NaN` out of the rule, so a
shadowed value keeps its fact. Seven regressions cover the rejected forms plus
`exec`, `undefined`, `null`, an array, an object and a shadowed `NaN`; all seven
fail on the previous commit.
2026-11-04 19:39:43 +08:00
Wenyu Chiou 9cc34178c0 test(native): bind lipo refusal fixture to a real source (#7977) 2026-11-04 19:38:59 +08:00
STDquantum 2ae7878fc7 fix(native): retain all breakpoint locations within budget (#7975)
* fix(native): retain all breakpoint locations within budget

* fix(native): describe incomplete locations accurately

* test(native): cover incomplete location warning

* test(native): bind lipo failure to a real fixture

* test(native): use valid Mach-O fixture for lipo errors

* test(native): drop fixture change already in main

* test(native): share breakpoint limitation wording with formatter

* test(native): assert limitation delivery via owner constants

* style(native): format breakpoint limitation projection
2026-11-04 19:38:15 +08:00
STDquantum 094ec5eb3f fix(cli): reject missing mcp add option values (#7996)
* fix(cli): reject mcp add options missing their values (#7609)

* fix(cli): reject mcp add values swallowed by the next flag (#7609)

* fix(cli): reject empty mcp add -c value (#7609)

* test(cli): verify rejected mcp add preserves settings

* chore: keep main formatting out of merge diff
2026-11-04 19:37:31 +08:00
吴佳浩Alben 3640f01512 fix(javascript): emit no promise for a literal non-callable executor (#8264)
* fix(javascript): emit no promise for a literal non-callable executor

Node's `Promise` constructor calls the executor synchronously and throws a
`TypeError` when it is not callable, before any promise object exists. The
analysis reported a `constructor` promise operation with a hardcoded
`sourceResolution: "complete"`, which the graph projects as
`creates-promise` `resolved`.

Measured on Node 24.22.1:

    new Promise()                        -> THREW TypeError
    new Promise(1) / (-1)                -> THREW TypeError
    new Promise("work") / (`work`)       -> THREW TypeError
    new Promise(true) / (!0)             -> THREW TypeError
    new Promise(null) / (void 1)         -> THREW TypeError
    new Promise(1n) / (/re/)             -> THREW TypeError
    new Promise([]) / ({})               -> THREW TypeError
    --- accepted ---
    new Promise(() => {})                -> OK Promise
    new Promise(async () => {})          -> OK Promise
    new Promise(function () {})          -> OK Promise
    new Promise(class {})                -> OK Promise

A class passes Node's own `typeof` check, so it keeps its operation. An
identifier, a call result or a spread may still supply a function and also keeps
its operation.

The filter gates the candidate push rather than `promiseCandidateDetails`, which
also decides whether an `await` becomes an `awaited-expression`: gating there
made `await new Promise(1)` emit a spurious task fact, whereas gating at the push
site makes it emit nothing, which is the honest answer.

* chore: retrigger title and readiness checks after the title was corrected

* fix(javascript): reject a non-finite executor, not only a literal

greptile caught that `new Promise(1e309)` still emitted a promise. Babel stores
that literal as `Infinity`, and `readExactJavaScriptLiteral` refuses to price a
non-finite number, so the guard missed it.

Measured on Node 24.22.1 — every one throws before a promise exists:

    new Promise(1e309)     -> THREW TypeError
    new Promise(1e400)     -> THREW TypeError
    new Promise(Infinity)  -> THREW TypeError
    new Promise(-Infinity) -> THREW TypeError
    new Promise(NaN)       -> THREW TypeError

A numeric literal is now read directly rather than through the exact-value
reader. `Infinity` and `NaN` are identifiers rather than literals, so they are
decided through `isUnmodifiedGlobalReference`; a shadowed binding keeps its
promise, which is the same scoping rule the timer fix uses.
2026-11-04 19:36:45 +08:00
Wenyu Chiou 6aeaa04e54 fix(javascript): reuse shared AST child traversal (#7967) 2026-11-04 19:36:00 +08:00
吴佳浩Alben d5ba7cec60 fix(javascript): link a response body reader only to a settled fetch (#8028)
* fix(javascript): link a response body reader only to a settled fetch

`collectResponseConsumers` treated any `.json()`/`.text()`/`.arrayBuffer()` on a
binding holding a request result as a response consumer, and gave it the
request's endpoint at `resolution: "complete"`. That holds only for a settled
`fetch`.

Measured on Node 24.22.1:

    await fetch(u)               -> Response      json/text/arrayBuffer = function
    fetch(u) unawaited           -> Promise       json = undefined; pending.json() THROWS TypeError
    http.get(u)                  -> ClientRequest json/text/arrayBuffer = undefined
    http.request(u)              -> ClientRequest json = undefined; request.text() THROWS TypeError
    new WebSocket(u)             -> WebSocket     socket.json() THROWS TypeError
    await http.get(u)            -> ClientRequest (await does not help)

So four shapes emitted a resolved "this endpoint's body is read here" fact for a
call that throws before any body is read: an un-awaited fetch, `http.get`,
`http.request`, and a `WebSocket` handshake.

Link only requests that settle to a `Response`: a `fetch` call whose value is
awaited through expression wrappers. A binding that also holds a request kind
without a body reader keeps the consumer a candidate (`partial`) rather than a
proven one, and a binding with no settled fetch emits no consumer at all, which
matches the `collectReleases` / `forkRejectsStdioArray` precedent — the receiver
provably has no such member, so there is no fact to report.

* chore: retrigger title and readiness checks after the title was corrected
2026-11-04 19:35:16 +08:00
RRFZ a97f827469 fix(pe): reject malformed certificate validity times (#8832) 2026-11-04 19:34:31 +08:00
Machen John 39172ca2e1 fix(ghidra): keep a changed existing project's refusal typed through launch (#8273)
* fix(ghidra): keep a changed existing project's refusal typed through launch

When the selected REA_GHIDRA_PROJECT_PATH project changes after
open_binary commits its profile, the launcher's verified copy refuses it
with AnalysisArtifactChangedError. GhidraHeadlessLauncher wraps that in
GhidraLaunchError, and the session error's cause became the wrapper, so
the projection missed the existing artifact_changed branch and returned
generic execution_failure with rea doctor advice.

Read the launcher's primary failure when it is an
AnalysisArtifactChangedError, so the refusal reaches callers as
artifact_changed with the changed path and the existing reopen guidance.
The refusal stays fail-closed: no automatic reopen or fresh-import
fallback. Other launch failures keep their current projection.

Fixes #8217

* fix(ghidra): describe a project profile mismatch without a timing claim

When the owned copy of a selected Ghidra project no longer matches the
fingerprint committed by open_binary, copyExistingGhidraProject threw
RegularFileChangedError, so the artifact_changed reason said the input
"changed while being read". A digest mismatch only shows that the
project differs from its committed profile; the change may have happened
before the copy started.

Throw AnalysisArtifactChangedError directly with a reason that states
the mismatch and leaves the time of the change unestablished. Changes
observed while files are being read keep their existing reason.
2026-11-04 19:33:29 +08:00
2SinceJuly 1dd70f6c2b fix(cli): seal in-process Evidence before publishing (#8468)
* fix(cli): seal in-process Evidence before publishing

The CLI rebuilt a plain bundle from records it had already authenticated,
so writeEvidenceBundle cloned and authenticated the complete analyzed
graph again at the final checkpoint. On a default heap that second copy
could exceed available memory after minutes of analysis and discard the
whole run.

Seal the in-process bundle once so publication can stream the canonical
form without the redundant authentication copies. The regression test
fails on the previous behavior with resource_constraint under a one-byte
heap allowance.

Fixes #8411

* fix(cli): preserve the heap check for mutable Evidence

Only seal the publication bundle when every record is already immutable.
Mutable producer records keep the writer's existing heap check instead of
being copied before that check. The regression now covers the safe
fallback and lets the shared temporary-directory fixture own cleanup.

* test(cli): keep a distinct mutable source in the publication regression

Seal the main record and give the mutable source its own evidence ID so
the map cannot replace it before the bundle guard runs.
2026-11-04 19:32:44 +08:00
Rohit Ghumare f042aaaf90 fix(gdb): keep sessions usable after an oversized stream record (#8029)
* fix(gdb): keep sessions usable after an oversized stream record

A single MI console, target or log record over the 16 MiB line limit
failed the whole session protocol, so close could not re-synchronize
and GDB plus its inferior stayed owned with no recovery path.

MI stream records carry only output and c-strings escape newlines, so
a raw LF always ends the record. #receive now drops the remainder of
an oversized stream record, marks pending command output and retained
records truncated, and keeps parsing later lines; oversized result
and async records still fail closed as before.

Fixes #8023.

* fix(gdb): keep earlier records in a chunk with an oversized stream record

* test(gdb): drop superseded oversized-record test copies
2026-11-04 19:32:00 +08:00
Dragolone 1f8084c7db fix(node-sea): bound ELF note scans by the notes a carrier holds (#8031)
Artifact classification probes every ELF root for a Node SEA note. Each
PT_NOTE program header was scanned on its own, one read per note, so
headers that declare the same note bytes multiplied the work: a 295 KB
ELF with 4,096 such headers kept inspect-artifact busy for about three
and a half minutes.

Every note header takes at least 12 bytes, so disjoint tables hold at
most one note per 12 file bytes. The scan now stops there. Past that
count the remaining tables are unreadable: an ELF without a confirmed
blob keeps its native classification, and a confirmed blob fails closed.
2026-11-04 19:31:14 +08:00
Adolanium 1861059f8b fix(wasm): name the compare_wasm_interfaces side of rejected Evidence (#7302)
* fix(wasm): name the compare_wasm_interfaces side of rejected Evidence

compare_wasm_interfaces parsed both sides inside one try and reported any rejection, such as tampered content, a different operation or a rebound subject, as an input issue at path [], which names no argument. Each side is now parsed separately and a rejection is reported at [left] or [right], as the function, native and managed comparers already do.

* test(wasm): cover both comparison sides through the MCP boundary
2026-11-04 19:30:31 +08:00
highmachs d0d31fcf21 fix(browser): accept 3-digit HTTP response status codes up to 999 (#8075) 2026-11-04 19:29:44 +08:00
Dragolone d857b61bc5 fix(android): ignore action-less filters in activity export defaults (#8030)
Android's package parser discards an activity, activity-alias or receiver
intent filter that declares no action, so such a filter neither makes the
component exported by default nor triggers the API 31 explicit-export
requirement. Services keep action-less filters. The manifest inventory
counted every declared filter, reporting exported_default true (or an
explicit-export validation error) for components Android does not export.
intent_filter_count still reports every declared filter.
2026-11-04 19:28:59 +08:00
morluto 810cebcba0 fix(ghidra): explain read-only project edit refusals (#8413)
* fix(ghidra): explain read-only project edit refusals

* test: follow provider guidance ownership
2026-11-04 19:28:15 +08:00
Tawhidul IslamandTawhidul Islam 703c11c90f fix(javascript): recover configuration file reads from fs/promises (#8446)
* fix(javascript): recover configuration file reads from fs/promises

* fix(javascript): disallow nonexistent readFileSync on fs/promises

---------

Co-authored-by: Tawhidul Islam <[email protected]>
2026-11-04 19:23:52 +08:00
Beqa 4910e149ad docs(setup): describe FreeBuff as published setup support (#7246)
Setup against npm latest already accepts --client freebuff. Replace the
stale proposed-integration wording while keeping the unverified native
client loading caveat.
2026-11-04 19:22:36 +08:00
Christopher Pruijsen 5b953e13c4 fix(mcp): keep detail guidance in compact input schemas (#8034)
Compact input schemas are the default advertisement since #5975, and the
profile drops every property description. On 23 of the 31 tools with a
guided `detail` option, including inspect_artifact and inspect_macho,
`detail` is advertised as a bare complete|summary enum, so callers take the
complete default. On a universal macOS app that reply was about 699k
characters for inspect_artifact and 336k for inspect_macho, against about
4.4k and 3.2k with summary.

Keep the canonical description of a root `detail` property in the inline
compact input form. The reduced form already keeps first-level guidance, and
compact output schemas are unchanged. Advertised compact input schemas grow
from 127,616 to 133,358 bytes across 233 tools; the largest schema stays at
4,107 bytes, within the 13,312-byte budget.

The new boundary test compares `detail` guidance between the full and compact
tools/list and fails before the change.
2026-11-04 19:14:52 +08:00
Beqa a56cc93cab test(desktop): cover ENOTDIR intermediate path refusal (#8692)
* test(desktop): cover ENOTDIR intermediate path refusal

Lock the desktop consumer projection for selections whose intermediate
component is a regular file, matching inspect-artifact's unavailable-target
refusal after directory source spelling validation.

Fixes #7424

* style: oxfmt ArtifactSourceResolution test
2026-11-04 19:13:53 +08:00
吴佳浩Alben 007e181f68 fix(javascript): require exactly one fork IPC channel (#8017)
* fix(javascript): require exactly one fork IPC channel

Node's fork() rejects an explicit stdio array that carries more than one IPC
channel, and the throw is synchronous, before any child process exists:

    if (ArrayPrototypeFilter(options.stdio, ...'ipc').length > 1)
      throw new ERR_IPC_ONE_PIPE()

The analysis already dropped an array with no "ipc" (#7992) but still reported a
resolved fork for an array carrying it twice.

Measured on Node 24.22.1:

    fork({stdio: ["ipc", "ipc"]})                -> THROWS ERR_IPC_ONE_PIPE
    fork({stdio: ["pipe", "ipc", "ipc"]})        -> THROWS ERR_IPC_ONE_PIPE
    fork({stdio: ["ipc"]})                       -> SPAWNED connected=true
    fork({stdio: ["pipe", "ipc"]})               -> SPAWNED connected=true
    fork({stdio: ["pipe", "pipe", "pipe", "ipc"]})-> SPAWNED connected=true

So the required count is exactly one, not at least one. Count the resolved "ipc"
literals and reject the candidate unless the count is one; a spread or an
unreadable element may still change the count and is left alone, as before.

* chore: retrigger title and readiness checks after the title was corrected
2026-11-04 19:11:35 +08:00
hhangandhhang f8ec48095a fix(cli): guide delimited analysis view exports (#8730)
Co-authored-by: hhang <[email protected]>
2026-11-04 19:10:35 +08:00
Entrpiandrobotaro 1df7ac9320 fix(ghidra): update the host no-return probe for the current bridge (#8462)
The host no-return verifier, scripts/verify-real-ghidra-noreturn.mjs,
fails before its Evidence checks. ReaNoReturnProbe calls private bridge
methods through reflection, and two bridge changes left the probe behind:

- d4bd65c53 added a Function parameter to noReturnCallLimitations, and
  225f26a5f renamed the method to functionBoundaryLimitations. The probe
  still looked up noReturnCallLimitations(Iterable) and stopped with
  NoSuchMethodException.
- 01a1b3fda made procedureReferences return a function observation
  envelope. The probe read references and unresolved_calls from the top
  level of that envelope instead of from its value.

Pass each function and its instructions to functionBoundaryLimitations,
and read procedureReferences results from the envelope value. The require
checks are unchanged. CI does not run this verifier.

These edits are the probe repair from robotaro's open draft that treats
demangled C++ runtime imports as returning, copied line for line, so
either change merges cleanly after the other.

Co-authored-by: robotaro <[email protected]>
2026-11-04 19:09:33 +08:00
RRFZ 52ff3123cb docs(mcp): list every tool that omits the untrusted-content marker (#8837) 2026-11-04 19:04:57 +08:00
김준호 67552d56cc docs(limits): say setup forwards the selected MCP response budget (#8342)
The design guide said the setup allowlist does not copy
REA_MCP_MAX_RESPONSE_BYTES, but SELECTED_REGISTRATION_SETTINGS includes
it and setup forwards an explicit selection, as the client guide states.

Fixes #7420
2026-11-04 19:03:56 +08:00
Vinícius Andrade 6ba69cdcc1 docs(cli): document standard input for JSON request arguments (#7139)
An argument that accepts inline JSON or a JSON file path also reads the
request from standard input when it is "-". The behavior shipped without
documentation. Describe it next to the request-file example, with its
UTF-8, byte-order mark and 8 MiB limits.

Fixes #7138
2026-11-04 19:02:54 +08:00
SathishM 578e4bb71a docs(installation): list every engine accepted by doctor --provider (#4767)
The One analysis engine readiness row offered ghidra (or hopper, ida) while
the CLI accepts hopper, ghidra, ida, jadx and kuna for doctor --provider.
Add the missing IDs and keep the table aligned.

Related to #4624: docs/cli.md carries the same stale enumeration and is
updated separately in #4651.
2026-11-04 19:02:32 +08:00
Abduallah Jamal 47cedcc93a fix(cli): reject empty artifact command targets before resolution (#8834) 2026-11-04 16:47:46 +08:00
RRFZ 6c3c5ebc69 fix(rust): state inflate limits on the consumed zlib stream (#8835) 2026-11-04 16:47:31 +08:00
RRFZ 772c1bc401 fix(pe): reject empty and non-minimal certificate serial INTEGERs (#8836) 2026-11-04 16:45:48 +08:00
ShuaixinHuang 1998e65705 fix(ghidra): discover installed versioned Homebrew JDKs (#8853)
* fix(ghidra): discover installed versioned Homebrew JDKs

* test(ghidra): protect Homebrew Java candidate precedence

* fix(ghidra): continue automatic JDK discovery after a failed keg
2026-11-04 16:10:46 +08:00
ShuaixinHuang 650a459346 fix(cli): share boolean timing argument handling (#8857)
* fix(cli): share boolean timing argument handling

* fix(cli): preserve executable launcher mode
2026-11-04 16:10:30 +08:00
Pierre d7df9df339 fix(ghidra): mark empty xrefs results as unresolved, not unreferenced (#8831)
* fix(ghidra): mark empty xrefs results as unresolved, not unreferenced

Ghidra 12.2 resolves no references to data addresses on some Mach-O
imports, and xrefs returned a bare empty list with nothing separating
'no references exist' from 'this provider resolved no references', so
callers concluded addresses were unreferenced.

projectExecution now attaches ghidraEmptyXrefsLimitation to an empty
xrefs result: the list reflects what this analysis resolved, not proof
the address is unreferenced. Non-empty results carry no caveat.

Covered by behavior tests for both branches; validated against
FM9-Edit-arm64 (expect: limitation) once the daemon session reopens.

* fix(ghidra): drop circular trace_feature cross-check from empty xrefs caveat

trace_feature calls xrefs internally, so recommending it as an independent
cross-check repeats the same lookup the caveat qualifies. Keep disassembly
as the single independent check.

* fix(ghidra): report actionable-reference absence and invalidate stale snapshots

Review follow-up on the empty xrefs caveat:
- The bridge filters synthetic entry-point references before observation, so
  "resolved no references" could be false; the caveat now reports that no
  actionable references were returned and names the omission.
- Drop the trace_feature cross-check: traceReferences observes xrefs per
  matched string, repeating the same lookup instead of independent evidence.
- Bump instruction_references semantics to v3 so analysis snapshots captured
  before the caveat replay no bare empty results; the profile commitment in
  the snapshot binding no longer matches.

* feat(ghidra): mark empty function-reference facets as unresolved too

Extends the empty-xrefs caveat to the sibling ReferenceManager consumers
review follow-up identified: procedure_references and analyze_function
reference facets built on the same observation path could still present
empty arrays as collected facts.

- ghidraEmptyFunctionReferencesLimitation attaches when
  procedure_references returns no references for the requested direction
  and when a dossier observes no references in either direction;
  callers/callees (function-manager derived, observed working on the
  reported target) stay caveat-free.
- One emptyReferenceCaveat owner replaces the inline xrefs branch.
- function_references semantics bump to v3 so snapshots captured before
  the caveat do not replay bare empty facets; raw profile digests
  recomputed.

* fix(ghidra): cover annotation and instruction reference facets in the caveat

Review follow-up: annotate_native_function returns a dossier built by the
same analyzeFunction collection and inspect_native_instruction builds
incoming_references with visitObservedReferencesTo; both could present
empty arrays as collected facts under the reported ReferenceManager
failure.

- annotate_native_function attaches the caveat when its dossier observes
  no references in either direction.
- inspect_native_instruction attaches it when incoming_references is an
  explicit empty array (null or omission stays unavailable, matching the
  field contract).
- Split the diagnostics describe to keep each block under the lint bound.
- Behavior tests cover attach and non-attach for both operations.

* fix(ghidra): name the honest cross-check limits in the reference caveats

Review follow-up: the motivating failure was a string address, so
'cross-check the disassembly at this address' pointed at nothing — data
and string addresses have no instruction to disassemble — and
inspect_native_instruction's incoming_references is built through the
same visitObservedReferencesTo path, so it was not independent either.

- xrefs caveat now states that no independent in-product cross-check
  exists for the empty result, that surrounding disassembly only helps
  at a code address, and that data or string addresses need an external
  disassembly scan (otool -tV adrp/add pairing).
- Function-facet caveat separates outgoing references (own disassembly
  is independent) from incoming references (no in-product check; scan
  other code externally).

* fix(ghidra): use the address-scoped caveat for inspect_native_instruction

Greptile review follow-up: inspect_native_instruction checks incoming
references to one exact address, so the function-scoped caveat misdescribed
the scope; an instruction can be unreferenced while its function has
references elsewhere, and the address may have no containing function.
Attach the address-scoped xrefs caveat instead.

* fix(ghidra): qualify each empty reference direction independently

Analyze-function and annotate-native-function results attached the
unresolved-references caveat only when BOTH reference directions were
empty, so a dossier with one populated direction presented its empty
direction as a collected fact.

Split the combined function-references caveat into direction-scoped
limitations and attach one per empty direction (analyze_function,
annotate_native_function dossier readback, procedure_references by its
requested direction).

* fix(ghidra): qualify empty outgoing instruction references

inspect_native_instruction results carry two independent
ReferenceManager facets: the incoming list and the outgoing references
built from instruction.getReferencesFrom(). Only the incoming list was
checked, so an instruction with a populated incoming list and no
outgoing references presented the empty outgoing facet as a collected
fact, and any populated incoming list suppressed the caveat entirely.

Each facet is now qualified independently: the empty incoming list
keeps the address-scoped caveat; the empty outgoing list attaches the
new instruction-scoped outgoing limitation (this stream is not
entry-point filtered, so it claims no entry-point omission). Truth
table covered in describe("Ghidra empty instruction reference facets").

* fix(ghidra): gate the instruction outgoing caveat on decoded status

Non-decoded inspect_native_instruction results (data, not-in-listing,
not-instruction-boundary, outside-memory) return before the bridge ever
queries instruction.getReferencesFrom(), so their empty references list
is an unobserved facet, not an empty ReferenceManager observation. The
unconditional outgoing caveat claimed the ReferenceManager returned no
references from an instruction that does not exist at that address.

The outgoing limitation now attaches only when status is decoded; the
address-scoped incoming caveat still applies per the empty incoming
list, matching the original motivating data-address case.

* fix(ghidra): dossier incoming omission and external outgoing recovery

Two unqualified cases remained in the function-reference caveats:

- The analyze_function/annotation dossier excludes incoming references
  whose source is inside the function's own body (a self-recursive call
  is enough), so the projected empty list can mean 'no outside source
  observed' or 'only in-body sources observed'. The dossier now uses a
  dedicated incoming limitation naming the exclusion, pointing at the
  outgoing edges/disassembly where in-body edges remain visible, and at
  procedure_references(incoming) - the same ReferenceManager stream
  without the in-body exclusion - as the in-product discriminator.
  procedure_references keeps the generic incoming caveat: its incoming
  stream is not in-body filtered.

- An external function has no local body, so the outgoing caveat's
  'function's own disassembly' recommendation was impossible. Records
  classified external now get an external-specific outgoing limitation
  naming the defining artifact and this program's call sites instead
  (procedure_references outgoing, analyze_function, annotation readback).

* fix(ghidra): qualify dossier incoming caveat observation

The dossier-specific incoming caveat claimed the ReferenceManager
observed no incoming references from outside the body, but
visitObservedReference (ReaGhidraBridge.java:3973-3974) drops
entry-point references before the body filter (1619-1621), so the
manager may have returned references the dossier never reports.
Reframe as 'returned no actionable' and name both omissions; correct
the recovery claim: procedure_references(incoming) lacks the in-body
filter but omits synthetic entry-point references as well.
2026-11-04 11:58:44 +08:00
morluto 5a11bf09a5 fix(mcp): surface byte search for encoded literals (#8817) 2026-11-04 11:53:38 +08:00
morluto f8f9095d2d feat(kuna): verify release 1.990 (#8813) 2026-11-04 11:49:36 +08:00
Rishikesh Gowda K K e75d1c741c feat(elf): decode Linux core dump process and memory mapping notes (#8830)
* feat(elf): decode Linux core dump process and memory mapping notes

Inspect Linux core dump note tables and decode structured metadata without
requiring external tools or debuggers:
- NT_PRPSINFO (type 3): decode process ID, parent ID, process group,
  session, UID, GID, execution state, filename, and command line arguments
  bounded by the text budget.
- NT_FILE (type 0x46494c45): decode mapped memory ranges (start_address,
  end_address, file_offset_pages) and mapped file paths with page size.
- NT_SIGINFO (type 0x53494749): decode faulting signal number, errno, and code.
- NT_AUXV (type 6): decode auxiliary vector pairs with known AT_* tag names.
- Support both 32-bit and 64-bit, little-endian and big-endian targets.
- Retain unknown core note types gracefully without partial success errors.
- Add comprehensive unit tests, fixtures, acceptance tests, and documentation.

* fix(elf): preserve raw path bytes and enforce auxv termination

* fix(elf): account for path hex text budget and enforce auxv trailing padding

* fix(elf): account for row and scan limits, ABI signal order, and lossless source IDs

* fix(elf): cache auxv padding scans, pre-charge entries, and isolate page-size test
2026-11-04 11:46:11 +08:00
morluto c3647ade1e fix(objdump)!: preserve unknown executable identity (#8824)
* fix(objdump)!: preserve unknown executable identity

* fix(objdump): avoid claiming rejected architecture output

* fix(objdump): align launch errors with command identity

* test(objdump): avoid coupling identity checks to wording

* test(objdump): align launch failure expectations
2026-11-04 02:01:44 +08:00
morluto 6be6596aac test(mcp): bound schema compilation for native and web workflows 2026-11-04 00:14:47 +08:00
morluto f3e5b33538 fix(artifacts): settle transient DMG verification within its deadline 2026-11-04 00:14:46 +08:00
morluto 26b033d51f fix(process): classify withheld Darwin environments before parser failures 2026-11-03 23:48:22 +08:00
ShuaixinHuang 9a68ce57a0 test(elf): cover big-endian ABI notes through CLI and MCP (#8803) 2026-11-03 23:25:44 +08:00
highmachs 276d99e787 fix(plist): decode OpenStep quoted string unicode and octal escape sequences (#8794) 2026-11-03 23:12:06 +08:00
ShuaixinHuang 19082acddb test(electron): consolidate handler presence coverage in public workflows 2026-11-03 23:11:45 +08:00
ShuaixinHuang e8cb71740b test(electron): remove duplicate graph protocol fixture 2026-11-03 23:11:45 +08:00
ShuaixinHuang c28d47bf13 fix(electron): retain wrapped protocol handler registrations 2026-11-03 23:11:45 +08:00
Cursor Agent 8c857ce56c docs(readme): move Discord button below site links
Place the Join us on Discord button under each hero's website, guides,
and showcases line. The invite and alt text stay the same.
2026-11-03 16:56:44 +08:00
morluto 28bffe92c8 docs(readme): move Discord button below site links (#8755) 2026-11-03 16:51:45 +08:00
morluto 4f7c4cc218 docs(readme): add Join us on Discord button (#8754) 2026-11-03 16:45:28 +08:00
Liu Jie 4b961e3715 fix(cli): protect Amp settings during default registration (#8076) 2026-11-03 14:50:33 +08:00
Paritosh LahreandClaude Opus 5.5 8db538a7ca fix(browser): keep negative script columns Chrome reports (#8684)
* fix(browser): keep negative script columns Chrome reports

Chrome reported a Debugger.scriptParsed event with startColumn -60 and
endColumn -15 for an inline script in WordPress's helper frame. CDP declares
these columns as plain integers, but REA's admission required min(0), so one
script failed the whole observe-web-execution run with unreadable_output.

CDP resource columns now share one representation, cdpResourceColumnSchema,
for script start/end columns, initiator call frames, parser initiators and
event listeners. Positions on a script's first line include its start column,
so keeping the reported values keeps the documented script-relative
conversion consistent. The domain contract and web runtime docs state that a
negative column does not locate a column in the resource.

Fixes #8674

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(browser): apply the CDP column policy to passive capture

inspect_web_page and discover_webmcp_tools still read CDP columns through
cdpSourceCoordinates, whose own >= 0 check turned a negative column into
null and reported invalid_protocol_value. WebMCP discovery also dropped the
tool's whole registration_source.

cdpResourceColumnSchema now lives with the domain's CDP observation schemas.
Runtime admission, the passive coordinate helper and the runtime, console,
initiator and WebMCP output schemas all use it. Lines keep their
non-negative rule.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-11-03 14:50:00 +08:00
Wenyu Chiou e01fcfb4b2 fix(reference): Exclude Git pointer files from source identities (#7667)
* fix(reference): Exclude Git pointer files from source identities

Match .git pointer files in the default import policy after the existing
directory rules, preserving ordinary-checkout exclusion provenance and Git
HEAD discovery. Cover linked-worktree identities, repeated imports, nested
metadata, similarly named source files, and configured-secret precedence.

Independent review: parent independent review APPROVE for Draft CI on patch
61bddf108952c77858fa71c333b4bb8a05ab33e8b03f5c6bccd879cd19c7c379 and source
tree 2d2372a9ec. Review triggers: delegated
shipping output, source inventory/file I/O behavior, and more than 50 lines.

Validation: npm run check passed with Node 24.22.1/npm 11.16.0 on Windows;
tracked Vitest regression 1 passed/3 Windows skips; test discovery passed.
Eight offline Linux production imports passed on cached Node 22.19.0 as
supporting evidence only. Pinned Linux/macOS Vitest cases await hosted CI.

Fixes #7551

* test(reference): Isolate worktree fixtures from host Git settings

Route every fixture Git command through one wrapper with a filtered child
environment, owned empty global config/template/hook paths, and no system
config reads. Keep resource cleanup under the existing temporary owner.
These settings are scoped to synthetic child processes; host Git settings
and this repository's commit/push checks are unchanged.

Targeted independent review: parent review APPROVE on patch
4be9a2af3e40edac3d64f687170e966a87fcf762f1877f2c55016e120bcd90ac and tree
a2ba91ee9c. The reviewer confirmed the native
proof helper hash, complete fixture command coverage, and owned cleanup.
Review triggers: delegated follow-up and fixture file/process boundaries.

Validation: npm run check:fast passed; targeted Vitest 1 passed/3 native
Windows skips. On pinned Node 24.22.1, the actual helper rejects inherited
hooks/templates while commit, worktree creation, HEAD and status checks pass.
The unisolated control copies the injected template and its commit fails.

Refs #7551
2026-11-03 14:49:28 +08:00
Wenyu Chiou f5cfe09c07 fix(environment): disclose truncated comparison differences (#7673)
Preserve the twenty-entry diagnostic prefix and report the full count,
omitted count and truncation fact at the owning report projection. Document
the recovery limitation and cover the public HAR-to-comparison workflow.

Fixes #7621

Validation: 121 source tests; three selected CLI regressions (five other
cases unselected); nine README conformance cases; full --noEmit typecheck;
documentation facts/metadata/schema/licenses; module boundaries and
architecture guards. VitePress rendering is blocked by Windows EPERM on its
worktree-local Vue symlink. Full aggregate coverage remains for Draft CI.

Independent code review: APPROVE, relayed by parent task
01a17716-6299-75fe-87e9-b0398b8c9019. Trigger: >=50 changed lines / >=3 files.
Reviewed tree: ba964efcd6
Reviewed full-index patch SHA256:
972dcfc219dd7b9606175b1848068f92210fa3f7d0a8a244c1ac7ce296ae32d9
2026-11-03 14:48:54 +08:00
Beqaandmorluto f59d884f35 fix(flutter): read unlabeled Dart Version::String() from engines (#7232)
* fix(flutter): read unlabeled Dart Version::String() from engines

Release libflutter.so embeds Dart's Version::String() without the
standalone VM's "Dart VM version:" label, so identify_flutter_build left
dart_version null on real APKs. Accept that channel-bearing form beside
the labeled scanner path.

* fix(flutter): qualify unlabeled version candidates

---------

Co-authored-by: morluto <[email protected]>
2026-11-03 14:48:21 +08:00
ShuaixinHuang 4f4e2552cc fix(verification): compare retained Ghidra function evidence (#8672)
* fix(verification): compare retained Ghidra function evidence

* test(verification): require the exact missing Evidence refusal
2026-11-03 14:47:49 +08:00
Ha1baraA1 b3e22620a5 fix(mcp): keep scoped doctor output parseable (#7648) 2026-11-03 14:47:16 +08:00
Hasan Zayni 53fca98e3a fix(setup): omit manual connectors from automatic selection (#8009)
Retain detected manual connectors and explicit selection while excluding unsupported formats from automatic setup readiness. Fixes #7522.
2026-11-03 14:46:43 +08:00
Jeremy Levartovsky 5ed283d168 fix(har): accept Chrome DevTools bracketed IPv6 server addresses (#7225)
Chrome DevTools HAR exports declare creator WebInspector 537.36 and
report IPv6 serverIPAddress values in brackets: its HAR writer strips
the port with lastIndexOf(':') but its bracket removal,
replace(/\[\]/g, ''), only matches the literal pair '[]'. The pinned
schema then rejects the whole archive at the first such entry.

Extend the existing Playwright 1.58.2 adaptation to this declared
producer. Brackets are removed only in the disposable validation copy
and only around a valid IPv6 literal; reported spellings are retained
and the per-entry limitation names the declared producer.
2026-11-03 14:46:10 +08:00
Ha1baraA1 f251009e0a fix(artifacts): report required bytes for ASAR header limits (#7452) 2026-11-03 14:45:36 +08:00
Beqa d892e7861a fix(docs): drop duplicate native-inspection guide row (#7480)
Remove the incomplete Choose a guide duplicate that omitted kernel-modules.md so the docs index keeps one complete native-inspection entry.

Fixes #7405
2026-11-03 14:45:03 +08:00
morluto 9ebbe67e05 fix(ci): await website worker tests and format Wrangler config 2026-11-03 12:04:33 +08:00
Lewis WangandClaude Fable 5.1 b912ec184c fix(javascript): accept retained empty endpoint seeds (#6462)
Application analysis preserves the complete literal request endpoint,
including the empty string, and the event seed already accepts a
retained empty name. The endpoint seed still required a non-empty
value, so trace-javascript-semantics refused to select a retained ""
endpoint with invalid_request.

The endpoint seed now accepts any string and keeps exact matching.
Boundary-field seeds keep their minimum because boundary nodes are only
produced from method names.

Found while fixing #6458; the event half of that report landed in
cf5e4f6fa.

Co-authored-by: Claude Fable 5.1 <[email protected]>
2026-11-03 10:10:33 +08:00
morluto 193e01abb1 fix(kuna): keep timeout recovery from changing workers (#8687)
* fix(kuna): keep timeout recovery from changing workers

* test(kuna): verify timeout recovery with real provider
2026-11-03 10:10:11 +08:00
morluto d496d803a4 fix(kuna): give actionable lookup recovery (#8656)
* fix(kuna): give actionable lookup recovery

* fix(kuna): keep lookup advice operation neutral

* style(kuna): apply repository formatter
2026-11-03 09:50:10 +08:00
morluto 6259ac41a4 fix(mcp): guide target recovery for scoped tools (#8659)
* fix(mcp): guide target recovery for scoped tools

* test(mcp): cover unscoped target recovery

* fix(mcp): warn before scoped session restart

* refactor(mcp): own scoped target recovery guidance

* fix(mcp): include unknown history in restart guidance
2026-11-03 09:49:49 +08:00
morluto 79766ca96a fix(website): identify all public deployment verification requests 2026-11-03 09:41:49 +08:00
ShuaixinHuang 22c493ad5c fix(native): preserve implementation file locations (#8667)
fix(native): keep UI handler offsets distinct from method records
2026-11-03 09:38:12 +08:00
ShuaixinHuang b7911b2d30 fix(evm): explain limiter configuration and access failures (#8669) 2026-11-03 09:38:01 +08:00
JANG BYUNGKUN 6442ea02cb feat(binary-layout): admit ELF64 AArch64 and name the refused host (#8711)
* feat(binary-layout): admit ELF64 AArch64 and name the refused host

inspect_binary_layout refused every AArch64 ELF, and on a non-x64 host the
refusal read "supports Linux x64 only", which looked like a target refusal
(#8709 ran on arm64 WSL and suspected an e_machine check).

The pwntools adapter now admits little-endian ELF64 AArch64 EXEC/DYN/REL
objects alongside the existing profiles, pwntools must report aarch64/64/
little, and the Evidence subject names arm64. The host refusal now names the
detected platform/arch and says the target was not read; the host check
itself is unchanged.

verify:binary:layout cross-compiles an AArch64 executable and relocatable
object (never executed) and checks sections, symbols and relocations
against GNU readelf, decoding ELF64 r_info as a 32-bit type under a 32-bit
symbol index. The real-binary-layout workflow installs the toolchain.

Refs #8709

* test(binary-layout): refuse RISC-V as the unsupported machine and assert the host refusal path

verify:binary:layout used e_machine 183 (AArch64) as its unsupported
target, which this branch now admits; use EM_RISCV (243) instead.

The unsupported-host provider test now asserts the refusal names this
host's platform/arch and says the target was not read, so reverting the
decoder to the old message fails it.
2026-11-03 09:37:41 +08:00
morluto eef8f44003 feat(website): publish agent discovery and Markdown responses 2026-11-03 09:33:41 +08:00
ShuaixinHuang 510e83eadc fix(firmware): recommend the operation-specific engine (#8668) 2026-11-03 09:17:33 +08:00
2d11fd92d2 fix(javascript): reconcile Node targets with entry assets (#8670)
* fix(javascript): reconcile Node targets with entry assets

* test(inspector): read reconciled evidence payload

* Update tests/boundary/inspector/javascriptRuntimeNodeTarget.test.ts

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* test(inspector): read the workflow evidence payload

* Update tests/boundary/inspector/javascriptRuntimeNodeTarget.test.ts

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* test(javascript): restore Evidence workflow result access

---------

Co-authored-by: morluto <[email protected]>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-11-03 08:54:39 +08:00
morluto 9bea563e30 fix(kuna): qualify parallel project pseudocode (#8694)
* fix(kuna): qualify parallel project pseudocode

* test(kuna): cover parallel caveats through MCP
2026-11-03 03:30:19 +08:00
morluto e749160ab0 fix(mcp): qualify string byte-read guidance (#8704)
* fix(mcp): qualify string byte-read guidance

* fix(mcp): avoid implying complete string references
2026-11-03 03:29:59 +08:00
morluto 920d634e83 ci: exempt designated authors from readiness declaration 2026-11-03 02:18:55 +08:00
morluto 55078b9f57 feat(mcp): page retained incoming reference addresses (#8591) 2026-11-03 02:13:21 +08:00
morluto 75c3cab540 fix(kuna)!: separate verification policy from analysis identity (#8699)
* feat(kuna): verify release 1.989

* fix(kuna)!: separate verification policy from analysis identity

* docs(kuna): show historical evidence recovery

* test(kuna): cover verification warnings through MCP
2026-11-03 01:34:40 +08:00
morluto 3c05b4d3fe fix(ci): remove undeclared dependency and handle PTY chunks 2026-11-03 01:10:56 +08:00
morluto 2cb29693c3 fix(ci): preserve release proposal overflow notes 2026-11-03 00:21:16 +08:00
Kaoru0822andClaude Opus 5.5 26c3e9e413 fix(native): select off-screen windows so the UI lane can complete (#8654)
* fix(native): stop calling an off-screen window absent

The native UI helper looked up its selected window with
`.optionIncludingWindow`, which returned nothing while that window was off
screen. An existing window owned by the selected process was reported as
`window-mismatch`, before any permission check, so the verifier could not reach
its permission boundary. Select from all windows instead; absent and foreign
windows still fail.

The verifier's changed-bytes check also read `error.message`, but since
cc90771af a selection failure carries its helper code in `error.issues`, so the
check could not pass. Read the issue. Together these let the permissions lane
complete on main.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* test(native): own UI test processes from the moment they launch

The off-screen test registered cleanup only after parsing the ready record, so a
startup failure left `/usr/bin/open` and its app running. The lane test killed
only the verifier on a timeout, leaving its launcher and detached fixture alive
until they expired.

Register the off-screen owner immediately after the launch; the app inherits the
run token, so the sweep needs no ready record. In the lane test, every stand-in
records itself and leaves on one release the test's cleanup awaits.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-11-02 23:57:32 +08:00
Kaoru0822andClaude Opus 5.5 144843ccc2 fix(debugger): let REA exit when a debugger could not be stopped (#8652)
* fix(debugger): let REA exit when a debugger could not be stopped

`rea debug-with-gdb` and `rea debug-with-rizin` printed cleanup_incomplete
and set exit status 1, but the retained child and its pipes kept the event
loop alive, so the process never exited. A stdio MCP server whose client
disconnected hung the same way.

closeOwnedDebuggerSessions is the owner both managers finish through. It now
unreferences retained sessions, as supervised providers already do after
incomplete cleanup, restores the references on retry, and names the retained
process IDs. The one-shot commands report those IDs, since no later command
can retry. Debugger ownership, signals and detach behavior are unchanged.

GDB cleanup requests also no longer hold the host until their 120 s command
deadline, because cleanup already bounds its own wait.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(debugger): name retained debuggers when an MCP server shuts down

An MCP server that exits with a retained debugger logged only "REA provider
cleanup failed" and "could not close cleanly", so nothing named the process
left for the caller to check. The PIDs were on OwnedDebuggerCleanupError, but
each shutdown reporter read only the outer aggregate's message, and the debug
failure_cause description dropped the field too.

The failure type now lives in its own dependency-free module with one reader
that walks composed failures. The shutdown report, the server's onclose log,
the debug description and the one-shot CLI all use it, and the shutdown report
writes the same remediation the CLI returns.

The fake debuggers now model each request exactly, with real reply shapes, and
a request they do not model fails the test.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* test(debugger): compare retained-debugger guidance with its formatter

The new checks froze the wording "Check process ... and its target".
docs/testing.md asks guidance checks to compare with the owning formatter,
alongside independent checks of the facts. They now do, and each still proves
the process ID is named, because a formatter comparison alone would pass if the
formatter dropped it.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-11-02 05:16:34 -08:00
Kaoru0822andClaude Opus 5.5 4a01211f11 fix(javascript): keep static facts after semantic results use the parent budget (#8648)
Static checkpoints were admitted against the same budget as semantic and
application results, half of the parent's free heap. Once those charges used
it, the next checkpoint stopped the analysis and every remaining source lost
its static facts, with most of the parent heap still free.

Semantic and application results still stop at that budget. Static checkpoints
may now continue to three quarters of the free heap while the live heap also
has room for another copy of what it holds. Without that room, admission falls
back to the result budget. The live condition is needed because the limit V8
reports counts young-generation capacity that retained objects cannot use:
without it, the parent aborted on heap exhaustion at small heap limits.

Reserved relationships are given up only when the larger share runs short. A
stop still fails with a typed error, which now reports the limit that applied,
what was available, the live heap in use and what the checkpoint needed.

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-11-02 05:16:16 -08:00
Kaoru0822andClaude Opus 5.5 6a0ccf7eeb fix(doctor): report provider settings a client registration does not record (#8645)
* fix(doctor): report provider settings a client registration does not record

Provider checks read the terminal running doctor, so a registration with no
env block looked ready while its server reported not_configured. Compare what
an available provider relied on with the registration's own file and report the
setting names in a non-blocking registration_environment check. The client's
effective environment stays unknown, so health and the exit status are unchanged.

Fixes #8202

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* test(doctor): write the Cordis fixture at its literal path

The test resolved the path through supportedClients, whose default environment
argument CodeQL traces to the fixture's writes even though the test passes {}.
It only needed the path, so spell it like the other fixtures in this file and
drop a leftover directory it never used.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-11-02 05:14:50 -08:00
Kaoru0822andClaude Opus 5.5 710fad8acf feat(evidence): discover missing Evidence links without transferring records (#8633)
* feat(evidence): discover missing Evidence links without transferring records

An agent could only learn that a retained record's source was absent by reading
complete records or by having a downstream analysis refuse. The summary said
nothing about links, and complete-record retention was easy to mistake for
source closure.

- Add detail: links to get_evidence_bundle and CLI inspect-evidence-bundle:
  paged link rows with retained or missing status and no record payloads, for
  selected records against the whole session or file.
- Add retained-closure traversal, the walk composed workflows require, reporting
  complete or incomplete. Direct reads make no closure claim.
- Count direct and missing direct links in the summary.

Fixes #8454

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(evidence): count a repeated Evidence link once in every delivery

The Evidence schema does not deduplicate evidence_links, so the summary counted
a repeated link twice while link rows and counts reported it once. Both now read
one helper for a record's distinct direct links.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* test(evidence): validate link delivery against the advertised schemas

The test never called tools/list, so neither the shared validator nor the MCP
client checked its replies against what each profile advertises. A schema that
rejected the links result could have passed. Fetch the advertised definition and
validate every request and reply against it.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-11-02 05:14:34 -08:00
Kaoru0822andClaude Opus 5.5 93b8b82db7 feat(native): deliver batch decompile as a retained summary (#8631)
* feat(native): select retained batch decompile results through analysis views

A completed batch_decompile could only be read whole. When it did not fit the
MCP response budget, or an agent wanted one function, inspect_analysis_view
refused the parent as unsupported and the only recovery was exporting the
session.

- Own the batch result schema in the domain, replacing the contract's copy and
  the workflow's hand-written item types.
- Add summary and batch-decompile views: body-free rows keyed by original index
  with exact filters and opt-in bodies, and UTF-16 pages of one body.
- Report the decompiling provider from the upstream analysis profile, since the
  Evidence provider is the composing workflow.
- Keep batch limitations visibly aggregated rather than attributing them to one
  successful item.

Fixes #8498

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* feat(native): deliver batch decompile as a retained summary

A batch that fits still floods the caller's context with every body, and one
that does not fit only named an export. Selecting a body that cannot fit named
an export too, even though its text pages can.

- Accept detail: summary on batch_decompile: retain the complete batch first and
  return its counts, identities and sizes. The default stays complete, and the
  selection never reaches the Evidence parameters.
- Offer a ready-to-call body-free first page when a complete batch overflows.
- When a selected batch page overflows, continue from the same retained parent
  with fewer rows, then with one body's text pages.

Refs #8498

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(native): count every unavailable entry label in a batch summary

label_unavailable only counted failed lookups, so a batch whose provider
answered null for every label reported zero. The identity contract defines a
null name as an unavailable label in both cases.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(native): continue an oversized batch view from the caller's own source

A batch view read from an Evidence file or inline record advertised a
retained-evidence continuation for a parent the session never retained, so
following it failed. A view's continuation now repeats the caller's source and
only narrows the selection. An inline record offers none, because it cannot be
repeated within the reply it overflowed. Non-batch views skip the selection
parse, since delivery asks about every view it returns.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* test(native): require advertised batch schemas through the shared validator

A local validator map dropped any tool without an output schema, so removing an
advertised schema would have left these contract tests green. Require both named
schemas and validate through createMcpSchemaValidation, as docs/testing.md asks.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(native): offer file view continuations on servers that retain nothing

A fixed-target server reads an Evidence file through inspect_analysis_view but
records nothing, so its overflow skipped the continuation and returned generic
CLI guidance. A view's continuation names the caller's source and needs no
session, so it no longer depends on recording. Retained references still do.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-11-02 00:50:42 -08:00
Kaoru0822andClaude Opus 5.5 3f4aff2f22 fix(cli): keep captured source Evidence when one-shot delivery never commits (#8632)
* fix(cli): keep captured source Evidence when one-shot delivery never commits

A completed observation that was cancelled at final close, failed cleanup, or
never reached bundle commit returned its evidence links but dropped the source
records the invocation had already captured. The caller could neither read those
records nor tell whether they were missing, captured or written.

Report the closure at publishCliAnalysisEvidence, the shared boundary every
--evidence_output command uses. details.source_closure carries authentic captured
records within 64 KiB and explicit omissions: not_captured for a link this
invocation holds no record for, and not_inlined for a captured record outside this
response. A committed bundle keeps its receipt instead, nothing is written after
cancellation, and successful delivery is unchanged.

Fixes #8589

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(cli): keep the typed failure when collecting source closure cannot finish

A captured source with a data-sized evidence_links array made
pending.push(...links) exceed V8's call-argument limit. On final-close
cancellation or a refused publication that RangeError replaced the original typed
failure, which is the one outcome this delivery step exists to preserve.

Walk the closure through a Set, which is a deduplicating breadth-first queue, so
no link becomes a call argument and each link costs one lookup however often it
repeats. undeliveredSourceClosure now owns authentication and never throws: if
examining the closure fails, it reports collection_failure with the bounded error
type and an unknown closure state, and the original failure is still returned.

Fault the final-close tests on the scope the command gave to matching, after
matching completed, and assert that close was reached, so a moved close fails
instead of passing on an earlier one.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-11-02 00:50:19 -08:00
highmachs 8b89eee767 fix(native): retain ELF dynamic symbol visibility in export evidence and views (#8617)
Preserve the dynamic symbol's visibility attribute (default or protected) when extracting ELF dynamic exports, and expose it across complete Evidence and projected analysis views.

Historical Evidence records lacking visibility normalize safely to 'unknown', and projected views omitting visibility do not inject default values into partial row projections.

Fixes #8517
2026-11-02 16:47:37 +08:00
吴佳浩Alben 59f5502a40 fix(javascript): emit no event operation for a literal non-function listener (#8621)
* fix(javascript): emit no event operation for a literal non-function listener

`EventEmitter` validates a listener before registering or removing anything:

    if (typeof listener !== 'function')
      throw new ERR_INVALID_ARG_TYPE('listener', 'function', listener);

The throw is synchronous, before any listener exists, but the analysis reported a
`register` or `remove` operation for those calls.

Measured on Node 24.22.1:

    emitter.on(1)                      -> THREW ERR_INVALID_ARG_TYPE
    emitter.on("x", 1)                 -> THREW ERR_INVALID_ARG_TYPE
    emitter.on("x", null)              -> THREW ERR_INVALID_ARG_TYPE
    emitter.on("x", true)              -> THREW ERR_INVALID_ARG_TYPE
    emitter.on("x", {}) / ([])         -> THREW ERR_INVALID_ARG_TYPE
    emitter.once / addListener / prependListener / prependOnceListener /
    off / removeListener with a literal -> THREW ERR_INVALID_ARG_TYPE
    --- accepted ---
    emitter.on("x", () => {})          -> ok
    emitter.on("x", function () {})    -> ok

`rejectsEventListener` drops the operation only for an exactly readable literal
that cannot be a function. An identifier, a call result or a spread may still
supply a function and keeps its operation, and a class expression passes Node's
own `typeof` check.

`emit` and `removeAllListeners` are untouched: `emit` passes its remaining
arguments as data to listeners that already exist, and `removeAllListeners` takes
only an optional event name.

* fix(javascript): price a non-finite listener and keep a shifted spread

Two review findings on the event-listener guard.

**A non-finite numeric literal was missed.** Babel stores `1e999` as a
`NumericLiteral` holding `Infinity`, and the exact-value reader refuses to price a
non-finite number, so the guard never saw it:

    e.on("x", 1e999)  -> THREW ERR_INVALID_ARG_TYPE

A numeric literal is always a number and therefore never callable, so the guard now
decides on the literal node directly.

**A leading spread shifts the listener position.** `e.on(...args, 1)` does not pass
`1` as the listener: Node spreads the array and ignores the trailing argument once
the listener is satisfied. The guard read `arguments[1]` regardless, so it dropped a
real registration. It now leaves the position undecided when argument 0 is a spread.

Five regressions cover both forms; all five fail without the fixes.
2026-11-02 16:47:22 +08:00
Kaoru0822andClaude Opus 5.5 2ac6d3efe2 fix(process): re-read unreadable tokens before signaling and name the process (#8553)
* fix(process): re-read unreadable tokens before signaling and name the process

Darwin KERN_PROCARGS2 samples a process's argument length and stack top
before it reads the address space, and exec publishes the new length
before it copies the new strings out (xnu sysctl_procargsx,
exec_copyout_strings). A read during exec can return a zero-filled or
stale strings area, and exec keeps the PID and start time that bracket
the read. The final sweep already re-read such tokens, but the
pre-signal group check read each member's token once, so one transient
read of a live group member failed the whole cleanup.

Move the bounded re-read into one shared fact-reading helper and use it
for both the sweep and the pre-signal group check. The group check binds
re-reads to the generation it first observed, so a reused PID stays
unresolved; the sweep keeps classifying the PID's current process.

An unreadable-token failure now carries the native identity read after
the token and the summary names the candidate by its native name,
parent, group and start identity. The Darwin helper and Linux procfs
reader report the short native process name (p_comm/comm) for display.

Fixes #8337

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(process): refresh other pre-signal members after token re-read waits

The pre-signal group check re-read only members whose token was
unavailable, so a readable member kept the relation it had before the
retry waits. A member that moved to another process group during those
waits could be validated on its old group, and the old group signaled
while the moved member kept running.

After any retry wait, the shared re-read helper now reads every other
live pre-signal fact once more with the same generation binding. A moved
member then takes the existing changed-group path, which retains its
ownership evidence and fails cleanup. An inconclusive refresh replaces
the stale fact so it fails closed. Nothing changes when no re-read ran,
and the sweep keeps classifying current processes because every group
it finds is revalidated before signaling.

Also cover PID reuse that happens before the re-read starts, where both
re-read identities agree and only the first-read generation rejects the
replacement's token.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(process): keep known identity changes final across token re-reads

The pre-signal re-read treated a first read without a same-generation
identity as unbound and replaced it with the re-read. On Linux the first
read can already establish PID reuse (an ESRCH token whose exit check sees
another generation); the re-read then classified the replacement, so a
zombie replacement could let cleanup succeed.

A pre-signal check now re-reads only same-generation facts. Changed,
exited, exiting and foreign-token facts are final. Each attempt also
re-reads the group's owned members instead of one separate refresh after
the loop, so a member that moves groups or turns unreadable while another
waits is seen within the same bound. The caller's run token selects the
owned members, and the sweep keeps classifying current processes.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-11-02 00:21:23 -08:00
Kaoru0822andClaude Opus 5.5 365c3245e8 fix(setup): refuse Hopper on macOS below the release's minimum (#8548)
* fix(setup): refuse Hopper on macOS below the release's minimum

Hopper setup planning accepted any macOS 12+ host as supported, and the
installer never compared the host with the selected release. The current
publisher-signed Hopper 6.6.1-demo requires macOS 12.4 (feed
deployment_target and the app's LSMinimumSystemVersion), so setup on
12.0-12.3 installed an app the host cannot run.

The installer now takes the minimum from the release that owns it: the
feed's deployment_target before the package download, and the signed
app's LSMinimumSystemVersion (decoded in-process after signature
verification) before copying. A host below either fails with the new
incompatible_host_version code and an installer detail naming both
versions; an incomparable app value is a bundle failure. Planning keeps
REA's own macOS 12 floor but reports support as unknown, since the
release minimum is read only after approval, and the plan discloses the
check. Info.plist decoding is shared with app-bundle resolution.

Fixes #8502

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* test(setup): compare the macOS Hopper plan detail with its owning disclosure

The planning test froze an explanatory sentence from the setup plan. The
macOS install detail now lives in macHopperInstallDisclosure beside the
commands and origins the installer owns, the plan uses it, and the test
compares the plan with that constant while keeping its independent
support-state assertions. An installer test also pins that the signed
app's requirement is read only after both signature checks pass.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* test(setup): compare macOS Hopper refusal details with their owners

The installer's refusal details were template literals inside
installMacHopper, so tests copied whole sentences. The installer now
formats them through exported macHopperHostVersionDetail and
macHopperIncomparableMinimumDetail. Tests compare each diagnostic and the
setup remediation with those owners and SetupInstallFailure's recovery,
and separately assert the URL, versions, setting name, failure code and
request order. The Info.plist type refusal is checked by error type, key
and path instead of a sentence fragment.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-11-02 00:05:30 -08:00
Kaoru0822andClaude Opus 5.5 aba56df689 fix(ghidra): reject uncommitted handshake reports before the first query (#8630)
The handshake check ran only inside startSession, which a query reached
only when the profile committed a startup input, for PE or for an existing
project. Any other session started lazily inside callTool, so analysis seeds,
disabled analyzers, extension results, MIPS soft-float storage or a RISC-V
calling convention that the profile did not commit were never checked and
the query returned its result.

Every query now runs the checked start first. The client shares one startup
attempt, so there is still one launch, and a provider restarted within the
session is checked again. That leaves startSession as the only start in the
provider client, so the PE data-type path reuses its session info.

Recovery guidance is unchanged: the former eager-start condition now selects
only whether a completed handshake counts as a running provider.

Fixes #8612

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-11-02 00:00:24 -08:00
Kaoru0822andClaude Opus 5.5 dec8d2d588 feat(native): select retained batch decompile results through analysis views (#8629)
* feat(native): select retained batch decompile results through analysis views

A completed batch_decompile could only be read whole. When it did not fit the
MCP response budget, or an agent wanted one function, inspect_analysis_view
refused the parent as unsupported and the only recovery was exporting the
session.

- Own the batch result schema in the domain, replacing the contract's copy and
  the workflow's hand-written item types.
- Add summary and batch-decompile views: body-free rows keyed by original index
  with exact filters and opt-in bodies, and UTF-16 pages of one body.
- Report the decompiling provider from the upstream analysis profile, since the
  Evidence provider is the composing workflow.
- Keep batch limitations visibly aggregated rather than attributing them to one
  successful item.

Fixes #8498

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(native): count every unavailable entry label in a batch summary

label_unavailable only counted failed lookups, so a batch whose provider
answered null for every label reported zero. The identity contract defines a
null name as an unavailable label in both cases.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* test(native): require advertised batch schemas through the shared validator

A local validator map dropped any tool without an output schema, so removing an
advertised schema would have left these contract tests green. Require both named
schemas and validate through createMcpSchemaValidation, as docs/testing.md asks.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-11-01 23:58:54 -08:00
hhangandhhang 0e6943c9da fix(session): guide retained cleanup before reopening targets (#8628)
* fix(session): guide retained cleanup before reopening targets

* test(cli): isolate worker completion deadline from startup

---------

Co-authored-by: hhang <[email protected]>
2026-11-02 15:56:09 +08:00
hhangandhhang 20a282a181 fix(javascript): report SVG script references and skipped bodies (#8627)
Co-authored-by: hhang <[email protected]>
2026-11-02 15:55:56 +08:00
Kaoru0822andClaude Opus 5.5 f95bdbce76 fix(native): stop value traces at unconfirmed child cleanup (#8576)
After a validated seed, trace_native_values turned a cleanup-incomplete
failure from a call-target read or a followed callee dossier into a
plain-text unknown, kept dispatching reads, and returned successful
Evidence without the cleanup_incomplete status, cleanup reason or owned
resources.

The trace now classifies child failures with the shared stopsTraversal
predicate that call graph traversals use. A cancelled child still ends
the trace with its cancellation. A child whose provider cannot confirm
cleanup stops the trace: the provider's own error is the outcome, so its
resources and bound cleanup retry stay intact, and the work admitted
before the stop is returned as the trace's normal shape, with traversal
truncated by cleanup_incomplete and a located unknown at the stopping
read. That stop stays primary over a concurrent caller abort and over
the output-size check. Ordinary child failures remain unknowns.

MCP already retains such partials as workflow Evidence. The one-shot CLI
dropped them, so runDirectAnalysis now retains the partial workflow
Evidence on the failure through the same shared owner.

Fixes #8371

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-11-01 22:56:02 -08:00
gensokyo-dev 8250bf2320 perf(test): bind deferred runtime calls to advertised schemas (#8622) 2026-11-02 11:55:40 +08:00
morluto 00c4f44a1c fix(ci): repair verifier fixtures and formatting failures 2026-11-02 10:59:40 +08:00
gensokyo-dev d3ab0faf52 refactor(ci): parse readiness fences with a static expression (#8586) 2026-11-02 03:14:29 +08:00
gensokyo-dev 6d60db1951 fix(test): restrict storage fixture to loopback (#8582) 2026-11-02 02:52:20 +08:00
morluto 0354145a2a docs: clarify test audits and organize verification lanes 2026-11-02 02:21:40 +08:00
morluto 7bcefead69 test: strengthen and prune audited test coverage (#8573)
* test: strengthen and prune audited test coverage

* test: require delivered DeepSeek skill instructions

* test: keep schema fixture types private

* test: verify Gemini receives skill instructions

* test: bind Qwen skill evidence to its tool result

* test: report NativeAOT verifier checks precisely

* fix: verify Ghidra cleanup paths reliably

* fix: verify Command Code skill activation

* test: tighten cleanup and fixture checks

* test: verify packet capture evidence is recorded

* fix: report HTTP3 capture provenance accurately

* test: preserve resource coverage through MCP

* test: prune repeated assertions and retain cancellation identity

* fix: validate client verifier fixture paths

* test: skip Copilot verifier cases on Windows

* test: keep verifier setup target relative
2026-11-02 02:13:32 +08:00
gensokyo-dev ce9095d727 fix(test): contain browser fixture HTTP request targets (#8566) 2026-11-02 01:48:46 +08:00
ShuaixinHuang 46c13b9981 fix(electron): preserve runtime rejection and exception policies (#8542)
* fix(electron): preserve runtime rejection and exception policies

* fix(electron): retain rejection handlers for revoked proxies
2026-11-02 01:15:08 +08:00
Kaoru0822andClaude Opus 5.5 85013da0ce fix(cli): describe one-shot timeouts for an invocation whose session has ended (#8535)
* fix(cli): describe one-shot timeouts for an invocation whose session has ended

A one-shot CLI analysis that reached a provider deadline told its caller
that the Ghidra session remained usable and reported a runtime-editable
limit applying to the next operation, although the command had already
released its provider session and runtime.

The shared error projection now takes an explicit caller lifetime.
Providers keep guidance that holds only for a continuing caller (session
reuse, same-session runtime updates) apart from the request outcome, and
the presentation adds it only for a continuing caller. One-shot owners
(runDirectAnalysis, runProviderAnalysis, finishAnalysisCommand and the
JavaScript application command) project for the invocation: details.limit
reports scope rea-invocation, applies_at next_invocation and the launch
environment seed, and the remediation leads with that setting for a new
invocation. MCP output keeps its runtime-update and same-session guidance.

Covers the Ghidra decompile timeout, decompiler failure, regex stack
exhaustion and startup timeout, the shared regex deadline, the Kuna
decompile timeout and the Hopper startup timeout.

Fixes #8311

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(cli): project composed child failures for the caller lifetime

Cleanup composition embedded its primary and cleanup failures as
continuing-caller projections, and baked the primary's message and the
cleanup's remediation into the composed error. A one-shot command whose
Ghidra startup timed out while shutdown also failed therefore still
reported a rea-runtime limit and update_analysis_settings in
details.diagnostics.primary_error.

childFailureDiagnostics now snapshots each embedded child at composition
for both lifetimes when its projection depends on the caller, so cleanup
history keeps its composition-time state and the final projection selects
the caller's form. composedCallerGuidance re-attributes message and
remediation with their continuing-only parts kept separate. Error copies
and redaction keep the invocation diagnostics. The Hopper startup timeout
now carries its continuing guidance on the error like the other producers.

Invocation limits keep agent_editable and rea_restart_required, with
values that hold for a new invocation, so only scope and applies_at
change and environment is added.

In existing-project mode a decompile timeout or a cancelled session now
says the next session copies and opens REA_GHIDRA_PROJECT_PATH again
rather than repeating import and analysis.

Tests compare delivered guidance with the owning formatters and constants
instead of freezing prose.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-11-01 08:57:32 -08:00
Kaoru0822andClaude Opus 5.5 cbc69537cd fix(discovery): carry provider repair instructions into capability discovery (#8532)
* fix(discovery): carry provider repair instructions into capability discovery

Provider readiness reported only a failure description, so capability
discovery copied that description into tool_availability remediation and
dropped repairs the owners already had: the JEB discovery-deadline setting,
the ADB/Apktool execute-permission repair kept in diagnostics, and the IDA
and Kuna configuration recovery.

Unavailable ProviderAvailability now requires the owner's remediation
next to its reason, and readiness-derived unavailable capability
descriptors carry it too. Every producer supplies it (ADB, Apktool, JADX,
JEB, Ghidra, Hopper, IDA, Kuna, pcap, owned-process prerequisites, profile
rejection); the inventory uses it verbatim, falling back to the reason
only for static capability facts that declare no repair. ADB/Apktool
configuration failures own remediation as a field instead of diagnostics,
and execution refusals project it as remediation.action. JADX doctor reads
the availability's remediation instead of re-deriving it.

binary_session complete candidate availability and unavailable capability
groups expose remediation (CLI capabilities/providers too); summary omits
it and lists it in omitted_fields. The superseded IDA
configuration_remediation and windows_native_failure.remediation
diagnostics are removed.

Fixes #8506

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(discovery): keep owner repairs through provider selection and capability maps

Address review findings on the readiness-remediation contract:

- Provider selection rejections carry the availability owner's
  remediation, CLI/MCP error details project it, and an explicit
  selection refused by readiness uses it as remediation.action. IDA's
  unreadable registration and Ghidra's Windows readiness repair no
  longer degrade to generic selection advice now that the diagnostics
  copies are gone.
- Ghidra's Windows readiness and existing-project capability maps carry
  the same repair as the availability.
- pcap readiness keeps the limiter owner's repair (now a typed
  remediationAction) and falls back to Wireshark guidance only when the
  failure has none.
- ADB/Apktool repairs are owner constants that are complete sentences.
  The execution refusal keeps its previous message-plus-repair recovery,
  which the compiled CLI contract asserts.
- Regression tests compare with the owning formatter or constant and
  add public MCP and CLI coverage for an explicit IDA selection.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-11-01 08:57:11 -08:00
Kaoru0822andClaude Opus 5.5 eb39a07966 feat(evidence): read selected complete records without the whole bundle (#8528)
* feat(evidence): read selected complete records without the whole bundle

get_evidence_bundle could discover a record by exact ID, but reading it
required the complete canonical bundle: complete + filters was rejected,
and inspect_analysis_view refuses parents without a view, such as
annotations, pseudocode sources and native traces.

- Add detail: records, a page (offset, limit; default 0, 8) of the
  complete matching records in canonical bundle order. Records keep their
  original IDs, subjects, providers and readback; missing_evidence_links
  names direct links of the returned records that are not retained.
- The selection is labelled as historical and not an importable bundle;
  complete keeps returning the canonical bundle, and offset/limit
  require detail: records.
- Filters keep one owner shared by summary and records delivery, for the
  MCP session and the CLI's inspect-evidence-bundle.
- An oversized record page now advises a smaller limit or one
  evidence_id; whole-bundle overflow guidance mentions record reads.

Fixes #8307

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* test(evidence): compare record-selection guidance with its owners

Assert the record-selection limitations and oversized-page guidance
against their owning constants, and check the reported limit facts
separately, instead of pinning explanatory phrases.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-11-01 08:56:58 -08:00
Kaoru0822andClaude Opus 5.5 4b840aa1fb fix(setup): report the failed Hopper request URL and HTTP status (#8526)
* fix(setup): report the failed Hopper request URL and HTTP status

Hopper download callers collapsed every transport failure into a bare
`ok: false`, so HTTP 404 and 503 from the macOS feed and DMG, the Linux
publisher feed, the public key and the launch-time package download produced
identical generic advice without the failed URL.

The shared downloader now returns a typed failure (url plus http_status,
timeout, network, response_too_large or url_refused) and keeps caller
cancellation as a rejection. Installers attribute it to the request
(release_feed, package or public_key); HopperReleaseError carries it, and
setup projects it once into `download` and a remediation naming the URL,
outcome and next step. Replaces the Linux-only "Package download returned
HTTP" special case.

Fixes #8503

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(setup): refuse credentialed Hopper URLs and keep known HTTP outcomes

Native Fetch rejects a URL with userinfo using a TypeError that echoes the
whole URL, so a credentialed redirect reached `download.detail`, setup
remediation and publisher messages as a network failure. The downloader now
refuses request and redirect URLs carrying credentials as
`url_refused: credentials` before requesting them, and reports the caller URL
through the shared userinfo-removing `sanitizeBrowserUrl`.

Releasing an unadmitted body no longer replaces the outcome it was released
for: an errored (truncated) body after HTTP 503, or a failed cancel at the
size limit, keeps `http_status` or `response_too_large`.

Tests compare delivered remediation with the owning formatters and assert
request, URL, status and recovery class independently.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-11-01 08:56:38 -08:00
Kaoru0822andClaude Opus 5.5 423059a4de feat(javascript): page retained feature traces through analysis views (#8522)
* feat(javascript): page retained feature traces through analysis views

A bidirectional trace_application_feature result can exceed one MCP
response, and inspect_analysis_view refused trace parents as unsupported,
so the only recovery was exporting the whole session.

- Add a shared feature-trace view owner: a summary with the trace's
  identity, producer counts and coverage, and pages of paths, nodes,
  edges, seed_matches and native_handoffs in retained order with exact
  filters. Node rows are compact unless observations are requested.
  Page coverage never upgrades trace coverage.
- Accept an optional MCP view on trace_application_feature that retains
  the complete trace before returning the selected facts; the CLI keeps
  complete JSON and reads saved traces with inspect-analysis-view.
- Offer a ready-to-call first paths page when a retained complete trace
  exceeds the MCP response budget.
- Share the distinct authority/state/confidence basis helper with
  module references.

Fixes #8296

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(javascript): name the retained parent when a selected view overflows

Review follow-up for the feature-trace views.

- Share one delivery owner between trace_application_feature and
  compare_source_to_bundle: stage the selected view against the
  completed parent, keep the established contract that a cancelled
  request commits no session facts, then commit sources and the parent.
- A selected view that exceeds the response budget now references the
  retained parent and offers the same selection at half its limit,
  instead of naming the view record, which cannot be a view parent.
- Cover two native handoffs paged one at a time, the oversized selected
  page, and cancellation after the trace completes.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(javascript): bind feature-trace pages to their collection rows

Advertise each feature-trace page with exactly its own row schema, so a
paths page cannot validate with node, edge, seed-match or handoff rows.
The native-trace view in #8533 received the same review finding; the two
sibling views now share one strict page contract.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* test(javascript): mix valid rows from another collection into a trace page

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-11-01 08:56:05 -08:00
Kaoru0822andClaude Opus 5.5 44b6384009 fix(native): find procedures by the secondary names exact lookup accepts (#8520)
* fix(native): find procedures by the secondary names exact lookup accepts

search_procedures matched only each procedure's primary name, while exact
lookup also accepts Kuna inventory aliases (versioned-symbol and generated
sub_ names) and Ghidra symbols at an exact function entry. A search for a
name procedure_address resolves returned nothing, and trace_feature
silently missed the procedure.

Both providers now match the same names their exact lookup accepts. A
procedure still appears once under its primary name; the optional
matched_aliases field lists the secondary names that matched, so aliases
never become procedures of their own and list_procedures is unchanged.
The shared domain helper owns that row rule for Kuna and for the Ghidra
host-side Unicode folding pass. The Ghidra analysis profile records the
new search semantics so older snapshots cannot replay alias-blind results.

Fixes #8330

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(ghidra): match leaf spellings of procedure names in search

procedure_address accepts each entry symbol's leaf or namespace-qualified
spelling, but procedure search tested only qualified names, so a regex
such as ^audit_alias$ missed a secondary symbol ns::audit_alias (and ^same$
missed a primary alpha::same) that exact lookup selects.

Lookup and search now share one bridge definition of the names that select
a procedure, so they cannot drift. Matching secondary symbols are still
reported by their qualified names; because a leaf ends its qualified
spelling, the host's Unicode literal pass stays exact over those names.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-11-01 08:53:29 -08:00
Kaoru0822andClaude Opus 5.5 3f61303939 fix(session): clear provider-inferred unavailability after recovery (#8518)
* fix(session): clear provider-inferred unavailability after recovery

A provider adapter or process failure marked every routed operation of
that provider unavailable, but a later success cleared only the operation
that succeeded. Sibling operations, and composed tools built on them, kept
advertising the stale failure until each was retried individually.

Move runtime availability into one owner that records whether each entry
was observed for the operation or inferred from another operation's
provider-wide failure. A success clears its own failure and that provider's
inferred entries while keeping failures observed on other operations or
providers and declared refusals. Outcomes are ordered by dispatch to the
provider client, so an older in-flight success no longer erases a newer
failure while a request queued behind a failure still clears it.

Fixes #8404

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(session): keep newer successes when older failures complete last

Runtime availability kept one merged entry per operation and forgot
successful dispatches. An older provider-wide failure that completed after
a newer success re-inferred unavailability for siblings the newer success
had already proved working, and a superseded refusal could survive its own
later success.

Record only the newest failure and newest success dispatch per operation
and per provider, and project unavailability from them: an operation is
unavailable while its own newest failure is newer than its own newest
success, or, as inferred, while its provider's newest provider-wide failure
is newer than any success from that provider. The projection no longer
depends on reply completion order.

The recovery tests now bound held-reply acknowledgements, release and join
every held call in finally, and compare remediation with the owning
formatters instead of copying their wording.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-11-01 08:53:11 -08:00
Kaoru0822andClaude Opus 5.5 35ae7232bf fix(enhanced): stop call graph traversals at unconfirmed provider cleanup (#8516)
* fix(enhanced): stop call graph traversals at unconfirmed provider cleanup

A relationship read whose provider could not confirm cleanup of resources it
owns was projected into a graph node or call-path failure, the traversal kept
dispatching queued reads, and get_call_graph / trace_call_path returned
success with the cleanup_incomplete status buried inside the result.

Both traversals now read relationships through one shared owner,
readCallRelationships, which classifies a cancelled read or a read with
unconfirmed owned cleanup as terminal. On such a cleanup stop the workflow
returns the provider's own error as the outcome, so its cleanup owner, retry
binding and live resolution stay intact, and keeps the work admitted before
the stop as the workflow's normal result shape with a caveat naming the
stopped procedure and the unexpanded count. The composed-workflow layer
turns that admitted result into partial workflow Evidence on the failure,
and the MCP adapter retains it through the shared partial-error delivery.
Ordinary read failures still produce successful partial results.

Fixes #8369

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(enhanced): retain every carried partial Evidence on enhanced failures

Enhanced-tool failures used the shared partial-error delivery only when this
PR's workflow partial was attached. A failing read that already carried its
own partial Evidence, and analyze_function dossier failures, were projected
inline and never recorded with the session writer, so readback and bundle
export could not recover them. Every enhanced execution failure now goes
through one delivery owner that uses toPartialErrorToolResult with the
session writer; it still falls back to inline when no writer or no valid
Evidence exists.

Tests compare the traversal caveat against its owning formatter instead of
copying the sentence.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-11-01 08:52:53 -08:00
Kaoru0822andClaude Opus 5.5 76cd23d2e8 fix(process): remove unused Darwin helper cache type export (#8511)
Scheduled deep static checks on main fail knip because the
DarwinHelperCache type alias added in #8318 has no consumer. Remove
the alias; callers already use createDarwinHelperCache's inferred
return type.

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-11-01 08:52:42 -08:00
morluto 3e6a5cb20a fix!: preserve address context input errors (#8419)
* fix: retain address context cleanup failures

* fix!: preserve address context input errors

BREAKING CHANGE: Strict clients that cached the previous full inspect_address_context output schema must refresh tools/list and accept the typed error field on unavailable facets.

* test: use session contract for address context
2026-11-02 00:52:27 +08:00
morluto 1d343a88e5 feat(kuna): verify release 1.988 (#8509) 2026-11-02 00:25:06 +08:00
morluto df25bc8068 fix(ghidra): retain relocation symbol observations (#8531)
* fix(ghidra): retain relocation symbol observations

* test(ghidra): verify named relocation symbols through CLI

* fix(ghidra): clarify verification and supervise test commands

* test(ghidra): share load verification guidance
2026-11-01 23:53:56 +08:00
ShuaixinHuang aa48d494e9 fix(pe): enforce certificate name string alphabets (#8494)
* fix(pe): enforce certificate name string alphabets

* test(mcp): assert structured absolute-path diagnostics

* test(mcp): assert typed selector and stdio diagnostics

* fix(pe): bound IA5 name scans before validation
2026-11-01 21:24:01 +08:00
ShuaixinHuang 32c253ba31 fix(electron): preserve application argv during active capture (#8492) 2026-11-01 21:23:46 +08:00
morluto bafe3784cd fix(native): report dispatch target applicability (#8400)
* fix(native): report dispatch target applicability

* fix(native): clarify unavailable mode recovery

* test(kuna): avoid overlapping verifier imports

* docs(native): explain mode-specific discovery

* fix(native): preserve unbound provider recovery
2026-11-01 21:17:55 +08:00
morluto d684125eed docs(providers): verify prerequisite recovery and sibling failures 2026-11-01 21:14:27 +08:00
morluto 53363166f0 fix(evidence)!: validate native view parent predicates (#8451)
Reject native view sources whose predicate disagrees with the selected operation. Use the original predicate for existing Evidence; do not alter a record without recomputing its identity.
2026-11-01 20:27:34 +08:00
morluto 3842809c37 docs(setup): clarify Hopper failures and installer verification 2026-11-01 20:27:15 +08:00
gensokyo-dev e5e0dc5542 fix(ci): include hidden VitePress configuration in CodeQL (#8486) 2026-11-01 19:05:37 +08:00
2SinceJuly 1aa6beb5dc fix(decompile): name the differing resume identity (#8465)
Resume refused every identity mismatch with one generic message and
generic input-schema recovery. Report the differing target, profile,
layout, or provider facts with the journal and current values so callers
can choose the matching provider/profile or a new output directory.

Fixes #8349
2026-11-01 18:42:27 +08:00
gensokyo-dev 71746351d3 fix(ci): preserve the package verifier HTTP boundary (#8483) 2026-11-01 18:36:21 +08:00
morluto 293db94dd0 docs: prioritize product contracts over skill guidance 2026-11-01 18:29:34 +08:00
morluto 60c0cf977a docs: clarify recovery for valid lookup failures 2026-11-01 17:53:37 +08:00
morluto 7c755f2147 fix: retain address context cleanup failures (#8417) 2026-11-01 17:24:01 +08:00
morluto 1c1c8f6286 fix(snapshot): report binding mismatches (#8432)
* fix(snapshot): report binding mismatches

* test(snapshot): assert structured mismatch recovery

* docs(snapshot): explain profile comparison recovery

* fix(snapshot): include profile in recovery advice

* docs(snapshot): explain fresh-session profile discovery

* refactor(snapshot): derive admission from binding comparison
2026-11-01 17:23:11 +08:00
gensokyo-dev a6caa79bf4 ci(codeql): separate production scans from test fixtures (#8450) 2026-11-01 15:24:29 +08:00
gensokyo-dev e18594b247 refactor(composition): unify capability bindings and cleanup ownership (#8441)
Bind typed service creation, observed availability and retryable cleanup in one composition owner. Reuse selected CLI/MCP factories and canonical contract groups without copying provider operation inventories.

Closes #8429. Refs #740.

Validated paired real application workflows, actual local Codex CLI Terra/high use, complete Evidence and Unknown checkpoint parity, compiler-backed import boundaries, SDK schemas and final real mixed routing. Automated review findings were fixed at their producing owners. Existing exact-base CodeQL alerts are documented on the PR under the owner-authorized unrelated-check exception; no global alerts or security rules were changed.

Assisted-by: Suika (gensokyo-skills:suika-scatter-gather-planning)
2026-11-01 13:30:21 +08:00
morluto ea285bc842 fix(ghidra): respect Java Unix socket path limits 2026-11-01 06:13:48 +08:00
morluto 6e9ee97a69 fix!: preserve feature traces when owner lookup is unavailable (#8368)
* fix!: preserve feature traces when owner lookup is unavailable

* test: strengthen feature trace cancellation assertions

* fix: preserve feature trace provider readiness errors

* test: verify all feature trace reference sources

* test: verify IDA guidance at its owning boundary

* style: format feature trace regression
2026-11-01 05:38:40 +08:00
gensokyo-dev f72638f6b1 feat(investigation)!: retain records across engine cleanup (#8361)
Separate engine cleanup, investigation reset and terminal disposal in the shared application owner. Keep stable Evidence IDs and full Unknown revision history through engine close and target changes; serialize reset with all record writers and cleanup owners, optionally acknowledging a complete checkpoint before clearing. Bound retained representation without eviction.

Validation: 43 focused files / 533 distinct tests, source/type/lint/schema/documentation checks, nine genuine source cases across eight families, real Ghidra session identity and cleanup, and nine actual local Codex CLI terra/high runs with independent full-result and checkpoint audits. See docs/verification/investigation-lifecycle.md for measurements, partial attempts and verification limits.

Fixes #8347.

BREAKING CHANGE: close_binary retains investigation records. Use reset_investigation to clear them. Cross-process recovery requires an explicitly acknowledged Evidence bundle checkpoint.

Assisted-by: Suika (gensokyo-skills:suika-scatter-gather-planning)
2026-10-31 21:34:45 +00:00
Kaoru0822andClaude Opus 5.5 d54a4661da fix(ghidra): compare the DOS verifier's CLI window before its own rename (#8339)
The DOS verifier compared a fresh CLI session with an MCP session that
its address-boundary helper had renamed and restored, so the entry's
symbol source differed (imported vs user_defined). Compare the CLI with
the pre-edit MCP window, then assert the restoration changes only the
symbol provenance.

Fixes #8189

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-31 11:13:04 -07:00
Kaoru0822andClaude Opus 5.5 d2518a19f8 feat(native): label binary overview counts with their inventory scope (#8338)
* feat(native): label binary overview counts with their inventory scope

`binary_overview` described its counts as "exhaustive", and its result was
a pair of bare scalars. An agent could read them as a discovery of every
function and string in the file, or as comparable across providers. They
are exact totals of the rows each provider's inventories return.

The shared `projectBinaryOverview` now adds one provider-neutral
`inventory_coverage` record, so Ghidra's direct summary and the
listing fallback carry the same record:
`{ scope: "provider_inventory", discovery_coverage: "unknown" }`.
The record reuses the `discovery_coverage` vocabulary that
`list_procedures({detail:"summary"})` already reports, now owned by one
domain schema. The tool description states the scope and that counts are
not comparable across providers. Counts, segment omission semantics and
limitations are unchanged.

Fixes #8287

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(native): label overviews in the workflow so older snapshots stay valid

Analysis snapshots replay the projected inventory summary. A snapshot saved
before inventory_coverage existed would then fail the overview output schema.
The record describes what overview counts mean, whoever produced them, so
the binary_overview workflow now adds it on both its provider-summary and
listing paths. Provider values and projectBinaryOverview are unchanged, and
the composed overview's linked atomic record stays as the provider
reported it.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(native): refresh composed overviews from before inventory_coverage

Bump the binary_overview workflow revision to 5, so snapshot and CLI replay
recompute a revision-4 composed overview from its atomic record. Replay
would otherwise return the cached overview without its count labels. The
revision-4 case joins the existing refresh scenario. The atomic Evidence
is not rewritten.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-31 11:12:52 -07:00
Kaoru0822andClaude Opus 5.5 a36c9dd9c8 fix(kuna): omit Mach-O data symbols from the procedure inventory (#8335)
* fix(kuna): omit Mach-O data symbols from the procedure inventory

Kuna's `functions --json` registers every Mach-O export as a function
entry, so globals in `__DATA,__data`, constants in `__TEXT,__const` and
`__mh_execute_header` were listed, counted and decompiled as procedures.

The Kuna session now classifies each inventory entry with the target's own
section table, read by the shared `inspect_macho` load-command reader from
the header bytes kept while its private copy is hashed. Entries in sections
that declare no instructions, or in the header and load commands, are data:
listings and searches omit them and report per-region counts, and
procedure operations refuse them by name or address while `address_name`
and `xrefs` still accept them. Instruction sections, symbol stubs and
symbol-pointer import slots are kept; addresses outside every section stay
with an explicit limitation, an unreadable section table keeps the whole
inventory unclassified, and ELF inventories are unchanged. Composed
workflows such as `decompile_all` and `binary_overview` follow the listing.

Fixes #8168

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(kuna): keep an entry's classification caveat on direct answers

Resolution now returns the selected entry's Mach-O placement and the
caveat it leaves open, built by the same constructor as the listings: the
one-entry unplaced wording, or the unreadable section table's reason.
Pseudocode, assembly, analyze_function (including the dossier's own
limitations), procedure_address, call queries by name and address_name
carry it; code and import-slot entries carry none.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-31 11:12:41 -07:00
Kaoru0822andClaude Opus 5.5 8a6ddec83a fix(mcp): name the close_binary retry for retained cleanup (#8329)
* fix(mcp): name the close_binary retry for retained cleanup

A close_binary that retired its target but could not confirm cleanup
returned cleanup_incomplete with retryable: false and only the generic
"Review the reported local resources" remediation, although the session
keeps every failed owner and the next close_binary on the same connection
retries them. Agents could abandon the connection that owns the only retry.

The MCP close tool now passes its retry operation to the session. The
session binds that typed AnalysisCleanupRetry to a failed retirement only
while it retains the cleanup for its next close; the close tool binds it to
process-capture failures its own scope retains and to compositions whose
session part was offered. Projection then reports
recovery.kind=retry_cleanup and appends a conditional instruction: keep the
connection, let the condition settle or resolve it, call close_binary
without snapshot_path and check the result. The hint disappears once that
cleanup resolves. retryable keeps describing the failed operation.

One-shot CLI closes, open_binary, analysis tools and failed snapshot saves
(which keep the target open) offer no retry. Cleanup composition copies the
owner's remediation without a retry offer, so the text appears once.

Fixes #8211

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* test: derive close retry guidance from its owning formatters

The new close_binary retry assertions froze ProviderCleanupError's
explanatory sentence, so a harmless wording edit would break them. The MCP
test now compares delivered remediation with analysisErrorRemediationAction
applied to the delivered typed error plus cleanupRetryRemediation. The
domain tests use the same formatters, check that unbound and released errors
carry no retry text, and check that a bound composition adds the offer once.
Recovery fields, cleanup state and retry behavior keep separate assertions.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-31 11:12:30 -07:00
Kaoru0822andClaude Opus 5.5 c534e249d1 perf(process): reuse a verified Darwin ownership helper across processes (#8318)
* perf(process): reuse a verified Darwin ownership helper across processes

Every REA process on macOS compiled the Swift ownership helper into a fresh
temporary root and then paid macOS's first-launch assessment of that new
executable: 408 ms + 325 ms of a 1.4 s `rea inspect-macho /bin/ls`.

With the default xcrun compiler and module cache, the reader now keeps one
content-addressed copy under ~/Library/Caches/rea/process-helper/<key>/.
The key covers the helper sources, toolchain selection, architecture and
Darwin release. A copy is executed only after the cache root, entry and
files are verified as current-user owned, not group- or world-writable,
not links, and digest-matched to the entry manifest. Malformed entries
owned by the user are removed and rebuilt; linked or foreign entries are
left untouched and the per-process compile runs as before. Any cache
failure falls back to that compile, so the cache never fails an ownership
operation.

A miss compiles exactly as before, then publishes a copy through an atomic
rename, keeping the first concurrent publisher, and runs the shared copy.
That way its first-launch assessment is paid once. Per-run roots keep their
existing cleanup; published entries are never removed by close or exit.
An explicit REA_DARWIN_SWIFTC_PATH or REA_DARWIN_MODULE_CACHE_PATH keeps the
per-process compile and writes nothing to the cache. `rea uninstall
--purge-data` removes the namespace.

Fixes #2483

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(process): harden the shared Darwin helper cache

Address review findings on the shared ownership-helper cache:

- Verify every real parent of the cache root: each must belong to the user
  or root and not be writable by others (sticky root-owned directories
  excepted), so no other user can rename a verified entry. Execute through
  the real path.
- Open manifest and helper through the owned non-blocking regular-file
  reader, so a FIFO cannot block preparation, and retain handles whose close
  fails.
- Key entries on the swiftc and SDK that xcrun itself resolves, including
  their real paths, sizes and modification times. DEVELOPER_DIR, TOOLCHAINS,
  xcode-select and in-place updates therefore select new entries.
- Never remove or replace an invalid or published entry. Removal could race
  another process that has verified or is running that path, so such an
  entry falls back to the per-process compile.
- Give the cache a close() that retries retained staging removals and
  handle closes and reports ProviderCleanupError while any remain. The
  reader closes it with its per-run root.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(process): keep cache lookups owned and fall back from unlaunchable helpers

Address the second round of review on the shared ownership-helper cache:

- Pass the preparation signal to both xcrun toolchain lookups, and wait for
  every started lookup before reporting the first failure. Cancellation and
  close then stop a stalled lookup instead of waiting out its deadline.
  A failed or cancelled key computation is retried by the next lookup.
- When the verified shared copy cannot launch (EACCES, EPERM, ENOEXEC,
  ENOENT or an unrequested SIGKILL, for example from a noexec home volume),
  the reader stops using the cache. It reuses its own compiled helper or
  compiles one, and retries the read once.
- Anchor the cache's returned cleanup errors as cleanupRoot does, so every
  error returned while cleanup was pending resolves after a retry succeeds.
- Build the cache path with appendCallerPath, so a home with symlinks and
  `..` keeps the caller's meaning instead of being normalized lexically.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(process): keep concurrent fallbacks and dual cleanup failures

Each helper read now records before awaiting whether it launched the shared
copy, so a concurrent read's fallback cannot hide its own. When the per-run
root and the cache's retained resources both fail one close, the returned
error composes both. It stays incomplete until both owners recover.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(process): refuse shared helpers that an ACL lets others write

macOS ACLs can grant writes that the POSIX mode bits do not show. On
Darwin, lookup and publication list the real ancestors, the entry and its
files with `ls -lde`, and refuse sharing when any ACL entry allows a
write-class right. The default `group:everyone deny delete` entries on
macOS home folders are unaffected. The unlaunchable-helper test now removes
the execute bit from a verified copy, which refuses launch on both Linux
and macOS.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-31 11:12:02 -07:00
Kaoru0822andClaude Opus 5.5 41e7333cb5 feat(guidance): distinguish focused matches from broad parent sections (#8305)
* feat(guidance): distinguish focused matches from broad parent sections

Guidance search treated a parent section, including every descendant, as one
match. Terms scattered across unrelated subsections made a 49K-character root
the only result for "switch provider annotations", and every ancestor of a
focused hit was listed alongside it.

The section parser now records each section's own text (heading and body
before its first subsection) and its parent. Search keeps the same admission
rule but classifies each match by match_scope: "section" when every term is
in the section's own text, "subsections" when the terms are only spread
across subsections. Ancestors whose terms all sit inside one subsection are
no longer listed, section matches rank first, and subsections matches carry
own_text_terms plus bounded greedy pointers to the subsections holding the
remaining terms. Every match reports content_length so callers can judge a
read before making it. Read still returns the complete section.

Own text is tokenized once per request instead of each expanded subtree.

Fixes #8290

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* test(guidance): require the scattered-match checks to run

Count the subsections matches the real-bundle test inspects and fail when
none were checked, and document that a parent whose own text holds every
term stays listed beside its matching subsection.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-31 11:11:05 -07:00
morluto 60149282fc test: strengthen integration oracles and verification (#8295)
* test: strengthen integration oracles and verification

* test: bound lifecycle gates and strengthen workflow checks

* test: verify complete paged and extracted results

* test: preserve bytecode and cancellation evidence

* test: strengthen retained evidence and fixture cleanup

* test: require cross-projection bridge identities

* test: retain fixture owners through cleanup failures

* test: verify graph examples and project selectors

* test: tighten provider and lifecycle regressions

* test: consolidate hex coverage and bound cutter cleanup

* test: bound subprocess output waits and strengthen graph checks

* test: check mcp recovery and preserve cleanup owners

* test: verify structured mcp argument failures

* test: preserve artifact cleanup and gdb limit evidence

* test: preserve evidence identities and bound provider gates

* test: retain browser and comparison cleanup owners

* test: bound startup and strengthen fixture oracles

* test: bound lifecycle gates and verify recovered evidence

* test: verify public outcomes and lifecycle ownership

* test: assert precise evidence and release lifecycle gates

* test: verify selected facts and bound cleanup waits

* test: check retained evidence and decoded records

* test: verify final reports and retain cleanup ownership

* test: bound reviewed startup gates and retain cleanup

* test: verify PE errors and preserve MCP cleanup

* test: preserve MCP fixture cleanup ownership

* test: release provider gates before late startup
2026-11-01 00:15:28 +08:00
Kaoru0822andClaude Opus 5.5 2d5dec26c3 fix(ghidra): assemble the Swift verifier's unresolved-jump fixture before linking (#8325)
swiftc rejects assembly sources as inputs, so verify:ghidra:swift failed
during fixture preparation before any provider check. Assemble both
assembly fixtures to objects with xcrun clang and link only objects.

Fixes #8188

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-31 09:01:06 -07:00
Kaoru0822andClaude Opus 5.5 da0e9669a8 fix(decompile): keep provider no-pseudocode facts on batch items (#8323)
* fix(decompile): keep provider no-pseudocode facts on batch items

A batch_decompile item whose provider answered without pseudocode was
reported as unreadable output with doctor guidance, and the provider's
reason, such as a Kuna export failure for that entry, survived only in the
batch-wide limitations. The workflow read only the observation's result.

Read each item's complete observation. A null answer is now a
capability_unavailable item naming the provider, and every item answered
without usable text keeps that observation's own limitations, so a
batch-only Evidence export retains the association. Empty or non-text
pseudocode stays unreadable output; typed provider failures and successful
items are unchanged, and the batch Evidence still aggregates every caveat.
The batch workflow revision changes so earlier snapshots are not replayed
with the previous item semantics.

Fixes #8226

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(decompile): match null-pseudocode guidance to reported caveats

A null answer without caveats told the caller to read item limitations
that the batch omits when the list is empty. The guidance now depends on
whether that observation reported caveats, and says the provider gave no
further explanation when it did not.

The batch tests now select their contracts by name from the enhanced and
session families instead of the full catalog.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-31 09:00:48 -07:00
Kaoru0822andClaude Opus 5.5 73ff6ca0c8 fix(native): separate value-trace page omission from traversal truncation (#8309)
trace_native_values set one truncated flag for two different facts: the
returned page omits retained nodes, and a budget stopped or narrowed the
traversal. A last page of a complete trace and a node- or edge-capped trace
both reported truncated: true with next_offset null and no unknown naming the
cap, so a caller could not tell whether to follow pages, raise a budget, or
treat the examined graph as exhausted.

Results now carry page_truncated for page omission and a traversal object
that is complete, or truncated with the observed reasons in schema order:
max_depth, max_functions, max_call_sites, max_nodes, max_edges,
node_payload_bytes, provider_pcode_truncated and ambiguous_call_candidates.
truncated keeps its previous meaning as the union of both.

A new budget owner admits nodes and edges and records every cause together
with a located unknown, only after a budget rejects work; an exactly filled
budget is no longer truncation. Depth and ambiguous-candidate limits are
resolved after traversal, so a destination analyzed or queued through a
shallower path is no longer reported as depth-limited. Once an edge is
rejected, remaining call sites in that procedure are not resolved.

Fixes #8283

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-31 08:59:58 -07:00
Kaoru0822andClaude Opus 5.5 c287a73276 fix(ghidra): report unbacked file-offset addresses without schema repair (#8308)
* fix(ghidra): report unbacked file-offset addresses without schema repair

address_to_file_offset projected a well-formed address in uninitialized
or unmapped Ghidra memory as generic invalid input: the headline said the
arguments were invalid and recovery pointed at the input schema, while the
real reason survived only in a nested issue.

The bridge now gives each outcome its own code (address_unmapped,
address_uninitialized, file_mapping_absent, file_mapping_ambiguous,
file_offset_unrepresentable) instead of folding them into invalid_request.
The Ghidra projection keeps the bridge reason as the headline, points to
inspect_native_load_image, records the caller's exact address and the
file_mapping outcome, and scopes diagnostics to the request.
AnalysisInputError gains conformsToInputContract so a conforming request
omits recovery.kind=inspect_input_contract. Malformed addresses and other
bridge refusals keep their existing projection.

Fixes #8244

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* test(ghidra): match fixture block ends to bridge conventions

The fake bridge treated .text's exclusive list_segments end (0x1100) as an
inclusive bound, so address_to_file_offset mapped an address outside the
block. Give the fixture's blocks one owner (start and size) and derive each
reply from the production bridge convention: exclusive list_segments ends,
inclusive byte-search coverage, half-open containment for file offsets.

Add an MCP boundary test that reads the block edges from list_segments and
search_bytes, maps the last file-backed byte and refuses the first address
past each block with its outcome code.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-31 08:59:00 -07:00
Kaoru0822andClaude Opus 5.5 7c9ed94259 fix(ghidra): decide batch names independently of request order (#8306)
* fix(ghidra): decide batch names independently of request order

The Ghidra bridge applied set_addresses_names in JSON member order, each
address in its own transaction, so a reordered but equivalent map could give
a shared name, or a name released by another selected address, to a
different address.

The bridge now resolves every destination (namespace and leaf name) against
the names held before the batch. Addresses that request the same destination,
or a name that another selected address holds, are rejected without applying;
the remaining edits are independent and apply as before. Both naming
operations report each failure's canonical address, reason and related
addresses. The adapter projects the bridge reply to the unchanged boolean
results and keeps the reply as raw_result only when an address failed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(ghidra): report each contested batch name once

A batch conflict listed every other requester on each failure, so k
addresses competing for one name produced k(k-1) related addresses, and
the adapter retained the whole reply, booleans included, as raw_result.

The bridge now reports each contested destination once in a conflicts
table with its namespace, name, requesting and holding addresses, and
each conflict failure refers to it by index. Conflicts are ordered by
their first requester, so indexes do not depend on member order. Single
and batch naming share one planner and reply layout. The adapter
validates the table in linear time and keeps only failures and conflicts
in raw_result.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-31 08:53:11 -07:00
gensokyo-dev a989c0e458 refactor(evidence): unify retained inputs across domains (#8299)
Declare logical Evidence sources once and resolve exact retained inputs through
shared application workflows across 20 composition tools. Preserve domain-owned
authentication, source closure, caller choices, coverage and resource admission.

Validation includes public CLI/MCP regressions, local Codex terra/high trials
and fresh-import real-artifact workflows. Measurements, failed attempts and
coverage limits are recorded in docs/verification/evidence-input-composition-2026-10-31.md.

Fixes #8293.
Fixes #8281.

Assisted-by: Suika (gensokyo-skills:suika-scatter-gather-planning)
2026-10-31 14:45:21 +00:00
morluto 23de10bb28 fix: streamline tests and preserve Darwin process ownership (#8275)
* test: strengthen suite guards and regression oracles

* test: share JSON client registration contract

* test: reuse fixtures and reduce repeated setup

* fix(process): preserve ambiguous Darwin token ownership

* test(process): bound child readiness waits
2026-10-31 16:43:04 +08:00
Entrpiandmorluto b43c329df7 feat(artifacts): inventory and extract UF2 and DfuSe firmware containers (#8230)
* feat(artifacts): inventory and extract UF2 and DfuSe regions

open_binary refused UF2 and DfuSe firmware containers as an unsupported
format, so inspect_artifact and extract_artifact could not show which
bytes a drag-and-drop or DFU update writes at which addresses.

UF2 is recognized by a complete first block with all three magic
numbers. Valid main-flash payloads are joined into address-contiguous
regions per familyID, named family-0x<id>/0x<start>-0x<end>.bin, and
known IDs are named from a small table copied from Microsoft's
uf2families.json at a pinned commit. Numbering is counted per familyID
and block count: absent, repeated, out-of-order and out-of-range block
numbers are reported, and absent blocks leave coverage unknown.
Not-main-flash blocks are excluded and reported; file-container blocks
are reported and leave coverage unknown; invalid blocks and a partial
final block are excluded with their position. A rewrite of one address
with different bytes fails closed, naming both block spans.

DfuSe is recognized only by its prefix together with a DFU 1.1a suffix.
Each element becomes one region at its file range with its target's
alternate setting and name. Structural faults fail closed with a byte
offset; overlapping elements stay separate and are reported. A suffix
CRC mismatch follows integrity_policy: fail refuses with a dfu-crc32
integrity error, and record-and-continue records a root contradiction
that extraction also carries.

HEX, UF2 and DfuSe share one retained-region reader and one run
merger, so the HEX decoder and reader lose their private copies.

* docs(artifacts): document UF2 and DfuSe images and their esptool lane

Describe recognition, region names, metadata, numbering, failures, the
DfuSe suffix CRC under integrity_policy, and limits, and link the page
from the CLI and firmware guides and the agent skill reference.

npm run verify:uf2 runs esptool merge-bin --format uf2 on generated
inputs at known addresses, with and without MD5, with 256-byte
payloads, and on a concatenation of two chips' outputs. Inspection and
extraction through the built CLI must return every input at its address
under the familyID esptool itself reports.

* fix(artifacts): accept both DfuSe image size conventions

UM0391 calls DFUImageSize the file length, and dfu-util's dfuse-pack.py
writes that value, while ST's DfuFileMgr, MicroPython's dfu.py and pydfu
leave the 16-byte suffix out. The decoder accepted only the second form,
so it refused dfuse-pack.py output as malformed.

Accept either value and keep the declared one in image_size; any other
value still fails with the suffix offset and the file length. The tests
pin both producers' output for one image, CRC included, from their
build() functions transcribed and run under Python 3.

* fix(artifacts): keep reused UF2 block numbers from hiding missing blocks

esptool numbers every merged input from 0, so two inputs with the same
block count share one numbering group. A block lost from one input left
its number present in the other, and the file reported complete
coverage. Each block number now records the target addresses it was
seen at: equal address counts are complete copies, a resend to the same
address is one block, and unequal counts make coverage unknown with a
limitation and ambiguous_numbering_group_count. sequence_count becomes
numbering_group_count, and region block positions become
first_file_block and last_file_block.

Review follow-ups in the same change:
- compare_artifacts compares a DfuSe element's alternate setting and
  target name, so bytes moved to another device memory are changed;
- the shared reader keeps no region bytes outside its map after close;
- one domain type replaces four identical decoder failure types;
- the UF2 decoder returns a typed limit instead of throwing on bytes
  beyond its admitted size, and caps retained regions, not runs;
- DfuSe caps elements with bytes only, names empty elements and
  element-less targets, drops the constant bVersion and bcdDFU fields,
  and says whether the CRC matches when DFUImageSize fits neither
  convention;
- classification evidence carries a suffix only when the probe saw the
  end, and root classification reads the tail only for a DfuSe prefix;
- the UF2 domain region owns family_name, and the root records the
  family registry revision; the table's MIT notice is preserved;
- docs pin the producer revisions (dfu-util ee6b393d and f9a537c9,
  MicroPython cb99ca98), give the MD5 structure's real meaning, and
  describe container contradictions in the MCP contract guide;
- tests and the esptool lane add the equal-count inputs and a block
  lost from one of them, accepted boundaries beside each refusal, a
  DfuSe root larger than the classification prefix, and CLI refusals.

* feat(artifacts): name the UF2 families that LibreTiny builds for

LibreTiny writes UF2 for Realtek, Beken and Bouffalo Lab modules. The
Microsoft registry revision REA already cites (cf3bd7f9) lists eight of
those families: RTL8710A, RTL8710B, RTL8720C, RTL8720D, BK7231U, BK7231N,
BK7251 and BL602. Their regions now carry those names. LN882H, which
that revision omits, keeps its familyID with no name.

* fix(artifacts): count DfuSe declarations without bytes instead of listing them

Empty DfuSe elements have no entry, so DFUSE_MAX_ELEMENTS does not cap
them, and the target walk kept a description string for each one. At 8
bytes per element, a file under the 128 MiB ceiling can declare about
16.7 million of them. The walk now counts every declaration without bytes
and builds only the first NAMED_ELEMENTS descriptions, which is all that
the limitation shows. The limitation text and empty_element_count do not
change. The guide now says that both DfuSe limitations name the first four.

On this host, decoding 4,000,000 empty elements (a 32 MB file) ran out of
memory under a 128 MB V8 heap before this change, and it peaked at 1.59 GB
RSS with the default heap. It now decodes in 114 ms with a 220 MB peak.

---------

Co-authored-by: morluto <[email protected]>
2026-10-31 16:27:00 +08:00
22e5aa97af feat(artifacts): inspect classic iNES container regions (#2816)
* feat(artifacts): inspect classic iNES container regions

* fix(artifacts): preserve iNES source disappearance as integrity failure

* fix(artifacts): revalidate iNES source before parsing header

* fix(artifacts): validate admitted roots before classification

* fix(artifacts): normalize source loss during root hashing

* fix(artifacts): bind iNES extraction admission to inventory

* test(artifacts): clarify non-executing inspection wording

* fix: preserve binary plist detection with extended magic probe

* test(browser): wait for popup document loads before capture

* fix(artifacts): honor cancellation across source validation

* fix(artifacts): prefer artifact magic over implicit database suffix

* fix(artifacts): select extraction readers from scanned format

* fix(artifacts): complete prefix reads and share ZIP format admission

* fix(browser): budget scenario cleanup for content drain and shutdown

* test(artifacts): admit valid universal inputs before routing

* test(artifacts): verify iNES workflows on macOS

* test(ci): own iNES macOS coverage and portable fixture paths

* fix: preserve cleanup retries and avoid duplicate inventory parsing

* fix(contracts): share ZIP target formats across MCP lifecycle schemas

* fix(target): preserve typed cancellation during descriptor admission

* refactor(artifacts): use JSON-safe inventory producer output directly

* test(artifacts): share file read setup and satisfy CI checks

* fix(artifacts): classify changed admission as integrity failure

* ci: run Mach-O summary delivery coverage on macOS

* test(javascript): align parameter provenance with main contract

* test(browser): await discoverable fixture pages before runtime verification

* fix(artifacts): pass target metadata to format detection

* fix(artifacts): bind extraction to opened target

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

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

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

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

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

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

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

---------

Co-authored-by: 吴佳浩 <[email protected]>
Co-authored-by: morluto <[email protected]>
(cherry picked from commit df3d96da80)

* fix(artifacts): bind session inventories to target identity

* test(browser): bound fixture readiness acknowledgements

---------

Co-authored-by: 64579525 <[email protected]>
Co-authored-by: 吴佳浩Alben <[email protected]>
Co-authored-by: 吴佳浩 <[email protected]>
2026-10-31 15:49:16 +08:00
Entrpi e669365c3b feat(ghidra): admit RISC-V ilp32 ELF with a derived calling convention (#8229)
* feat(ghidra): record the RISC-V float-ABI calling-convention decision

Decision D3 (#7477), provisional: admit ELF32 soft-float (ilp32) RISC-V
images through a lane that keeps the pinned RISCV:LE:32:default language and
corrects the calling convention in the bridge.

This commit is the contract only; admission is unchanged.

- The domain owns the e_flags float-ABI vocabulary and parses
  .riscv.attributes and Tag_RISCV_arch into a declared, absent or
  unavailable ISA declaration.
- The Ghidra profile owns the lane table (class and float ABI), lane ids
  built from fields, the ilp32-prototype-v1 policy and its __rea_ilp32
  prototype, and the extensions Ghidra 12.1.4 decodes with p-code.
- The session handshake accepts the bridge's calling-convention report.
- docs/ghidra-riscv.md keeps the decision record and per-ABI policies for
  ilp32 and ilp32f; a test keeps it in step with the tables.
- tests/conformance/c/riscv-float-abi.c is the source the policies were
  measured on.

* feat(ghidra): admit RISC-V ilp32 ELF with a derived calling convention

ESP32-C2, C3, C5, C6, C61, H2 and H21 builds are ELF32 soft-float (ilp32)
images, which the RISC-V lane refused at its double-float check. Ghidra
12.1.4's generic RV32 compiler specification models ilp32d, so its stock
prototype would read float and double arguments from fa0-fa7.

- Target resolution reads the one SHT_RISCV_ATTRIBUTES section through the
  same bounded ELF declaration reader as range validation, and records
  Tag_RISCV_arch as declared, absent or unavailable.
- Admission selects a lane from the class and float-ABI table. The soft-float
  lane also requires an RV32I Tag_RISCV_arch that declares only extensions
  Ghidra decodes with p-code; the refusal names any others (Zcb, Zcmp, Zcmt,
  Zb*, xesp*).
- Soft-float profiles commit float_abi, arch, riscv_calling_convention
  ilp32-prototype-v1 and the lane elf32-exec-le-soft-float-generic-v1.
  Double-float profiles are unchanged.
- ReaGhidraRiscvCallingConvention.java runs after analysis. It derives
  __rea_ilp32 from the default prototype without floating-point entries and
  rules, with fs0-fs11 killed by calls, self-checks its storage and assigns
  it to every non-thunk function, external function and function
  definition. Its report is written last.
- The provider client requires that report exactly when the profile commits
  the policy, before any operation, and keeps it as session limitations and
  caller-project history.

ELF32 single-float (ilp32f), quad-float and ELF64 soft- or single-float
images stay refused, now with a reason built from the lane table.

* test(ghidra): verify RISC-V ilp32 against real Ghidra

verify:ghidra:riscv now also builds tests/conformance/c/riscv-float-abi.c
with ESP32-C3's -march=rv32imc_zicsr_zifencei -mabi=ilp32, typed with DWARF
and linked again with --strip-all. Every allocated section of the stripped
image must keep its address and bytes.

- riscv-checks.mjs holds the ilp32 storage of each fixture function together
  with the words the compiler's call site writes there. An LLVM objdump
  reading of the caller must find every word at its stated location: an
  unaligned named double, the a7/stack split, two floats in an integer pair,
  a large structure by reference, aligned variadic pairs and an indirect
  call.
- Through CLI and stdio MCP, the soft-float builds must report the soft-float
  lane and ilp32-prototype-v1, __rea_ilp32 with exactly that storage (one
  integer word per location when stripped), caller pseudocode that passes the
  source constants and names no floating-point register, the prototype
  outcome in retained Evidence, and decoded fence.i and csrr.
- The RV32 double-float image relabelled as soft-float, refused before, is
  admitted through the new lane. RV64 soft-float, single-float and unsupported
  bits are refused with their reasons.
- riscv-checks.test.ts runs the checks offline against recorded LLVM and
  Ghidra output, including Ghidra's stock ilp32d prototypes as a negative
  control.

* fix(ghidra): pass RISC-V ilp32 values over 8 bytes by reference

The psABI passes any value wider than 2 x XLEN by reference, scalars
included. __rea_ilp32 converted only structures and unions over 8 bytes to
pointers, so a long double (16 bytes) was joined into a1-a4. Measured on
Ghidra 12.1.4, rea_riscv_abi_quad(long double value, int sel) read value
from a4:4,a3:4,a2:4,a1:4 and sel from a5, where clang passes a pointer in
a1 and sel in a2.

- ReaGhidraRiscvCallingConvention.java now has one derivation driven by a
  per-policy row (floating-point entry size, preserved floating-point
  registers). Every policy inserts a rule before the input list's final
  join that converts any value of 9 bytes or more to a pointer; a return
  that size already becomes a caller buffer. The self-check requires a
  16-byte value to pass by reference.
- The fixture gains rea_riscv_abi_quad. The call-site oracle now checks the
  words the by-reference pointers point at, for the large structure and the
  long double, and the recorded data comes from an LLVM 19 build.
- docs/ghidra-riscv.md: the decision record and the ilp32 policy row.

ilp32-prototype-v1 keeps its id: no release carries it yet.

* test(ghidra): check RISC-V lane limitations by their facts

The limitation tests froze a full explanatory sentence, the note count and
array positions, so a harmless rewording failed them: the pattern Greptile
flagged on #7486. Assert the facts the notes must carry instead: the pinned
language, the static-linkage boundary, one note naming the declared ABI,
the ABI the pinned language models and the prototype REA applies, and the
declared ISA.

* test(ghidra): check return storage in stripped RISC-V ilp32 builds

The stripped branch of assertRiscvIlp32Storage compared parameter storage
only, so an inferred return that lost the high word of a register pair,
such as rea_riscv_abi_wide at a0:4 instead of a1:4,a0:4, still passed.

Stripped returns must now use the typed build's storage, or none when the
result goes through caller memory. Recovered type names are still not
compared: a test accepts other type names and refuses the dropped high word.

* docs(ghidra): record decision D3 as decided in #7477
2026-10-31 15:48:46 +08:00
Entrpi e6d0618979 feat(artifacts): inventory and extract ESP flash layouts (#8228)
* feat(artifacts): inventory and extract ESP32 firmware images

ESP32-family app and bootloader images (magic 0xE9) had no reader, so
open_binary refused OTA downloads, app partitions and bootloaders.

Add the esp-image container format behind inspect_artifact and
extract_artifact. A pure decoder reads each byte once and keeps only
fixed-size fields. Derived facts cover the chip, from a table cited to
ESP-IDF v6.1; segment classification as ESP-IDF's should_map and
should_load; checksum and appended SHA-256 results; app and bootloader
descriptors; Secure Boot v2 signature blocks, observed and unverified;
and undescribed trailing byte ranges. Truncation and undescribed bytes
make coverage unknown. ESP8266 images are refused with a specific
reason, and ciphertext stays unrecognized.

Add the format to every target, Evidence, snapshot, live-summary and
artifact vocabulary, document it, and add an optional verify:esp-images
lane that compares esptool-built images with esptool image-info and
espsecure signature-info-v2.

Refs #3615

* test(artifacts): run the ESP image lane on esptool v5.6.0

The lane compared descriptor fields only when esptool image-info printed
one, so a descriptor that REA reports and esptool does not print passed
without comparison. Compare the descriptor kind in both directions.

Record that the lane passed for all 13 images with esptool and espsecure
v5.6.0, the version the layout facts cite, as well as with 4.12.0.

* refactor(artifacts): define ESP image layout sizes once

The decoder and the derived facts each defined the segment-header,
appended-digest and flash-sector sizes, the erased-byte scan and the
offset formatter. Define them once with the other fixed-layout structures
and import them in both places. The signature slot count is the one
constant that drives the slot scan.

Correct the observedEnd comment: it is the end of the last chunk read.

* fix(artifacts): report ESP-IDF's segment length limit by chip

ESP-IDF's verify_segment_header rejects a segment of 16 MiB or more
unless the chip maps flash through 32-bit cache addresses
(SOC_SPI_MEM_SUPPORT_CACHE_32BIT_ADDR_MAP: ESP32-S3, P4, C5, C61, H4 and
S31 in v6.1). Record that capability in the chip table and add a
limitation for such segments; unknown chips get none.

`loaded` repeats should_load. It does not mean the bootloader accepts the
segment: verify_load_addresses still rejects one outside the chip's RAM,
which REA does not model. Say so in the docs, the skill reference and the
inventory limitation.

* test(artifacts): check ESP lane tools first and verify extraction

The lane started work before checking its tools, so a missing RISC-V
target, ld.lld, esptool or espsecure surfaced as a raw spawn or command
error. Check each one before building and name the variable to set. Warn
when esptool is not v5.6.0, the release the layout facts cite.

Assert each segment's class for the ESP32-C3 app, its variants and the
bootloader, as the Xtensa image already did. Extract the app and the
signed image through the CLI and compare every file with the bytes at
esptool's segment offsets or in the appended signature sector; the
source's SHA-256 must be unchanged and equal the reported root_sha256.

* docs(artifacts): state what the ESP image lane does not establish

The Verification section listed the lane's checks but not its gaps: only
ESP32-C3 and ESP32 images come from a real producer, no real project
build, OTA download or flash dump is inspected, no signature is verified,
and the lane does not run in CI.

Record the timing of the final lane run on esptool v5.6.0.

* fix(artifacts): compare ESP segment declarations

Segment entries hash only their data, so compare_artifacts reported a
segment that moved to another load address with the same bytes as
unchanged; only the image itself changed. Compare each segment's
load_addr and data_len as its declaration, as Node SEA and PyInstaller
members are compared, and count a changed declaration as a metadata
change, also when content is unavailable.

Header offsets are layout and the classification is derived, so neither
is compared.

* test(artifacts): run ESP lane commands as owned process trees

The lane ran its tools through execFile. A timeout killed only the direct
child, so a linker or assembler that Clang or GCC had started could keep
running while the lane removed its workspace. Run every command through
createVerifierCommands, check the tools inside withVerifierCleanup, and
remove the workspace only after commands.close() has stopped every tree;
a failed close keeps the workspace.

Only a nonzero exit now counts as a refusal or an expected esptool
failure; timeouts and spawn failures propagate. Supervising each command
roughly doubles the lane's run time.

* test(artifacts): cancel ESP decoding after its first decoded chunk

The cancellation test requested cancel or close on setImmediate, which
can land while the file is still being opened and checked, so it never
showed that decoding had started. It also passed against a reader that
ignored both requests while decoding. Acknowledge the decoder's first
chunk, then cancel or close, join the operation, and assert that no
later chunk reaches the decoder and that the reader ends closed.

* docs(artifacts): record which ESP metadata comparison ignores

Segments are compared by their declaration. The image's and the
signature blocks' format_metadata are not: every field comes from their
own bytes, which content comparison already covers, and a block's
image_digest_matches follows the image's content.

* feat(artifacts): inventory and extract ESP flash layouts

A whole-flash dump of an ESP32-family chip was read as its bootloader
image, with the partition table, apps and data left as undescribed bytes,
or refused when it began with erased flash. Read it as an esp-flash-image
container instead.

The partition table is decoded as esp_partition_table_verify accepts it
and is found at the caller's partition_table_offset, at the default
0x8000, or by scanning 0x1000-aligned sectors for a table whose MD5
verifies, which the inventory reports as derived. The bootloader is
located at its chip's ROM bootloader offset. Each partition is an entry
with its declaration in flat format_metadata; the bootloader and app
partitions carry ESP image facts from the existing streaming decoder; the
otadata records are reported with a selection derived without rollback or
anti-rollback, and the running partition stays unknown. One unreadable
partition does not hide the others, and extraction writes every partition,
nvs_keys included, as ordinary data.

Flash layouts are detected before single ESP images. The scan runs only
when an ESP image sits at a ROM bootloader offset, and is refused when a
bootloader at another chip's offset shows that the dump does not start at
flash address 0. open_binary gains flash_offset and partition_table_offset,
which require format esp-flash-image and are part of the snapshot target
identity; the CLI takes them with --target-format esp-flash-image.
Comparison treats a partition's label, type, subtype, range and flags as
its declaration.

* test(artifacts): verify ESP flash layouts with gen_esp32part, merge-bin and otatool

Add the optional verify:esp-flash lane. It builds default and relocated
partition tables with ESP-IDF's gen_esp32part.py, assembles dumps with
esptool merge-bin, including one written from flash 0x1000, and writes
OTA records with otatool through flash_file_tool.py, which replaces
parttool's esptool calls with reads and writes of the dump file.

It compares REA's bootloader and app facts with esptool image-info, every
partition with gen_esp32part.py's decoding of the extracted table, and the
OTA records and derived slot with otatool read_otadata after each switch.
It also checks extracted bytes and source integrity, the scan and caller
table offsets, flash_offset on the 0x1000 dump, a corrupted app beside
valid partitions and a truncated dump. ESP32 linker scripts place the
conformance bootloader and app in ESP32 address ranges.

* fix(artifacts): read ESP flash layouts as the bootloader does

Read the partition table as bootloader_utility_load_partition_table
does: the last factory, otadata and same-subtype OTA declaration wins,
and a limitation names repeats. Plan each otadata record within its
partition and the dump. An otadata partition smaller than two sectors
selects none, the bootloader's INVALID_INDEX; a record the dump lacks
leaves the selection unknown while the other record stays reported. The
fallbacks follow the bootloader's indexes: ota_0-fallback and
factory-fallback name no partition when the table declares none, and the
assumptions add GPIO holds and deep-sleep wake.

Refuse a dump whose bootloader contradicts its origin before the default
table offset is tried, whether flash_offset was omitted or given, and keep
the ESP image reader's ESP8266 refusal. A table never lies inside the
located bootloader image, so single app images are no longer scanned. An
explicit esp-flash-image selection is decided before other container
signatures, and the native commands' --target-format no longer takes it.
Say when no bootloader confirms the default origin.

Exempt the primary bootloader and partition_table entries of ESP-IDF 5.3
from placement limits, report every region inside a longer one, and name
partitions by index when labels repeat ignoring case. Keep an undecodable
label's bytes in label_hex and compare them. Word undescribed ranges as
starting at a byte that is not erased, and treat a short erased region as
erased.

Add an MCP compare_artifacts test for partition declarations, reader
identity and close tests, scan edge cases, the 0x2000 ROM offset and a
literal otatool CRC.

* test(artifacts): verify ESP primary layout entries and the otadata fallback

Build a table with the primary bootloader and partition_table entries
that gen_esp32part.py fills from --primary-bootloader-offset, and check
that REA reads it with complete coverage, no overlap or misplacement
limitation, and a table that agrees with gen_esp32part's decoding. Erase
the otadata with otatool after the slot switches and check the records
against read_otadata and the factory fallback.

* fix(artifacts): keep ESP flash coverage complete for a dump cut short

An esptool merge-bin dump ends after the last part it wrote, usually inside
its last partition. Such a partition made the layout's coverage unknown, so
artifact comparison of two merge-bin dumps reported every unchanged
partition as unknown.

The partition table names every partition whatever the file's length, so a
partition cut short or left out by the end of the file hides no entry. As
the ESP image reader does for a cut-short segment, its entry holds the bytes
the file has and an entry limitation gives the declared size; a partition
with no bytes stays in partitions_outside_dump. Coverage stays complete
unless undescribed bytes remain or no bootloader is located. An MCP
compare_artifacts test of two dumps that end inside ota_0 now classifies
every partition, and fails on the old rule.

* fix(artifacts): report written bytes in ESP partitions, erased slots included

An app slot is erased for ESP-IDF's bootloader when its image header bytes
are erased flash, and REA reported only that status. A slot can still hold
data further in: a dump read from a device held a complete older app image
1 MiB into an OTA slot reported as erased, with no limitation.

Each partition entry now records where the bytes the dump holds are
written, relative to the partition: written_offset, the first byte that is
not 0xFF, written_end, just past the last, and written_bytes, how many are
not 0xFF. They are null for a partition outside the dump. The status stays
erased, which is the bootloader's view, and a limitation names an erased
app slot that holds written bytes and says they are not read as an image.
The reader reads every partition byte once more to find them.

* fix(artifacts): derive the ESP boot selection for every partition table

The boot selection lived inside otadata, so a table without an otadata
partition, the common factory-only layout, had none. ESP-IDF's
bootloader_utility_get_selected_boot_partition returns the factory index
when the table declares no otadata partition.

Move it to a top-level boot_selection that every layout has. Its basis
names the bootloader's branch: otadata, factory-fallback and ota_0-fallback
from the records, no-otadata for a table without otadata, none for the
bootloader's INVALID_INDEX, and unknown when the dump lacks a record the
bootloader reads, which replaces the null selection. running_partition
moves with it, and otadata keeps its partition index and records. The
assumptions name what each branch leaves out: no records assumption
without otadata, and none for none or unknown. The real-tool lane checks
no-otadata on a table gen_esp32part built without otadata.

* fix(artifacts): say when an ESP image is a flash dump read from 0x1000

An ESP32 dump read from flash 0x1000, as read-flash 0x1000 or merge-bin
--target-offset 0x1000 writes it, opened automatically as its bootloader
image with a megabyte of undescribed bytes and no next probe. The flash
reader refuses that origin, but nothing told an agent which flash_offset to
pass.

espFlashDumpOrigin owns the inference: an image of a chip whose ROM loads
the bootloader from flash R, at file offset F, is the bootloader of a dump
read from R - F when a partition table sits where that origin puts 0x8000.
The ESP image reader asks it for a non-app image at file offset 0, so the
result does not depend on how the reader was created, and records
flash_dump {flash_offset, table_file_offset}, null otherwise, with a
limitation that says to reopen the file as esp-flash-image with that
flash_offset. inspect_artifact proposes the same open_binary call as a next
probe, and the flash reader's misplaced-origin refusal names the offset.

* perf(artifacts): summarize nested ESP images in the flash layout summary view

The summary view returned an ESP flash layout's root facts whole, so each
app partition and the bootloader carried every segment and signature
block, about half of a dump's summary.

The summary view now gives each nested image a summary instead of its
facts: status, chip, entry point, segment count, load ranges (each mapped,
loaded or unclassified segment's address and declared length), the
checksum and appended SHA-256 statuses, coverage and limitations. Complete
detail keeps the full facts, every segment included, and the retained
Evidence is unchanged. On a 16 MiB device dump the summary's root falls
from 5,141 to 2,657 bytes, and the summary Evidence from 10,038 to 7,554.

* test(artifacts): expect the misplaced image the ESP bootloader search reports

8a748d3cc added misplacedImage to the bootloader search, which the
locator uses to name the origin a misplaced bootloader implies, but one
test still compared the search result without it. Expect the image's
chip and file offset, and no image for an unknown chip.

* fix(artifacts): give every ESP partition from the dump's start an entry

After ecbc1a772 kept coverage complete for a dump cut short, a partition
wholly past the end of the file still had no entry, only a
partitions_outside_dump row. Comparing a merge-bin dump with a full device
read then reported that partition as added or removed, as if one table
did not declare it.

Every partition from the dump's first byte on is now an entry holding the
bytes the file has: none for one past the end, with a limitation saying
the file holds none of its bytes, no image facts and null written fields.
partitions_outside_dump keeps only partitions that begin before the dump's
first byte, and coverage is unknown when one does, since it has no entry.
Two dumps that lack the same partition now compare it as unchanged, and a
dump against a longer one reports the partition as changed; an MCP test
of a merge-bin dump against a full one checks that no path is added or
removed.

Unlike the ESP image reader, which marks a truncated image's coverage
unknown because later segments and signature blocks may be hidden, the
flash reader can list every partition from the table whatever the file's
length; only the entry limitation follows the image reader.

* fix(artifacts): select no ESP app from a partition table the bootloader rejects

boot_selection followed bootloader_utility_get_selected_boot_partition but
not the table check before it. For a table whose MD5 does not match, or
that has a second MD5 entry, esp_partition_table_verify fails, the
bootloader selects no app and resets. REA still derived a slot from the
otadata records, although the table's own limitation said the bootloader
rejects it.

The selection now has basis none, with no assumptions, for a table the
decoder marks invalid. It also follows two more branches of the
bootloader: an otadata partition at offset 0, which the bootloader takes
for none, selects the factory index as no-otadata, and a table without an
OTA app selects its fallback even when the dump lacks a record, since the
bootloader then ignores the records.

The bootloader also rejects a table whose partitions end past the flash
size in its header. REA does not model that check; the format guide says
so.

* fix(artifacts): require the ESP dump origin's table to follow the image

espFlashDumpOrigin named a later flash origin whenever a partition table
decoded where that origin puts 0x8000. Bytes inside the image's own
segments could be read that way, so an ESP32 bootloader whose segment data
held table-shaped bytes at file offset 0x7000 was reported as a dump read
from 0x1000.

The table must now start at or after the image's end, the rule the locator
already applies to the default table. The ESP image reader passes the
image_end it decoded, or the file size for a truncated image; the flash
reader's refusal passes the end of the misplaced image's segments. The
bootloader search drops its misplaced flag, which only restated
misplacedImage, and the next probe formats offsets with offsetText.

Reader tests cover the 0x1000 and 0x2000 ROM offsets, a standalone
bootloader, bytes that begin like a table, an app with a table at 0x7000
and a table inside a segment; without the boundary check, three of them
fail.

* fix(artifacts): give the ESP flash summary its own kind and each image's identity

The summary view's ESP flash root kept kind esp-flash-image, so a consumer
bound to kind could not tell an image's summary from its full facts, and
the advertised schema held two objects with the same kind const. It is now
esp-flash-image-summary.

The per-image summary also dropped the descriptor, which is what tells two
app slots apart, and named each image only by its partition index, which
the summary view, having no entry rows, could not resolve. Each nested
image now carries its entry path from the layout plan, in both views. The
summary adds the descriptor kind with its identifying fields (an app's
project_name, version, idf_ver, secure_version and app_elf_sha256; a
bootloader's version, idf_ver and secure_version), the signature block
count and each load range's classification. Its enums and fields derive
from the schemas that own them.

An MCP test reads a dump with an app in ota_0 and an erased ota_1 that
holds older bytes: it checks both summaries, that the summary's inventory
Evidence is the one the complete view returns with full facts, and the
erased slot's written range.

* test(artifacts): cover cancel and close during the ESP written-bytes scan

The written-bytes pass reads every byte of each partition the dump holds,
checking that the reader is open before each chunk, but only app decoding
had a cancellation and close test. A data partition, which only that pass
reads, now holds 9 MiB; once the scan has examined its first chunk, a
cancel rejects preparation with the caller's reason and a close with
unavailable, and the scan examines no further chunk.

The erased-slot test now asserts the whole set of erased-slot limitations,
so factory, ota_0 and nvs, which the bootloader reads or which are not app
slots, are shown to have none.

* docs(artifacts): re-time the ESP flash lane and state only observed causes

The lane ran again after its assertions changed; both esptool runs passed,
and the timed run's figures replace the earlier ones. The erased-slot
paragraph no longer names a cause for the older bytes, which REA cannot
observe, and the skill reference states that every table has a
boot_selection in its own sentence, with the none basis for a table the
bootloader rejects.

* fix(artifacts): retire ESP flash members as the ESP image reader does

#7498's maintainer commits made the ESP image reader keep a member owned
until its consumer closes it, retire fully buffered bytes when
cancellation arrives after production, and refuse to finish an inventory
paused across close. The ESP flash reader was copied from the earlier
reader. Main's shared stored-member lifecycle cases failed two of eight
against it: a paused inventory completed after close, and buffered member
bytes stayed readable after cancellation.

Give the flash reader the same lifecycle and run the shared cases against
it.

* docs(artifacts): re-time the ESP flash lane after merging main

The lane passed again with esptool v5.6.0 and 4.12.0 on the merge of upstream main. Record the v5.6.0 run's time and peak resident set.

* fix(artifacts): read ESP image Evidence recorded before flash_dump

The flash layout work made `flash_dump` a required field of the
`esp-image` root metadata. Evidence recorded before it has no such
field, so newer REA rejected records that were valid when they were
made: `compare_artifacts` and `inspect_analysis_view` reported
`evidence_integrity_mismatch` for them.

The field is now optional when read. A record without it parses as it
is, so the graph digest it commits to still matches; nothing fills the
field in. The image reader still writes it for every image, and the
inspection probe reads a missing field as no hint.

A regression test recommits an image inventory without the field and
passes it inline to `compare_artifacts` and `inspect_analysis_view`.
Against a server built with the required field, `compare_artifacts`
rejects it.

* fix(artifacts): stop the ESP flash gap scan when the reader closes

The scan for the first written byte in each undescribed gap checked
only the caller's signal. `close()` marks the reader closed and waits
for preparation, so it waited while the rest of a long erased gap was
read.

The gap scan now takes the reader's open check, as the written-bytes
scan and the image decoder do, and checks it before each chunk. Tests
cancel or close the reader after the scan's first chunk of a 9 MiB
erased gap and confirm that no later chunk is examined.

* fix(artifacts): name ESP partitions with Windows device labels by index

The safe-label rule accepted labels such as `NUL`, `con` or `COM1`, so
extraction wrote `partitions/NUL.bin`. Windows reserves those names
before any extension, and ordinary Windows tools cannot use such a
file.

A label that is a Windows device name (`CON`, `PRN`, `AUX`, `NUL`,
`COM0`-`COM9` and `LPT0`-`LPT9`, any case, alone or before a dot) now
takes the existing index fallback. The partition metadata and the
entry's source path keep the label.

* fix(artifacts): read ESP flash partitions' written bytes in one pass

Each partition got its own scan of the bytes the dump holds, so
partitions declared over the same bytes read them again and again.
Overlaps only add limitations, so a crafted table reached this normally:
94 overlapping partitions over a 32 MiB dump would examine about 3 GiB
before the first entry.

The reader now reads the bytes the partitions cover once. Partition
boundaries cut them into segments; one pass counts each segment's
written bytes and bounds, and each partition's extent is the sum of the
segments inside it. Every partition keeps its own declaration, extent
and limitations. The pass checks the reader's open state before each
chunk, as the per-partition scan did.

A test declares 60 partitions over one MiB, 40 over all of it and 20
over its first half, and checks each partition's extent and the
overlap limitations. The reader reads 1,113,256 bytes of the
1,114,112-byte dump; one scan per partition read 52,493,480.

* test(artifacts): fail the ESP lanes on failed commands and refusals

`otatool` turned a nonzero exit into returned text, which its callers
discarded, and `inspect()` returned `{ refused }`, which the flash lane
never checked. A failed command then surfaced later as a slot
assertion, an output-parser error or a TypeError, hiding its cause.

Commands the lanes need to succeed now reject with the output they
exited with: `otatool` and `inspect()` do so through a shared
`failedCommand`. An expected refusal is opt-in: `refusal()` returns
REA's output and fails the lane when REA opens the file. The ESP image
lane's two ESP8266 cases use it.

Checked by calling `otatool` on a file without a table, `inspect()` on a
file REA refuses and `refusal()` on a file REA opens; each rejects with
its cause.

* fix(artifacts): require a partition entry in an ESP partition table

The decoder counted the slot, not the partitions, so a lone MD5 entry
over the empty prefix, followed by the terminator, decoded as a table
with no partitions. Default and caller-selected detection then accepted
that empty layout, although the guide requires one partition.

A table now needs one partition entry before its terminator. An MD5
entry alone is refused as `entry 1 at 0x20 ends the table before any
partition entry`. The cases cover the decoder and both automatic and
caller-selected location.

* fix(artifacts): decode an image shared by ESP app partitions once

Each app partition got its own decode, so app partitions declared over
the same bytes decoded and hashed the same image again and again, the
same pattern the written-bytes scan had. With 60 app partitions over
one 960 KiB image the reader read 64,029,736 bytes of a 1,114,112-byte
dump.

Regions with the same file range now share one decode; each keeps its
own copy of the facts. Partitions with different ranges still decode
separately, because truncation can change the facts: at most one decode
per partition the table holds, each within its partition.

A test declares 60 app partitions over one image, 40 holding all of it
and 20 cutting it short, and expects three decodes: the bootloader, the
whole image and the cut one. The 60-partition dump now reads
2,161,864 bytes, as a dump with one app partition does.
2026-10-31 15:24:32 +08:00
Entrpi 091892dfed test(ghidra): expect carved MIPS copies to be refused at target opening (#8227)
Since 651afa445 target opening refuses every ELF declaration beyond EOF,
including carved copies, and ac6e0e258 moved the carved MIPS boundary tests
to that contract. The real-Ghidra MIPS lane still expected a carved image to
resolve with section-table limitations and be refused later by Ghidra
admission, so verify:ghidra:mips failed at its first carved fixture.

The lane now requires open_binary and the CLI to refuse the carved copy as
target_unavailable (file_format_rejected) with the exact declared
section-table range and captured size. The carve and that expectation live in
the shared MIPS checks, and a boundary regression applies them to the
production resolver and error projection, so a later contract change fails
without real Ghidra. The MIPS and PSP guides describe the current contract.
2026-10-31 15:24:17 +08:00
Machen John 1dc97d8cec fix(ghidra): report a missing existing-project program as a configuration error (#8222)
* fix(ghidra): report a missing existing-project program as a configuration error

When REA_GHIDRA_PROJECT_PATH selects a project that has no root program
with the selected name, Ghidra's headless analyzer aborts with
"Requested project program file(s) not found: <name>". REA reported this
as a generic execution_failure with rea doctor advice, and the reason was
visible only in the captured diagnostics.

Recognize that refusal in the operation-scoped launcher output, matching
only the exact name REA passed to -process, and return a
ConfigurationError for REA_GHIDRA_PROGRAM_NAME. The message says whether
the name was configured or defaulted from the target file name, and the
remediation explains how to select the exact root program. The selected
project is unchanged and there is no fallback to a fresh import. Other
launch failures keep their existing projection.

Fixes #8216

* test(ghidra): assert missing-program guidance through its formatter

Compare the projected setting constraint and remediation with
ghidraMissingProgramGuidance instead of freezing explanatory phrases,
following docs/testing.md. The program name and configured-or-defaulted
origin are still checked independently.
2026-10-31 15:24:00 +08:00
Jonnalagadda Sai Amarthya 7ba3f558d4 fix(windows): keep polling owned process output after a failed poll (#8163)
* fix(windows): keep polling owned process output after a failed poll

* test(windows): pin the failed-poll retry delay
2026-10-31 15:23:13 +08:00
morluto 245a581818 feat(kuna): verify release 1.986 and guide version selection (#8187)
* feat(kuna): verify release 1.986 and guide version selection

* test(kuna): cover verified release doctor guidance
2026-10-31 14:58:56 +08:00
morluto ec107022fa docs: guide audits of existing investigation tools (#8272) 2026-10-31 14:38:13 +08:00
gensokyo-dev b349e3c708 fix(evidence): give missing retained closures actionable recovery (#8270)
* fix(evidence): give missing retained closures actionable recovery

* refactor(evidence): assign retained lookup context at resolution
2026-10-31 14:30:30 +08:00
gensokyo-dev d9e01e5af2 feat(evidence): aggregate exact retained comparison references (#8267) 2026-10-31 13:59:13 +08:00
gensokyo-dev 6e2404b869 fix(comparison)!: preserve same-entry correspondence across renames (#8263)
* fix(comparison): preserve same-entry correspondence across renames

* docs(comparison): align rename guidance and focused contract checks
2026-10-31 12:53:42 +08:00
morluto 356cc50475 docs: guide design and verification of provider workflows (#8261)
* docs: guide provider workflow design and annotation comparisons

* docs: connect provider tool design to workflow verification
2026-10-31 12:15:15 +08:00
ShuaixinHuang d48e472fa6 fix(mcp): preserve repeated HTTP host allowlists (#8164) 2026-10-31 03:13:07 +08:00
Abdelrahman Teimaandgensokyo-dev 47fa6ab379 feat(electron): report custom URL protocol registrations
Expose static Electron protocol call facts through the shared application graph and existing CLI/MCP Evidence views. Preserve canonical case-insensitive scheme identity, original spellings, exact source provenance and explicit unknowns; keep registration success and runtime routing unproven.

Integration fixes also enforce module-only privileged API ownership, reuse the shared finding-key owner, and align the owning type documentation with the Evidence semantics. Validation and bounded real Obsidian CLI/MCP measurements are recorded in the PR.

Co-authored-by: UnknownHacker1 <[email protected]>
Co-authored-by: gensokyo-dev <[email protected]>
2026-10-31 02:23:47 +08:00
Harsh Bhardwaj 69bf61f7c7 fix(browser): preserve event coverage for failed requests (#8158) 2026-10-31 01:36:41 +08:00
Machen John ca7d1c3750 fix(ghidra): match startup-timeout guidance to the project mode (#8114)
In fresh-import mode, the startup-timeout remediation now points to
REA_GHIDRA_PROJECT_PATH for reusing a completed analysis, next to the
existing REA_GHIDRA_STARTUP_TIMEOUT_MS and REA_GHIDRA_DISABLED_ANALYZERS
advice.

In existing-project mode, the message no longer says Ghidra was
importing and analyzing the target, and the remediation no longer
suggests REA_GHIDRA_DISABLED_ANALYZERS, which configuration validation
rejects together with REA_GHIDRA_PROJECT_PATH.

Refs #3751
2026-10-31 01:32:06 +08:00
吴佳浩Alben e5c46dff03 fix(javascript): emit no socket for a literal out-of-range port (#8137)
* fix(javascript): emit no socket for a literal out-of-range port

`net` and `tls` validate a numeric port before creating a socket, so a literal
port Node rejects throws synchronously and no socket exists — but the analysis
reported an `acquire` operation.

Measured on Node 24.22.1:

    net.connect(0) / 80 / 65535        -> Socket created
    net.connect(65536)                 -> THREW ERR_SOCKET_BAD_PORT
    net.connect(-1) / 1.5              -> THREW ERR_SOCKET_BAD_PORT
    net.connect(NaN) / Infinity        -> THREW ERR_SOCKET_BAD_PORT
    net.connect(1e400)                 -> THREW ERR_SOCKET_BAD_PORT
    net.connect(0n) / true / false     -> THREW ERR_INVALID_ARG_TYPE
    net.connect(null)                  -> THREW ERR_INVALID_ARG_TYPE
    net.connect("")                    -> THREW ERR_SOCKET_BAD_PORT
    net.connect("/tmp/x.sock")         -> Socket created (Unix socket path)
    net.connect({ port: 80 })          -> Socket created
    net.connect(undefined) / {} / []   -> THREW ERR_MISSING_ARGS (undecided)
    --- tls mirrors net ---
    tls.connect(80)                    -> Socket created
    tls.connect(65536) / -1 / NaN      -> THREW ERR_SOCKET_BAD_PORT

A string is a Unix socket path and an object may carry a path, so only an
exactly readable non-port literal is decided. `undefined` and an empty object
throw `ERR_MISSING_ARGS`, which is a missing option rather than a bad port, so
they stay undecided. `NaN` and `Infinity` are identifiers rather than literals
and are decided only through `isUnmodifiedGlobalReference`.

* chore: retrigger title and readiness checks after the title was corrected

* fix(javascript): place the port check away from the fs flag check

`#8125` adds an fs flag guard at the top of `collectAcquisitions` and this branch
added a socket port guard in the same spot, so the two conflicted on every
rebase. The port check now filters the completed candidate list from
`collectJavaScriptSemanticResources` and its helpers sit at the end of the file,
which cannot collide with the fs flag guard.

* fix(javascript): drop the redundant in-loop port guard

The caller now filters the completed candidate list, so the loop guard became
dead code. Removing it also keeps this branch from editing the same lines as the
fs flag guard in #8125.
2026-10-31 01:23:50 +08:00
morluto 96606f5114 test(process): strengthen macOS token parser boundaries 2026-10-31 01:18:12 +08:00
morluto ebfef27b46 fix(ci): consolidate shared timer limit export 2026-10-31 01:16:19 +08:00
gensokyo-devandhhang 0ffed7a48c feat(android): trace callers of external DEX methods (#8154)
Resolve exact external method targets from directly encoded DEX invokes.
Preserve complete caller identity, bounded position evidence and follow-up
selectors. Report aggregate position coverage independently of the caller
inventory, and reuse bounded prototype facts within the exact session.

Absorbs #8096. Fixes #7811.

Co-authored-by: hhang <[email protected]>
2026-10-31 01:07:18 +08:00
morlutoandCopilot Autofix powered by AI 675d4076de test: strengthen regression oracles and cleanup ownership (#8152)
* test: strengthen suite oracles and cleanup ownership

* test: reuse setup across independent scenarios (#8153)

* Potential fix for pull request finding 'CodeQL / Improper code sanitization'

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>

---------

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
2026-10-31 00:22:16 +08:00
+1 1ddee1089b feat(dotnet): expose field constant defaults (#8015)
* feat(dotnet): expose field constant defaults

* test(dotnet): guard field default comparison warning

Compare actual ordinary and constant-bearing observations independently of the limitation producer. Preserve warning delivery and unchanged matching assertions while allowing harmless wording changes.

Refs #5150

* test(mcp): retain structured bundle path diagnostics

Keep absolute-path refusal and all rejected parameter facts independent of legacy flat or current structured SDK diagnostic rendering. Retain relative CLI, absolute MCP and no-Evidence controls.

Refs #5150

* Revert "fix(ci): integrate main and repair inherited static checks"

This reverts commit afd4d571f6, reversing
changes made to 317bf3fe23.

* fix(ci): replay reviewed field-default integration

* fix(analysis-view): keep inferred confidence on views of inferred Evidence (#8099)

createAnalysisViewEvidence chose view confidence from the view result kind
only, so every summary, page or item view of an inferred parent
(compare_source_to_bundle, inspect_evm_interface, partially resolved
inspect_il2cpp_metadata) was sealed as derived while keeping the parent's
authority. A projection never makes its facts more certain: an inferred
parent now yields inferred views through CLI and MCP alike. Observed and
derived parents keep their existing view confidence and identities.

Fixes #8098

Co-authored-by: Claude Opus 5.5 <[email protected]>

* fix(apple): read flattened framework Info.plist from Resources (#8101)

A macOS framework without Versions/ keeps its binary, _CodeSignature/ and
Resources/Info.plist directly in the .framework, as Electron and Gecko apps
ship it. Bundle anatomy looked for a shallow bundle's plist only at its root
and reported info_plist_path: null under complete coverage.

Resolve a shallow bundle's root Info.plist first, then Resources/Info.plist,
the same layout a framework version directory uses. Keep the iOS platform
rule tied to a root-level Info.plist so a Resources/-only application root
is not classified as iOS.

Fixes #8100

Co-authored-by: Claude Opus 5.5 <[email protected]>

* fix(desktop): charge retained rows at their measured representation (#8105)

* fix(desktop): charge retained rows at their measured representation

The desktop scan reserved 1024 + 24 bytes per path character for every
row, omission and the root against its 32 MiB representation budget. Real
rows cost 800-940 heap and 490-640 JSON bytes, so the charge overstated
them 6-13 times and large macOS bundles stopped at 3,700-7,200 entries,
far below max_entries.

Charge each path at the larger of its UTF-16 heap size and its escaped
JSON size, plus the existing fixed row and omission overheads, in one
helper. The bound still covers the retained heap and the serialized
report, including control-character paths that expand six times in JSON.

Fixes #8104

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): charge every retained copy of a path

The tighter row charge counted each path once, but rows repeat it in
convention-fact values and the next probes those facts offer, and host
failure issues name the absolute path. Control-character paths under a
convention such as Content/Paks or resources/app.asar therefore serialized
47-80 MiB of reports within the 32 MiB budget.

Reserve each copy through the same text helper where it is stored: path
convention facts with their row, the probes any fact offers (at most two,
pinned by a projection test) with that fact, retained read ranges before
their read, and probe failure issues before they are recorded.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>

* fix(source-maps): resolve empty sourceRoot sources against the map URL (#8107)

webpack 5 and tsc write "sourceRoot": "" for map-relative sources. Since
5f702dd6c the shared join added "/" for any non-null root, so these
sources resolved from the URL root: rxjs's ../../../src/internal/Observable.ts
under /npm/[email protected]/dist/cjs/internal/ became /src/internal/Observable.ts,
and xterm's webpack://xterm/./src/... became https://host/webpack://xterm/....

Treat an empty root like a null root in sourceMapReference, the single join
used by trace-web-source-location, analyze-javascript-application and the
web source-map fetcher. Chrome, Firefox and trace-mapping skip an empty root,
and tc39/ecma426#183 wrote the current steps to match them. Non-empty roots,
including "/" and repeated slashes (#6726), keep their existing behavior.

Fixes #8106

Co-authored-by: Claude Opus 5.5 <[email protected]>

* fix(javascript): stop reporting self-defaulted receivers as module loads (#8109)

The CommonJS loader search treated any repeated state on its own ancestor
chain as an unknown loader. A self-default such as
`session.messages = session.messages || []` forms that cycle, because a
member read inside the assigned value was attributed to the later call's
read site and selected the same write again. Each `push` on the value was
then reported as an unresolved `require` load, and graph coverage became
partial.

Record a member read inside a flowed value as a depth-1 captured selection
at its own position, as member mutations already do for flowed origins.
Treat a revisited state as adding no source; the cycle's other entries are
still expanded once each. Remove the superseded ancestor tracking.

Fixes #8102

Co-authored-by: Claude Opus 5.5 <[email protected]>

* fix(captures): admit browser-serialized HAR request URLs (#8112)

* fix(captures): admit browser-serialized HAR request URLs

[email protected] checks request.url with ajv-formats' strict RFC 3986
`uri` format. Browsers record and send URLs as WHATWG serializes them,
which keeps characters such as `|` and `{` unescaped in the query, so one
Google Fonts or GraphQL GET entry made inspect-web-network-capture and
environment-build reject the whole HAR.

HAR 1.2 defines request.url only as the absolute request URL. Own that
correction in the HAR validator: every URL the RFC 3986 check accepts
stays accepted, and a URL that WHATWG URL serialization reproduces
unchanged is accepted too. Spellings WHATWG would rewrite (a backslash
authority, a tab, a space) and relative references still fail.

Fixes #8110

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(captures): state that RFC 3986 request URLs stay admitted

The HAR request URL rule is "passes RFC 3986, or WHATWG serialization
reproduces it unchanged". A spelling WHATWG would rewrite therefore fails
only when it also fails the RFC 3986 check; an uppercase host still
passes. Say so in the docs and the validator comment, and pin the
accepted uppercase-host case in the CLI test.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>

* fix(apple): charge the XML plist decoder's DOM before decoding (#8115)

* fix(apple): charge the XML plist decoder's DOM before decoding

The XML preflight charged 4 bytes per character and 256 bytes per start
or end tag. The decoder builds an @xmldom/xmldom DOM that measured
1,000-1,160 bytes per element and up to 240 bytes per text, comment or
CDATA node, which were not charged at all. A 1.75 MB plist of 230,000
<true/> lines was admitted under the 64 MiB decode budget and peaked at
249 MiB of heap; shipped Apple plists peaked at 80-104 MiB.

Charge each element and each text, comment, CDATA or DOCTYPE node at a
measured upper bound, keeping the character and number-projection terms.
The binary estimator already bounds its decoder and is unchanged.

Fixes #8113

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(apple): charge every XML element, attribute and reference

The XML preflight recognized only element names starting with an ASCII
letter, so names such as é, _, a CJK or astral character escaped the
element charge: 100,000 <é/> elements (0.5 MB) built an 84 MiB DOM before
the plist decoder rejected the tag. Attributes and entity or character
references were also uncharged; 20 attributes per element measured about
210 bytes each, and each reference about 26 bytes beyond its characters.

Classify markup by the character after "<" instead of a name pattern, so
anything other than an end tag, comment, CDATA, PI or declaration counts as
an element (saxes rejects malformed markup before the DOM is built). Charge
each unquoted "=" in a start tag as an attribute node and every "&" as a
reference.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(apple): charge XML references only where the parser expands them

The previous commit charged every "&" in the document as a reference,
including literal ampersands inside CDATA sections and comments. A valid
plist with one CDATA string of 1,000,000 ampersands was refused although
it is a single node the decoder holds as one string.

Count "&" during the existing token walk, only in text runs and in the
attribute values of start tags, so comments, CDATA, PIs and declarations
add no reference charge and the scan stays a single pass.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>

* fix(fit): preserve leading U+FEFF in property strings (#8083)

* fix(environment): retain conflicting scenario MIME uncertainty (#8082)

* fix(environment): retain conflicting scenario MIME uncertainty

* test(mcp): assert structured absolute-path diagnostics

* test(mcp): assert typed selector and stdio diagnostics

* fix(electron): keep literal IPC sends ambiguous beside unresolved handlers (#8120)

* fix(electron): keep literal IPC sends ambiguous beside unresolved handlers

A main handler on an unresolved channel can receive any literal renderer
send of a compatible mode, but pairing ignored it unless it had a scoped
receiver. The send was then reported unpaired, or paired with another
handler. Treat an unresolved-channel handler as a competitor that never
pairs.

The wrapper pass seeds only calls with a literal first argument, so it now
records which invocations of a dynamic site it traced. The syntax finding
gains `invocation_channels: "literal"` only when every invocation is a
traced direct call of an unexported local function supplying a literal
channel. Such a site is already represented by its per-invocation findings
and does not compete.

Fixes #8118

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(electron): keep guarded wrapper registrations competing in IPC pairing

A guarded wrapper registration keeps its predicate on a call path and has
no IPC finding. It still counted toward `invocation_channels: "literal"`,
so pairing lost it: a send on that channel was reported unpaired, or
paired uniquely with a direct handler. The enumerated site now carries
these channels as `guarded_invocation_channels`, and each one competes for
its own channel without pairing.

Index each binding's direct call sites by appending in place. Spreading
the earlier calls on every reference was quadratic in a file's call count.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>

* fix(ci): remove unused exports that fail deep static checks (#8095)

* fix(ci): remove unused exports that fail deep static checks

Knip on main reports three analysis-view hub re-exports, two type
re-exports, an unused artifact-format guard and an alias constant. Drop
the dead re-exports and guard, and give the debugger control-reply budget
its own literal: it is a separate budget from the caller command-output
default, as its comment already states.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(config): read the Rizin control budget from its own constant

The Rizin control-reply admission limit still took its value from the
caller command-output default, while GDB used DEBUGGER_CONTROL_REPLY_BYTES.
Read the dedicated constant in both so the control budget has one owner.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>

* perf(javascript): keep one member-effect record per stored fact (#8103)

Member-mutation indexing appended a binding write or escape record for
every traversal state that reached it. States that differ only in
coverage fields the record never stores (origin, owner initializer,
getter widening) produced identical records, and each later receiver
query rescanned all of them. Record each stored fact once, alongside the
existing required-module effect deduplication. Propagation is unchanged.

Effect ordering also re-walked the same top-level statement ancestry for
every candidate record. Cache it with the other initializer positions,
and test the selected path before the costlier receiver comparison.

On the #7774 reproducer the semantic IR and the complete
analyze-javascript-application Evidence are byte-identical, while the
CLI run drops from 146 s to 40 s.

Refs #7774

Co-authored-by: Claude Opus 5.5 <[email protected]>

* feat(native): open extension, framework, XPC and plug-in bundle directories (#8117)

* feat(native): open extension, framework, XPC and plug-in bundle directories

open_binary and the CLI inspection commands admitted only .app
directories and pointed every other Apple bundle at the JavaScript
analyzer. Resolve .appex, .framework, .xpc, .systemextension, .bundle
and .plugin directories through their Info.plist CFBundleExecutable, in
the Contents or flat root layout, plus the versioned framework layout
under Versions/Current. Exactly one layout may declare the bundle and
every resolved path must stay inside it.

Unresolvable bundles fail closed with typed constraints, probed paths
and the Info.plist that was read. Other Apple bundle types are refused as
unsupported instead of being offered the JavaScript route. Resource-root
discovery shares the same layout rule, so inspect_plist reads a
versioned framework's Info.plist. The recognized bundle vocabulary now
has one owner in the domain.

Refs #4310

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(native): refuse kernel extensions and state bundle error fields

Name the Apple bundle types binary routes recognize but do not open
(.dext, .kext, .qlgenerator, .mdimporter) explicitly, so a kernel
extension is refused as unsupported instead of receiving the JavaScript
route. Document which bundle refusals carry probed_paths and info_plist,
and make success tests throw the typed admission error before asserting.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* feat(native): open flattened frameworks through Resources/Info.plist

Electron and Gecko apps ship frameworks without Versions/: the program
file sits at the framework root beside Resources/Info.plist, the layout
#8101 taught bundle anatomy. Read that layout for a framework without
Versions/, and keep the versioned layout for one with it, so a versioned
framework's root Resources link is never read as a second declaration.
The resource root reports it as flattened-framework.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>

* chore(security): configure dependency and ownership reviews

* perf(javascript): index member-effect paths for projected reads (#8108)

* perf(javascript): index member-effect paths for projected reads

Each projected read scanned every member-write record of its binding and
tested the record path against the selection. On the #7774 reproducer
only 3.7% of scanned records pass that test. Index each binding's
append-only records by path in a lazily built trie that extends itself
with newly appended records, and visit only records whose exact keys a
selected choice names or whose keys are wildcards. Candidates come back
in record order and still pass the exact predicate, now owned next to
the index as semanticEffectPathMatches.

The semantic IR and the complete analyze-javascript-application Evidence
are byte-identical. On the full reproducer each worker phase drops from
about 80 s to 29 s, and to 8 s together with #8103.

Refs #7774

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* perf(javascript): stop escape checks at the first indexed match

The escape check only asks whether any matching escape is unsuperseded,
so it does not need ordered candidates. Share one trie traversal between
the ordered write candidates and an early-exit visitor for this check.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>

* fix(hopper): distinguish an absent demo dialog from publisher rejection (#8074)

* fix(hopper): distinguish an absent demo dialog from publisher rejection

The Linux demo watcher collapsed an admitted launcher with no first-run dialog into unsupported_demo_dialog. Report that post-authentication timeout as demo_dialog_absent, keep the unsupported dialog code for an observed but unrecognized Hopper window, and allow REA_HOPPER_DEMO_DIALOG_TIMEOUT_MS to widen the fixed wait. Closes #8008.

* fix(hopper): tolerate callers without the dialog timeout argument

* fix(hopper): report an invalid demo dialog timeout as its own failure

An invalid REA_HOPPER_DEMO_DIALOG_TIMEOUT_MS exited 73 and was shown as
invalid_launch_command. Exit 82 is invalid_dialog_timeout. The public error
names the setting, shows the rejected value and source, lists the allowed
range, and says to restart REA.

Drive the demo watcher with controlled windows and time. No window exits 81,
a Hopper window without a supported dialog exits 76, and a dialog that arrives
after the old 10 second limit is still accepted.

* fix(hopper): keep startup stderr capture on AdapterFailure

DemoWatchStop was inserted above with_stderr, so launch failures called a
method they no longer had and became unexpected_failure. Restore with_stderr
on AdapterFailure and reject a watcher fixture that cannot capture stderr.

---------

Co-authored-by: Cursor Agent <[email protected]>

* test(ci): require provider checks and reduce test startup (#8145)

* test(ci): require provider coverage and reuse test sessions

* fix(ci): prepare the runtime before Python producer tests

The fast Python job ran the suite on a fresh checkout, so analysis-efficiency
checks failed without compiled process modules. Install locked dependencies
and prepare the runtime before the suite.

* fix(ci): keep provider test reports inside the checkout

The provider-report reader accepted an arbitrary path from argv. Resolve the
path and reject anything outside the checkout before reading it. The packet
and signal workflows write those reports under .cache.

* fix(ci): require a test module in each named Python bridge family

Discovery still runs every bridge test. The mitmproxy, native, and pwndbg
families must each keep a test module, so deleting one cannot pass through
the others.

---------

Co-authored-by: Cursor Agent <[email protected]>

* fix(javascript): emit no http request for a target that is not a URL (#8147)

* fix(javascript): emit no http request for a target that is not a URL

`node:http` parses a string target with `new URL(...)` before a request object
exists, so a literal that is not an absolute URL throws `ERR_INVALID_URL`
synchronously. The analysis reported `endpoint` at `resolution: "complete"` for
those calls, and the graph then projected `constructs-request` as `resolved`.

Measured on Node 24.22.1:

    http.get("nope")                     -> THREW ERR_INVALID_URL
    http.get("example.test/api")         -> THREW ERR_INVALID_URL
    http.get("/api/items")               -> THREW ERR_INVALID_URL
    http.get("/api/items", { host })     -> THREW ERR_INVALID_URL
    http.get("")                         -> THREW ERR_INVALID_URL
    http.get("//example.test/x")         -> THREW ERR_INVALID_URL
    http.get("http://")                  -> THREW ERR_INVALID_URL
    http.request("nope")                 -> THREW ERR_INVALID_URL
    https.get("/api/items")              -> THREW ERR_INVALID_URL
    --- accepted ---
    http.get("http://example.test/x")    -> OK ClientRequest
    http.get("HTTP://example.test/x")    -> OK ClientRequest
    http.get("http:/x")                  -> OK ClientRequest
    http.get("http:///x")                -> OK ClientRequest
    http.get("http:x")                   -> OK ClientRequest

`URL.canParse` is the exact predictor of that branch, so a relative spelling such
as `"http:x"` keeps its operation. It already has precedent in this codebase
(`javascriptPackageImportAliases.ts`).

Two boundaries are deliberate. `fetch` and `new WebSocket` are host-supplied
globals: a browser resolves a relative target against the document URL, so their
spellings are not judged by Node's absolute-URL rule. A protocol mismatch such as
`http.get("ftp://...")` is also left alone because an options object can rescue
it, so it is not a provable throw.

The guard is scoped to `method === "get" | "request"`, which only
`requestMethod`'s `http`/`https` builtin branch can produce.

* chore: retrigger title and readiness checks after the title was corrected

* refactor(ci): unify lane ownership and guard Python tests (#8150)

* refactor(ci): unify lane ownership and guard Python tests

* fix(ci): reject entirely skipped bridge subtests

* docs: correct the raw ARM example and document a Cortex-M language override (#7483)

* docs(raw-binary): replace the STM32-style ARM example

The headline raw example opened ecu.bin at 0x8000000, an STM32 flash
address, while the arm lane decodes in ARM state. Map a 32-bit ARM-state
image at 0x8000 instead, and state beside the example that Thumb-only
images, including Cortex-M firmware, are not supported.

* docs(ghidra): document an interim Cortex-M language override

Ghidra imports little-endian ARM ELF files as ARM:LE:32:v8, which
defaults to ARM state and marks Thumb code only from $t mapping symbols
and odd function-symbol addresses. Unpinned arm ELF already admits
REA_GHIDRA_LANGUAGE_ID, so document ARM:LE:32:Cortex (ARMv6-M, ARMv7-M,
ARMv7E-M) and ARM:LE:32:v8-m (ARMv8-M) as an unverified interim.

State the override's full effect while it is set: raw-binary, DOS, DOL,
PSP and language-pinned lanes such as RISC-V refuse it, and REA asks
Ghidra to import every other target in the ARM language, because it does
not check the override's processor against the target.

* fix(ida): admit architecture families by allowlist (#7485)

IDA refused MIPS, RISC-V, PowerPC, Hexagon and arm64_32 by denylist, so
any family nobody excluded was reported supported, and the label ternary
printed "Hexagon" for any other metadata family. Admit exactly the
families IDA admits today (x86, x86_64, arm, arm64) and name every other
family from its own architecture id. Families that Ghidra admits keep
the pointer to a Ghidra profile.

Refusal text now uses architecture ids, for example "no verified mips
lane" instead of "no verified MIPS lane".

* ci(ghidra): probe M-profile ARM and Xtensa decoding (#7482)

* ci(ghidra): probe M-profile ARM and Xtensa decoding

The embedded plan relies on ARM:LE:32:Cortex, ARM:LE:32:v8-m and
Xtensa:LE:32:default, but CI only exercised ARM:LE:32:v7. Import
source-owned bytes for each language at 0x1000 with -noanalysis and
check how they decode from the language's default context: Cortex and
v8-m must start in Thumb state, v8-m must decode the ARMv8-M MSPLIM
register, and Xtensa must decode entry and retw.n.

Add the probe scripts the job runs, including the existing ARM context
probe, to the ghidra_bridge change scope.

* ci(ghidra): match decode-probe operands as complete operands

The operand expectation searched the instruction text for a substring,
so mrs/4/msp also accepted mrs r0,msplim. Compare it with each operand
Ghidra reports for the instruction, so only a complete operand matches.
The Cortex (msp, lr), v8-M (msplim, lr) and Xtensa (a1) expectations are
unchanged.

* test: guard target format vocabularies and DOL snapshot saves (#7481)

* test(mcp): save and reopen a GameCube DOL snapshot

#7290 reported that DOL Evidence could not be bundled or saved; 50822e1ef
fixed the bundle manifest and added bundle export/import coverage, but no
test saved a DOL session through close_binary. Save a DOL snapshot with
recorded Evidence, check its target and artifact manifest, then reopen it
and replay the read without running the provider again.

* test(binary): require every target format in each target vocabulary

A new target format must be added by hand to the Evidence subject,
bundle manifest, snapshot target and live summary vocabularies; missing
one caused #7290. Give every BinaryTarget format a fixture through a
mapped type, so a new format fails compilation until it has one, and
push each fixture through the producers that persist or project targets.
Caller-selected format hints must name executable target formats.

This is a guard until target formats have a single owner.

* test(mcp): assert the replayed DOL result and the owned replay limitation

The DOL snapshot replay test checked the subject and the provider-call
count but not the answer, so a replay returning null or another value
would pass. Compare the replayed normalized result with the original.
Name the replay limitation as SNAPSHOT_REPLAY_LIMITATION in the snapshot
cache that adds it, and assert that value instead of repeating the
sentence.

* test: list Intel HEX and S-record targets in the vocabulary guard

Upstream added the intel-hex and srec formats (#7136). The guard requires
a fixture for every target format, so after merging main it no longer
typechecked; both formats now have archive fixtures and pass the same
persistence and projection checks.

* test: list UnityFS among the target format fixtures

Upstream #7699 added the unityfs target format, so the vocabulary test's
per-format fixtures must name it; without one the test no longer typechecks
against main.

* test: list uImage among the target format fixtures

Upstream #7234 added the uimage target format, so the vocabulary test's
per-format fixtures must name it; without one the test no longer typechecks
against main.

* test: list the ESP image among the target format fixtures

Upstream #7498 added the esp-image target format, so the vocabulary test's
per-format fixtures must name it; without one the test no longer typechecks
against main.

* fix: correct stale provider refusal and diagnostic text (#7484)

* fix(ghidra): name every admitted family in the architecture refusal

The refusal listed x86, x86_64, arm, arm64, MIPS, RISC-V, raw 6502 and
raw m68k, but the admission set also holds powerpc and hexagon. Build
the text from SUPPORTED_ARCHITECTURES, marking raw-only families, so a
newly admitted family cannot be left out again, and name the refused
family.

* fix(hopper): stop calling the Ghidra Hexagon lane verified

Hopper declined Hexagon targets with "Select a verified Ghidra Hexagon
profile instead", but REA has no verified Hexagon lane. Record whether
each declined family's Ghidra lane is verified, and say that Ghidra
admits Hexagon through an unverified lane.

Name declined families by architecture id, as the Ghidra and IDA
refusals do.

* fix(ghidra): name the pinned language in profile readback refusals

The session readback said "MIPS" whenever a provider-pinned profile
conflicted with the target or with REA_GHIDRA_LANGUAGE_ID, although the
RISC-V, PowerPC and DOL lanes are pinned too. The readback helper was
MIPS-named and re-parsed the language slot that
ghidraProfileLanguageOverride already owns. Move it beside that owner as
a family-neutral readback and name the pinned language id instead.

* fix(ghidra): list DOL among independently verified load images

GameCube DOL load images are independently verified, but the declined
format reason, the inspect_native_load_image capability note and the
Windows P0 guide listed only DOS MZ, DOS COM and raw-binary.

* fix(ghidra): count RV32E's sixteen integer registers

The EF_RISCV_RVE refusal described a 15-register ABI. RV32E has 16
integer registers, x0-x15, as docs/ghidra-riscv.md already says.

* fix(elf): name the refused e_machine

"unsupported ELF architecture" did not say which machine was refused.
Include the numeric e_machine, for example EM_XTENSA 94.

* test(hopper): assert declined-lane facts and owned refusal reasons

The MIPS case banned the phrase "not verified" instead of checking
facts, so wrong advice would still pass. Expose each declined family's
Ghidra lane status, assert those statuses, check that the advice depends
on the status, and compare Hopper's refusals with
hopperDeclinedArchitectureReason. The RISC-V refusal table now compares
the provider's reason with ghidraRiscvUnsupportedReason and expects the
EF_RISCV_RVE declaration name rather than fixed wording.

* test(ghidra): assert verified load-image formats as owned facts

The declined-format test required display phrases such as "DOS MZ" and
"GameCube DOL", so harmless rewording failed it while wrong guidance
containing those names passed. The format list was also spelled three
times: in the attestation's membership check, its reason and the
inspect_native_load_image capability note. Give the verified formats one
owner with the formatters for both texts, assert the list as a fact, and
compare the delivered reason and capability note with those formatters.

* fix(hopper): stop restating which Ghidra lanes REA has verified

Hopper's refusal for families it declines carried its own table of
which Ghidra lanes REA has verified (mips, riscv and powerpc verified,
hexagon not). That restated the Ghidra provider's facts in Hopper and
goes stale whenever they change, the drift that made the Hexagon refusal
wrong in the first place. Reading Ghidra's facts from Hopper would
couple the providers instead; no module in src/hopper imports from
src/ghidra.

The table and hopperDeclinedGhidraLane are removed.
hopperDeclinedArchitectureReason now states only what Hopper owns: it
does not admit the family, and the Ghidra provider's lane reports
whether REA has verified it. The arm64_32 sentence is unchanged.

The test checks every declined family against the formatter: each
reason names the family and Ghidra, and apart from the name all reasons
are the same text, so none states a per-family Ghidra status.

* fix(hopper): route declined families to Ghidra without promising its lane facts

The refusal said the Ghidra lane reports whether REA has verified it, but
Ghidra admits Hexagon with no such note, so a caller could not find the
promised status. Hopper now only states that it does not admit the family
and routes the caller to the Ghidra provider.

---------

Co-authored-by: hhang <[email protected]>
Co-authored-by: Kaoru0822 <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: ShuaixinHuang <[email protected]>
Co-authored-by: morluto <[email protected]>
Co-authored-by: Henglie <[email protected]>
Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: 吴佳浩Alben <[email protected]>
Co-authored-by: Entrpi <[email protected]>
Co-authored-by: gensokyo-dev <[email protected]>
2026-10-31 00:10:36 +08:00
Entrpi 250dc81cd6 fix: correct stale provider refusal and diagnostic text (#7484)
* fix(ghidra): name every admitted family in the architecture refusal

The refusal listed x86, x86_64, arm, arm64, MIPS, RISC-V, raw 6502 and
raw m68k, but the admission set also holds powerpc and hexagon. Build
the text from SUPPORTED_ARCHITECTURES, marking raw-only families, so a
newly admitted family cannot be left out again, and name the refused
family.

* fix(hopper): stop calling the Ghidra Hexagon lane verified

Hopper declined Hexagon targets with "Select a verified Ghidra Hexagon
profile instead", but REA has no verified Hexagon lane. Record whether
each declined family's Ghidra lane is verified, and say that Ghidra
admits Hexagon through an unverified lane.

Name declined families by architecture id, as the Ghidra and IDA
refusals do.

* fix(ghidra): name the pinned language in profile readback refusals

The session readback said "MIPS" whenever a provider-pinned profile
conflicted with the target or with REA_GHIDRA_LANGUAGE_ID, although the
RISC-V, PowerPC and DOL lanes are pinned too. The readback helper was
MIPS-named and re-parsed the language slot that
ghidraProfileLanguageOverride already owns. Move it beside that owner as
a family-neutral readback and name the pinned language id instead.

* fix(ghidra): list DOL among independently verified load images

GameCube DOL load images are independently verified, but the declined
format reason, the inspect_native_load_image capability note and the
Windows P0 guide listed only DOS MZ, DOS COM and raw-binary.

* fix(ghidra): count RV32E's sixteen integer registers

The EF_RISCV_RVE refusal described a 15-register ABI. RV32E has 16
integer registers, x0-x15, as docs/ghidra-riscv.md already says.

* fix(elf): name the refused e_machine

"unsupported ELF architecture" did not say which machine was refused.
Include the numeric e_machine, for example EM_XTENSA 94.

* test(hopper): assert declined-lane facts and owned refusal reasons

The MIPS case banned the phrase "not verified" instead of checking
facts, so wrong advice would still pass. Expose each declined family's
Ghidra lane status, assert those statuses, check that the advice depends
on the status, and compare Hopper's refusals with
hopperDeclinedArchitectureReason. The RISC-V refusal table now compares
the provider's reason with ghidraRiscvUnsupportedReason and expects the
EF_RISCV_RVE declaration name rather than fixed wording.

* test(ghidra): assert verified load-image formats as owned facts

The declined-format test required display phrases such as "DOS MZ" and
"GameCube DOL", so harmless rewording failed it while wrong guidance
containing those names passed. The format list was also spelled three
times: in the attestation's membership check, its reason and the
inspect_native_load_image capability note. Give the verified formats one
owner with the formatters for both texts, assert the list as a fact, and
compare the delivered reason and capability note with those formatters.

* fix(hopper): stop restating which Ghidra lanes REA has verified

Hopper's refusal for families it declines carried its own table of
which Ghidra lanes REA has verified (mips, riscv and powerpc verified,
hexagon not). That restated the Ghidra provider's facts in Hopper and
goes stale whenever they change, the drift that made the Hexagon refusal
wrong in the first place. Reading Ghidra's facts from Hopper would
couple the providers instead; no module in src/hopper imports from
src/ghidra.

The table and hopperDeclinedGhidraLane are removed.
hopperDeclinedArchitectureReason now states only what Hopper owns: it
does not admit the family, and the Ghidra provider's lane reports
whether REA has verified it. The arm64_32 sentence is unchanged.

The test checks every declined family against the formatter: each
reason names the family and Ghidra, and apart from the name all reasons
are the same text, so none states a per-family Ghidra status.

* fix(hopper): route declined families to Ghidra without promising its lane facts

The refusal said the Ghidra lane reports whether REA has verified it, but
Ghidra admits Hexagon with no such note, so a caller could not find the
promised status. Hopper now only states that it does not admit the family
and routes the caller to the Ghidra provider.
2026-10-30 22:39:46 +08:00
Entrpi 54756ff24e test: guard target format vocabularies and DOL snapshot saves (#7481)
* test(mcp): save and reopen a GameCube DOL snapshot

#7290 reported that DOL Evidence could not be bundled or saved; 50822e1ef
fixed the bundle manifest and added bundle export/import coverage, but no
test saved a DOL session through close_binary. Save a DOL snapshot with
recorded Evidence, check its target and artifact manifest, then reopen it
and replay the read without running the provider again.

* test(binary): require every target format in each target vocabulary

A new target format must be added by hand to the Evidence subject,
bundle manifest, snapshot target and live summary vocabularies; missing
one caused #7290. Give every BinaryTarget format a fixture through a
mapped type, so a new format fails compilation until it has one, and
push each fixture through the producers that persist or project targets.
Caller-selected format hints must name executable target formats.

This is a guard until target formats have a single owner.

* test(mcp): assert the replayed DOL result and the owned replay limitation

The DOL snapshot replay test checked the subject and the provider-call
count but not the answer, so a replay returning null or another value
would pass. Compare the replayed normalized result with the original.
Name the replay limitation as SNAPSHOT_REPLAY_LIMITATION in the snapshot
cache that adds it, and assert that value instead of repeating the
sentence.

* test: list Intel HEX and S-record targets in the vocabulary guard

Upstream added the intel-hex and srec formats (#7136). The guard requires
a fixture for every target format, so after merging main it no longer
typechecked; both formats now have archive fixtures and pass the same
persistence and projection checks.

* test: list UnityFS among the target format fixtures

Upstream #7699 added the unityfs target format, so the vocabulary test's
per-format fixtures must name it; without one the test no longer typechecks
against main.

* test: list uImage among the target format fixtures

Upstream #7234 added the uimage target format, so the vocabulary test's
per-format fixtures must name it; without one the test no longer typechecks
against main.

* test: list the ESP image among the target format fixtures

Upstream #7498 added the esp-image target format, so the vocabulary test's
per-format fixtures must name it; without one the test no longer typechecks
against main.
2026-10-30 22:35:53 +08:00
Entrpi 19ab239278 ci(ghidra): probe M-profile ARM and Xtensa decoding (#7482)
* ci(ghidra): probe M-profile ARM and Xtensa decoding

The embedded plan relies on ARM:LE:32:Cortex, ARM:LE:32:v8-m and
Xtensa:LE:32:default, but CI only exercised ARM:LE:32:v7. Import
source-owned bytes for each language at 0x1000 with -noanalysis and
check how they decode from the language's default context: Cortex and
v8-m must start in Thumb state, v8-m must decode the ARMv8-M MSPLIM
register, and Xtensa must decode entry and retw.n.

Add the probe scripts the job runs, including the existing ARM context
probe, to the ghidra_bridge change scope.

* ci(ghidra): match decode-probe operands as complete operands

The operand expectation searched the instruction text for a substring,
so mrs/4/msp also accepted mrs r0,msplim. Compare it with each operand
Ghidra reports for the instruction, so only a complete operand matches.
The Cortex (msp, lr), v8-M (msplim, lr) and Xtensa (a1) expectations are
unchanged.
2026-10-30 22:32:41 +08:00
Entrpi 1926fc2dac fix(ida): admit architecture families by allowlist (#7485)
IDA refused MIPS, RISC-V, PowerPC, Hexagon and arm64_32 by denylist, so
any family nobody excluded was reported supported, and the label ternary
printed "Hexagon" for any other metadata family. Admit exactly the
families IDA admits today (x86, x86_64, arm, arm64) and name every other
family from its own architecture id. Families that Ghidra admits keep
the pointer to a Ghidra profile.

Refusal text now uses architecture ids, for example "no verified mips
lane" instead of "no verified MIPS lane".
2026-10-30 22:30:23 +08:00
Entrpi b33f4a5a1b docs: correct the raw ARM example and document a Cortex-M language override (#7483)
* docs(raw-binary): replace the STM32-style ARM example

The headline raw example opened ecu.bin at 0x8000000, an STM32 flash
address, while the arm lane decodes in ARM state. Map a 32-bit ARM-state
image at 0x8000 instead, and state beside the example that Thumb-only
images, including Cortex-M firmware, are not supported.

* docs(ghidra): document an interim Cortex-M language override

Ghidra imports little-endian ARM ELF files as ARM:LE:32:v8, which
defaults to ARM state and marks Thumb code only from $t mapping symbols
and odd function-symbol addresses. Unpinned arm ELF already admits
REA_GHIDRA_LANGUAGE_ID, so document ARM:LE:32:Cortex (ARMv6-M, ARMv7-M,
ARMv7E-M) and ARM:LE:32:v8-m (ARMv8-M) as an unverified interim.

State the override's full effect while it is set: raw-binary, DOS, DOL,
PSP and language-pinned lanes such as RISC-V refuse it, and REA asks
Ghidra to import every other target in the ARM language, because it does
not check the override's processor against the target.
2026-10-30 22:28:06 +08:00
morluto cd878ad57d refactor(ci): unify lane ownership and guard Python tests (#8150)
* refactor(ci): unify lane ownership and guard Python tests

* fix(ci): reject entirely skipped bridge subtests
2026-10-30 21:23:33 +08:00
吴佳浩Alben 8f920bb65b fix(javascript): emit no http request for a target that is not a URL (#8147)
* fix(javascript): emit no http request for a target that is not a URL

`node:http` parses a string target with `new URL(...)` before a request object
exists, so a literal that is not an absolute URL throws `ERR_INVALID_URL`
synchronously. The analysis reported `endpoint` at `resolution: "complete"` for
those calls, and the graph then projected `constructs-request` as `resolved`.

Measured on Node 24.22.1:

    http.get("nope")                     -> THREW ERR_INVALID_URL
    http.get("example.test/api")         -> THREW ERR_INVALID_URL
    http.get("/api/items")               -> THREW ERR_INVALID_URL
    http.get("/api/items", { host })     -> THREW ERR_INVALID_URL
    http.get("")                         -> THREW ERR_INVALID_URL
    http.get("//example.test/x")         -> THREW ERR_INVALID_URL
    http.get("http://")                  -> THREW ERR_INVALID_URL
    http.request("nope")                 -> THREW ERR_INVALID_URL
    https.get("/api/items")              -> THREW ERR_INVALID_URL
    --- accepted ---
    http.get("http://example.test/x")    -> OK ClientRequest
    http.get("HTTP://example.test/x")    -> OK ClientRequest
    http.get("http:/x")                  -> OK ClientRequest
    http.get("http:///x")                -> OK ClientRequest
    http.get("http:x")                   -> OK ClientRequest

`URL.canParse` is the exact predictor of that branch, so a relative spelling such
as `"http:x"` keeps its operation. It already has precedent in this codebase
(`javascriptPackageImportAliases.ts`).

Two boundaries are deliberate. `fetch` and `new WebSocket` are host-supplied
globals: a browser resolves a relative target against the document URL, so their
spellings are not judged by Node's absolute-URL rule. A protocol mismatch such as
`http.get("ftp://...")` is also left alone because an options object can rescue
it, so it is not a provable throw.

The guard is scoped to `method === "get" | "request"`, which only
`requestMethod`'s `http`/`https` builtin branch can produce.

* chore: retrigger title and readiness checks after the title was corrected
2026-10-30 19:42:11 +08:00
morlutoandCursor Agent 77b09314ff test(ci): require provider checks and reduce test startup (#8145)
* test(ci): require provider coverage and reuse test sessions

* fix(ci): prepare the runtime before Python producer tests

The fast Python job ran the suite on a fresh checkout, so analysis-efficiency
checks failed without compiled process modules. Install locked dependencies
and prepare the runtime before the suite.

* fix(ci): keep provider test reports inside the checkout

The provider-report reader accepted an arbitrary path from argv. Resolve the
path and reject anything outside the checkout before reading it. The packet
and signal workflows write those reports under .cache.

* fix(ci): require a test module in each named Python bridge family

Discovery still runs every bridge test. The mitmproxy, native, and pwndbg
families must each keep a test module, so deleting one cannot pass through
the others.

---------

Co-authored-by: Cursor Agent <[email protected]>
2026-10-30 19:34:19 +08:00
HenglieandCursor Agent b1ff7cdacc fix(hopper): distinguish an absent demo dialog from publisher rejection (#8074)
* fix(hopper): distinguish an absent demo dialog from publisher rejection

The Linux demo watcher collapsed an admitted launcher with no first-run dialog into unsupported_demo_dialog. Report that post-authentication timeout as demo_dialog_absent, keep the unsupported dialog code for an observed but unrecognized Hopper window, and allow REA_HOPPER_DEMO_DIALOG_TIMEOUT_MS to widen the fixed wait. Closes #8008.

* fix(hopper): tolerate callers without the dialog timeout argument

* fix(hopper): report an invalid demo dialog timeout as its own failure

An invalid REA_HOPPER_DEMO_DIALOG_TIMEOUT_MS exited 73 and was shown as
invalid_launch_command. Exit 82 is invalid_dialog_timeout. The public error
names the setting, shows the rejected value and source, lists the allowed
range, and says to restart REA.

Drive the demo watcher with controlled windows and time. No window exits 81,
a Hopper window without a supported dialog exits 76, and a dialog that arrives
after the old 10 second limit is still accepted.

* fix(hopper): keep startup stderr capture on AdapterFailure

DemoWatchStop was inserted above with_stderr, so launch failures called a
method they no longer had and became unexpected_failure. Restore with_stderr
on AdapterFailure and reject a watcher fixture that cannot capture stderr.

---------

Co-authored-by: Cursor Agent <[email protected]>
2026-10-30 18:29:18 +08:00
Kaoru0822andClaude Opus 5.5 767f6afc18 perf(javascript): index member-effect paths for projected reads (#8108)
* perf(javascript): index member-effect paths for projected reads

Each projected read scanned every member-write record of its binding and
tested the record path against the selection. On the #7774 reproducer
only 3.7% of scanned records pass that test. Index each binding's
append-only records by path in a lazily built trie that extends itself
with newly appended records, and visit only records whose exact keys a
selected choice names or whose keys are wildcards. Candidates come back
in record order and still pass the exact predicate, now owned next to
the index as semanticEffectPathMatches.

The semantic IR and the complete analyze-javascript-application Evidence
are byte-identical. On the full reproducer each worker phase drops from
about 80 s to 29 s, and to 8 s together with #8103.

Refs #7774

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* perf(javascript): stop escape checks at the first indexed match

The escape check only asks whether any matching escape is unsuperseded,
so it does not need ordered candidates. Share one trie traversal between
the ordered write candidates and an early-exit visitor for this check.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-30 17:52:46 +08:00
morluto 6bb831df60 chore(security): configure dependency and ownership reviews 2026-10-30 17:35:34 +08:00
Kaoru0822andClaude Opus 5.5 37342ecde0 feat(native): open extension, framework, XPC and plug-in bundle directories (#8117)
* feat(native): open extension, framework, XPC and plug-in bundle directories

open_binary and the CLI inspection commands admitted only .app
directories and pointed every other Apple bundle at the JavaScript
analyzer. Resolve .appex, .framework, .xpc, .systemextension, .bundle
and .plugin directories through their Info.plist CFBundleExecutable, in
the Contents or flat root layout, plus the versioned framework layout
under Versions/Current. Exactly one layout may declare the bundle and
every resolved path must stay inside it.

Unresolvable bundles fail closed with typed constraints, probed paths
and the Info.plist that was read. Other Apple bundle types are refused as
unsupported instead of being offered the JavaScript route. Resource-root
discovery shares the same layout rule, so inspect_plist reads a
versioned framework's Info.plist. The recognized bundle vocabulary now
has one owner in the domain.

Refs #4310

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(native): refuse kernel extensions and state bundle error fields

Name the Apple bundle types binary routes recognize but do not open
(.dext, .kext, .qlgenerator, .mdimporter) explicitly, so a kernel
extension is refused as unsupported instead of receiving the JavaScript
route. Document which bundle refusals carry probed_paths and info_plist,
and make success tests throw the typed admission error before asserting.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* feat(native): open flattened frameworks through Resources/Info.plist

Electron and Gecko apps ship frameworks without Versions/: the program
file sits at the framework root beside Resources/Info.plist, the layout
#8101 taught bundle anatomy. Read that layout for a framework without
Versions/, and keep the versioned layout for one with it, so a versioned
framework's root Resources link is never read as a second declaration.
The resource root reports it as flattened-framework.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-30 02:27:52 -07:00
Kaoru0822andClaude Opus 5.5 a47dcca010 perf(javascript): keep one member-effect record per stored fact (#8103)
Member-mutation indexing appended a binding write or escape record for
every traversal state that reached it. States that differ only in
coverage fields the record never stores (origin, owner initializer,
getter widening) produced identical records, and each later receiver
query rescanned all of them. Record each stored fact once, alongside the
existing required-module effect deduplication. Propagation is unchanged.

Effect ordering also re-walked the same top-level statement ancestry for
every candidate record. Cache it with the other initializer positions,
and test the selected path before the costlier receiver comparison.

On the #7774 reproducer the semantic IR and the complete
analyze-javascript-application Evidence are byte-identical, while the
CLI run drops from 146 s to 40 s.

Refs #7774

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-30 02:26:01 -07:00
Kaoru0822andClaude Opus 5.5 6b68fb642a fix(ci): remove unused exports that fail deep static checks (#8095)
* fix(ci): remove unused exports that fail deep static checks

Knip on main reports three analysis-view hub re-exports, two type
re-exports, an unused artifact-format guard and an alias constant. Drop
the dead re-exports and guard, and give the debugger control-reply budget
its own literal: it is a separate budget from the caller command-output
default, as its comment already states.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(config): read the Rizin control budget from its own constant

The Rizin control-reply admission limit still took its value from the
caller command-output default, while GDB used DEBUGGER_CONTROL_REPLY_BYTES.
Read the dedicated constant in both so the control budget has one owner.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-30 02:25:43 -07:00
Kaoru0822andClaude Opus 5.5 81378efb71 fix(electron): keep literal IPC sends ambiguous beside unresolved handlers (#8120)
* fix(electron): keep literal IPC sends ambiguous beside unresolved handlers

A main handler on an unresolved channel can receive any literal renderer
send of a compatible mode, but pairing ignored it unless it had a scoped
receiver. The send was then reported unpaired, or paired with another
handler. Treat an unresolved-channel handler as a competitor that never
pairs.

The wrapper pass seeds only calls with a literal first argument, so it now
records which invocations of a dynamic site it traced. The syntax finding
gains `invocation_channels: "literal"` only when every invocation is a
traced direct call of an unexported local function supplying a literal
channel. Such a site is already represented by its per-invocation findings
and does not compete.

Fixes #8118

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(electron): keep guarded wrapper registrations competing in IPC pairing

A guarded wrapper registration keeps its predicate on a call path and has
no IPC finding. It still counted toward `invocation_channels: "literal"`,
so pairing lost it: a send on that channel was reported unpaired, or
paired uniquely with a direct handler. The enumerated site now carries
these channels as `guarded_invocation_channels`, and each one competes for
its own channel without pairing.

Index each binding's direct call sites by appending in place. Spreading
the earlier calls on every reference was quadratic in a file's call count.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-30 02:21:23 -07:00
ShuaixinHuang 72039ecca9 fix(environment): retain conflicting scenario MIME uncertainty (#8082)
* fix(environment): retain conflicting scenario MIME uncertainty

* test(mcp): assert structured absolute-path diagnostics

* test(mcp): assert typed selector and stdio diagnostics
2026-10-30 17:19:46 +08:00
ShuaixinHuang d77104daab fix(fit): preserve leading U+FEFF in property strings (#8083) 2026-10-30 17:19:38 +08:00
Kaoru0822andClaude Opus 5.5 cce33142ed fix(apple): charge the XML plist decoder's DOM before decoding (#8115)
* fix(apple): charge the XML plist decoder's DOM before decoding

The XML preflight charged 4 bytes per character and 256 bytes per start
or end tag. The decoder builds an @xmldom/xmldom DOM that measured
1,000-1,160 bytes per element and up to 240 bytes per text, comment or
CDATA node, which were not charged at all. A 1.75 MB plist of 230,000
<true/> lines was admitted under the 64 MiB decode budget and peaked at
249 MiB of heap; shipped Apple plists peaked at 80-104 MiB.

Charge each element and each text, comment, CDATA or DOCTYPE node at a
measured upper bound, keeping the character and number-projection terms.
The binary estimator already bounds its decoder and is unchanged.

Fixes #8113

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(apple): charge every XML element, attribute and reference

The XML preflight recognized only element names starting with an ASCII
letter, so names such as é, _, a CJK or astral character escaped the
element charge: 100,000 <é/> elements (0.5 MB) built an 84 MiB DOM before
the plist decoder rejected the tag. Attributes and entity or character
references were also uncharged; 20 attributes per element measured about
210 bytes each, and each reference about 26 bytes beyond its characters.

Classify markup by the character after "<" instead of a name pattern, so
anything other than an end tag, comment, CDATA, PI or declaration counts as
an element (saxes rejects malformed markup before the DOM is built). Charge
each unquoted "=" in a start tag as an attribute node and every "&" as a
reference.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(apple): charge XML references only where the parser expands them

The previous commit charged every "&" in the document as a reference,
including literal ampersands inside CDATA sections and comments. A valid
plist with one CDATA string of 1,000,000 ampersands was refused although
it is a single node the decoder holds as one string.

Count "&" during the existing token walk, only in text runs and in the
attribute values of start tags, so comments, CDATA, PIs and declarations
add no reference charge and the scan stays a single pass.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-30 01:29:16 -07:00
Kaoru0822andClaude Opus 5.5 c5f50e80d8 fix(captures): admit browser-serialized HAR request URLs (#8112)
* fix(captures): admit browser-serialized HAR request URLs

[email protected] checks request.url with ajv-formats' strict RFC 3986
`uri` format. Browsers record and send URLs as WHATWG serializes them,
which keeps characters such as `|` and `{` unescaped in the query, so one
Google Fonts or GraphQL GET entry made inspect-web-network-capture and
environment-build reject the whole HAR.

HAR 1.2 defines request.url only as the absolute request URL. Own that
correction in the HAR validator: every URL the RFC 3986 check accepts
stays accepted, and a URL that WHATWG URL serialization reproduces
unchanged is accepted too. Spellings WHATWG would rewrite (a backslash
authority, a tab, a space) and relative references still fail.

Fixes #8110

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(captures): state that RFC 3986 request URLs stay admitted

The HAR request URL rule is "passes RFC 3986, or WHATWG serialization
reproduces it unchanged". A spelling WHATWG would rewrite therefore fails
only when it also fails the RFC 3986 check; an uppercase host still
passes. Say so in the docs and the validator comment, and pin the
accepted uppercase-host case in the CLI test.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-30 01:03:35 -07:00
Kaoru0822andClaude Opus 5.5 9d68aac76d fix(javascript): stop reporting self-defaulted receivers as module loads (#8109)
The CommonJS loader search treated any repeated state on its own ancestor
chain as an unknown loader. A self-default such as
`session.messages = session.messages || []` forms that cycle, because a
member read inside the assigned value was attributed to the later call's
read site and selected the same write again. Each `push` on the value was
then reported as an unresolved `require` load, and graph coverage became
partial.

Record a member read inside a flowed value as a depth-1 captured selection
at its own position, as member mutations already do for flowed origins.
Treat a revisited state as adding no source; the cycle's other entries are
still expanded once each. Remove the superseded ancestor tracking.

Fixes #8102

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-30 01:01:21 -07:00
Kaoru0822andClaude Opus 5.5 24398a89e4 fix(source-maps): resolve empty sourceRoot sources against the map URL (#8107)
webpack 5 and tsc write "sourceRoot": "" for map-relative sources. Since
5f702dd6c the shared join added "/" for any non-null root, so these
sources resolved from the URL root: rxjs's ../../../src/internal/Observable.ts
under /npm/[email protected]/dist/cjs/internal/ became /src/internal/Observable.ts,
and xterm's webpack://xterm/./src/... became https://host/webpack://xterm/....

Treat an empty root like a null root in sourceMapReference, the single join
used by trace-web-source-location, analyze-javascript-application and the
web source-map fetcher. Chrome, Firefox and trace-mapping skip an empty root,
and tc39/ecma426#183 wrote the current steps to match them. Non-empty roots,
including "/" and repeated slashes (#6726), keep their existing behavior.

Fixes #8106

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-30 01:00:41 -07:00
Kaoru0822andClaude Opus 5.5 0ce76e70f4 fix(desktop): charge retained rows at their measured representation (#8105)
* fix(desktop): charge retained rows at their measured representation

The desktop scan reserved 1024 + 24 bytes per path character for every
row, omission and the root against its 32 MiB representation budget. Real
rows cost 800-940 heap and 490-640 JSON bytes, so the charge overstated
them 6-13 times and large macOS bundles stopped at 3,700-7,200 entries,
far below max_entries.

Charge each path at the larger of its UTF-16 heap size and its escaped
JSON size, plus the existing fixed row and omission overheads, in one
helper. The bound still covers the retained heap and the serialized
report, including control-character paths that expand six times in JSON.

Fixes #8104

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(desktop): charge every retained copy of a path

The tighter row charge counted each path once, but rows repeat it in
convention-fact values and the next probes those facts offer, and host
failure issues name the absolute path. Control-character paths under a
convention such as Content/Paks or resources/app.asar therefore serialized
47-80 MiB of reports within the 32 MiB budget.

Reserve each copy through the same text helper where it is stored: path
convention facts with their row, the probes any fact offers (at most two,
pinned by a projection test) with that fact, retained read ranges before
their read, and probe failure issues before they are recorded.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-30 01:00:28 -07:00
Kaoru0822andClaude Opus 5.5 6d19649c0b fix(apple): read flattened framework Info.plist from Resources (#8101)
A macOS framework without Versions/ keeps its binary, _CodeSignature/ and
Resources/Info.plist directly in the .framework, as Electron and Gecko apps
ship it. Bundle anatomy looked for a shallow bundle's plist only at its root
and reported info_plist_path: null under complete coverage.

Resolve a shallow bundle's root Info.plist first, then Resources/Info.plist,
the same layout a framework version directory uses. Keep the iOS platform
rule tied to a root-level Info.plist so a Resources/-only application root
is not classified as iOS.

Fixes #8100

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-30 01:00:06 -07:00
Kaoru0822andClaude Opus 5.5 bfa082f777 fix(analysis-view): keep inferred confidence on views of inferred Evidence (#8099)
createAnalysisViewEvidence chose view confidence from the view result kind
only, so every summary, page or item view of an inferred parent
(compare_source_to_bundle, inspect_evm_interface, partially resolved
inspect_il2cpp_metadata) was sealed as derived while keeping the parent's
authority. A projection never makes its facts more certain: an inferred
parent now yields inferred views through CLI and MCP alike. Observed and
derived parents keep their existing view confidence and identities.

Fixes #8098

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-30 00:59:53 -07:00
morlutoandCursor Agent 317bf3fe23 fix: report provider admission failures as typed codes (#8066)
* fix: report provider admission failures as typed codes

* fix(evm): keep limiter path on admission diagnostics

* fix: assert WABT and CBOR admission codes

* fix: keep admission details on returned failures

* fix(evm): detect repeated CBOR keys in the decoder

---------

Co-authored-by: Cursor Agent <[email protected]>
2026-10-30 06:48:01 +08:00
morlutoandCursor Agent 3b647132c6 fix(kuna): report document failures as typed codes (#8064)
* fix(kuna): report document failures as typed codes

Kuna JSON, graphs, xrefs, spans, and command checks threw Error and
published the sentence. Those refusals are now KunaDocumentError codes
with the field or path that failed. Output errors keep the code as
reason and the same facts in diagnostics.

* fix(kuna): keep incoming-reference address fields

---------

Co-authored-by: Cursor Agent <[email protected]>
2026-10-30 06:07:53 +08:00
morlutoandCursor Agent 249b90c5e3 refactor(javascript): break the semantic analysis import cycle (#8063)
* refactor(javascript): break the semantic analysis import cycle

Split shared semantic facts out of the analysis hub so projectors of
effects, evaluation, and identity no longer import one another in a cycle.

* refactor(javascript): share the import specifier reader

Read static imports in one module used by the boundary checker and both
cycle guards, so those rules cannot drift apart.

---------

Co-authored-by: Cursor Agent <[email protected]>
2026-10-30 06:07:49 +08:00
940d71a45a refactor(analysis-view): break the analysis view hub cycle (#8061)
* refactor(analysis-view): break the analysis view hub cycle

Move request fragments, the request union, and the result union out of
analysisView.ts so projectors no longer import the module that imports them.
The dispatcher re-exports the public schemas and seals projector results.

Co-authored-by: morluto <[email protected]>

* fix(analysis-view): check inferred projector results

Require each projector result to match the unsigned result union before
sealing it, and parse the cycle guard's imports with Babel so comments
and side-effect imports are not misread.

* fix(analysis-view): close IL2CPP and Android symbol view inputs

Admit those projectors through AdmittedView and check test selections
against their request schemas, so misspelled fields fail at the call site.

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
2026-10-30 05:41:58 +08:00
morlutoandCursor Agent 4a8d8f72b3 feat(debugger): share GDB and Rizin session caps as REA_ settings (#8056)
* feat(debugger): share GDB and Rizin session caps as REA_ settings

Move the duplicated deadline guidance into the owned debugger helper and
replace the shared session, queue, output, and 120s command caps with
launch settings that name their value, source, and range.

* test(gdb): assert the command-output setting on truncated exit evidence

The retained-output limitation now names REA_DEBUGGER_MAX_COMMAND_OUTPUT_BYTES
instead of the old fixed-cap sentence.

* test(gdb): assert command-output limit fields on truncated exit

A truncated GDB exit now carries the command-output setting, value, and
source on the cleanup error. The coverage test reads those fields.

* fix(debugger): keep control replies and evidence hashes inside their budgets

Point the session-cap mutation target at DEFAULT_DEBUGGER_MAX_SESSIONS.
Give startup and cleanup replies a fixed 16 MiB budget so a small caller
output cap cannot truncate them. Lower the command-output maximum so
escaped Evidence JSON stays under Node's string ceiling, and keep GDB
startup timeout diagnostics when the error is rebuilt.

* test(debugger): throw typed Result errors before success checks

Success cases no longer assert ok and return. They throw the typed
error first, then check session, deadline, and close fields.

---------

Co-authored-by: Cursor Agent <[email protected]>
2026-10-30 05:41:54 +08:00
morlutoandCursor Agent 395b598383 refactor(admission): own inspector, web, and CLI state per host (#8058)
* refactor(admission): own inspector, web, and CLI state per host

Replace the process-global inspector, web-bundle, guidance, and JavaScript
counters with objects a host passes in, so independent instances do not
share admission or cleanup.

* fix(admission): keep worker and file cleanup on the host

Web-bundle and JavaScript workers retry only their host registry, and
each Inspector admission authorizes and closes its own file scope.
Browser providers can share one web-bundle admission.

* test(inspector): gate one admission's file cleanup

The isolation test delayed the first ArtifactResourceScope.retryCleanup
call, so a second call on a shared ElectronFileScope still finished.
Hold first.files.retryCleanup until the other admission's close
finishes, then release and join before restoring the method.

---------

Co-authored-by: Cursor Agent <[email protected]>
2026-10-30 05:23:33 +08:00
morlutoandCursor Agent b663c79552 fix(domain): replace bare parser errors with typed codes (#8057)
* fix(domain): replace bare parser errors with typed codes

OpenStep syntax, canonical JSON digests, managed-graph source checks,
frame fingerprints, and reference-flow invariants threw Error and
published the sentence. Each refusal now carries a code. Tests assert
that code, including OpenStep position, instead of the previous prose.

* test(domain): pin OpenStep refusal codes and positions

The syntax cases accepted any code and any offset. Each container
refusal now expects its code and source position, including the deep
suffix that changes (x from unterminated_array to expected_separator.
Scalar failures stay identical to the pinned decoder.

* test(filesystem): assert OpenStep keyed-archive codes

The deep-suffix keyed archive cases compared ArtifactReaderFailure
message text with the pinned decoder sentence. They now require
reason format and the OpenStep code and source position on the cause.

---------

Co-authored-by: Cursor Agent <[email protected]>
2026-10-30 05:23:10 +08:00
a4c183b750 refactor(adb): move device and client operations out of the provider switch (#8062)
* refactor(adb): move device and client operations out of the provider switch

Keep AdbProvider as the session owner. Device listing, device inspection,
and client identity now run in AdbDeviceOperations, and shared listing
execution construction lives next to the client identity result.

Co-authored-by: morluto <[email protected]>

* refactor(adb): use the shared client and provider identity types

Drop the ClientIdentity and ProviderIdentity aliases so method signatures
name AdbClientResult and AdbProviderIdentity directly.

Co-authored-by: morluto <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
2026-10-30 05:02:41 +08:00
morlutoandCursor Agent fa1aa64ce4 fix(errors): store resource-constraint recovery as one shape (#8055)
* fix(errors): store resource-constraint recovery as one shape

AnalysisResourceConstraintError kept remediationAction, fixedAllowance,
and retryable as independent optionals, so a refusal could claim both a
fixed allowance and a separate remediation. The constructor now stores
the existing ResourceConstraintRecovery union, and the three accessors
read that object.

* test(errors): cover competing resource-constraint recovery fields

A runtime options object can still carry both a fixed allowance and a
remediation action. The constructor keeps the allowance and drops the
other field; the regression now supplies both.

---------

Co-authored-by: Cursor Agent <[email protected]>
2026-10-30 04:50:02 +08:00
morlutoandCursor Agent b894be6361 fix(native)!: report NativeAOT failures as reason codes (#8053)
* fix(native)!: report NativeAOT failures as reason codes

PeImage admission failures and parser recoveries used to publish
Error.message as NativeAotReport.reason. The report is now a union on
status, and reason is a typed code with the fields that code needs.
Complete reports keep reason null and a located header. Partial and
not_applicable reports carry a code. Caller budget mistakes throw
AnalysisInputError or AnalysisResourceConstraintError with a diagnostics
code instead of a bare Error.

* fix(native): keep out-of-domain hydration pointers partial

An inline pointer outside the unsigned 64-bit domain made
writeBigUInt64LE throw RangeError, and the hydration catch rethrew it
instead of recording a partial report. Those targets are now an
admission detail. The load-image summary schema still accepts the
optional Ghidra extension's string reason.

---------

Co-authored-by: Cursor Agent <[email protected]>
2026-10-30 04:49:57 +08:00
morlutoandCursor Agent 5f9187cd27 refactor(artifacts): describe formats in one registry (#8054)
* refactor(artifacts): describe formats in one registry

Reader construction, classification retention, container role, and
extraction now read one descriptor per domain format id. unknown has
no reader, and zip package families share one capability row.

* fix(artifacts): keep reader presence in format descriptor types

Descriptor helpers no longer widen every format to an optional reader,
so ArtifactReaderFormat stays the union of formats that construct one.

---------

Co-authored-by: Cursor Agent <[email protected]>
2026-10-30 04:27:17 +08:00
morlutoandCursor Agent 1c14f66356 refactor(contracts): register CLI and MCP tools by family id (#8051)
* refactor(contracts): register CLI and MCP tools by family id

Walk the published tool-family list once for each adapter so a new
family id must supply both a CLI registrar and an MCP registrar.

* fix(cli): keep family registration off the contract tables

Walk family ids from the startup-safe list, and register the web script
commands with the application family that owns their MCP contracts.

---------

Co-authored-by: Cursor Agent <[email protected]>
2026-10-30 04:25:48 +08:00
HawkForgeandClaude Opus 5.5 1da304b282 fix(adb): verify pushes to long device paths (#8047)
* fix(adb): verify pushes to long device paths

sha256sum echoes the device path, so a correct digest line for a path
near 1 KiB exceeded the fixed 1 KiB capture budget. The resulting
resource failure was then reported as device_digest_source "unavailable",
the same outcome as a device without sha256sum. Size the budget to the
path, and return a capture-budget failure as a resource constraint
instead of an unverified success.

Fixes #3369

* Keep adbDeviceAnalysis.ts as on main

* test(adb): keep only the long-path digest test

Main already covers an oversized checksum answer as a resource
constraint with the completed push retained.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-30 02:48:51 +08:00
ShuaixinHuang da5ec6b62c fix(macho): preserve literal segment name BOMs (#8040) 2026-10-30 02:47:33 +08:00
morlutoandCursor Agent c0c3902207 fix(native): preserve Mach-O target selection in macOS tools (#7831)
* fix(native): preserve Mach-O target selection in macOS tools

* fix(native): retain exact Mach-O slice signature coverage

* fix(native): admit x86_64 LIB64 signature selectors

* test(native): match signature checks to actual slices

* fix(native): recognize vtool refusals on private Mach-O aliases

Match vtool's container refusal to the operand the command received so a
parenthesized universal path keeps completed facts and the refusal
limitation. Expect codesign -a only when the target format is universal.

---------

Co-authored-by: Cursor Agent <[email protected]>
2026-10-30 00:03:23 +08:00
morlutoandCursor Agent 845080aef6 fix(session): align resource-only status and snapshot recovery (#7896)
* fix(session): align resource-only status and snapshot recovery

* fix(session): report independent workflows during resource sessions

* test(mcp): honor SQLite runtime availability

* test: compare resource snapshot guidance with its formatter

Assert projected error codes and the owning remediation action for
resource-session snapshot refusals, and type MCP error text as a closed
content item.

---------

Co-authored-by: Cursor Agent <[email protected]>
2026-10-30 00:03:19 +08:00
morluto bf115be2cd fix(evidence)!: report storage publication limits (#7887)
* fix(evidence): report storage publication limits

* test(evidence): cover staged storage failure cleanup
2026-10-29 23:03:09 +08:00
morluto 539a494d25 docs(setup): restore target and Hopper launch settings (#7902) 2026-10-29 23:03:02 +08:00
morlutoandCursor Agent ec4a0a01c4 fix(hopper): reconcile observed caller relationships (#7953)
* fix(hopper): reconcile observed caller relationships

* fix(hopper): retain completed caller scans on failure

Attach the native reply and collected forward facts when reconciliation
fails or is cancelled, and check scan stop codes separately from their
wording. Live observations keep REA evidence ids rather than provider
supplied ones, and the real Hopper verifier uses advertised MCP schemas.

* fix(hopper): answer caller-scan methods in the package fixture

The packaged Hopper stand-in echoed unknown bridge methods, so
analyze_function rejected rea_caller_scan_target as an invalid scan
target and the native package baseline returned unreadable_output.

* fix(hopper): share caller identities in failed scan observations

Failure partials repeated each caller identity under every destination.
The page budget counted compact replies, then schema parsing copied the
expanded form. Identities are stored once and destinations cite addresses.

* refactor(config): own the Hopper caller-scan budget default

The environment schema hard-coded the 2000 ms scan default. The domain
limit owner now names that millisecond budget and the schema reuses it.

* test(mcp): assert bundle path issues by field

compare_bundles reports each relative path as an invalid_format issue
instead of one joined diagnostic sentence.

* test(flutter): assert ABI selector refusals as structured issues

inspect_dart_aot reports the ABI label as an issue on path abi. The
message is the selector contract, without an abi: prefix.

* fix(session): retain caller observations that cannot be replayed

A missing document selector blocks snapshot replay. Backward call-graph
and trace reads still keep the native result and forward observations,
and the workflow Evidence links those records.

* fix(session): keep decompile bodies out of the in-memory ledger

Caller and callee walks still stay in Evidence without a document.
procedure_pseudo_code does not, so decompile_all leaves each body in the
file-backed export instead of retaining the whole export in memory.

* fix(hopper): own caller-scan recovery and the page byte allowance

Scan facts stay in the domain. Environment recovery, including the time
budget bounds and the fixed allowances with no runtime override, is
appended from configuration. Setup forwards the budget, and the bridge
uses one byte allowance for instructions, native callers, and pages.

* fix(hopper): retain compact caller references within the scan budget

Recovered call sites stay in the address form the scan already counted.
Expanded identity copies are measured before allocation and refused when
they exceed the retention budget. Instruction lists are not part of the
retained reply.

* fix(session): omit instruction payloads from retained caller evidence

Documentless caller and callee reads stay in the ledger for the session.
Their evidence keeps call sites and drops instruction lists the scan
already consumed.

* fix(hopper): keep the owning caller address on each compact call site

A page reference marked source_procedure "caller" belongs to that page
item's caller. Saved sites record that address, and an owner missing from
the callee inventory is stored once instead of copied onto every site.

* fix(session): leave Hopper reply shaping to the Hopper adapter

Documentless caller evidence stores the adapter's raw result unchanged.
The session no longer reads native_result.instructions.

* test(hopper): throw typed scan failures before reading results

Successful caller-scan results now throw the typed error instead of
returning after an ok check. Expected failures assert structured tags,
stop codes, and reported limits.

---------

Co-authored-by: Cursor Agent <[email protected]>
2026-10-29 22:54:58 +08:00
8188497f13 feat(artifacts): inspect and extract Bun standalone module graphs (#6998)
* feat(artifacts): inventory and extract Bun standalone executable module graphs

Adds a Bun standalone artifact reader for Mach-O, PE, ELF and appended
trailers, supporting the 36-byte and 52-byte module record layouts. It
preserves module names, entry-point details and raw metadata, and
exposes contents, sourcemaps, bytecode and module-info as separate
entries for safe extraction without executing the binary. Parsing
rejects malformed or ambiguous graphs and enforces module-count,
retained-byte and expansion limits. Adds synthetic parser tests, CLI and
MCP coverage, agent skill guidance, and a verification script for
Bun-produced executables.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_016y53PdpyMAp9RSW65fatHe

* fix(artifacts): bound Bun section probes and reject truncated sections

Split Bun recognition into a bounded probe that reads only section
headers, length prefixes, and the Offsets/trailer suffix, and a single
payload load used by the reader. Inventory classification and extraction
admission no longer allocate the whole graph.

Match ELF `.bun` section names by comparing only the name bytes and
terminator, so many headers sharing one long name cost constant work each.
Reject sections whose declared extent ends past EOF instead of reading a
silently shortened window.

Launch Bun, Docker, and REA in the standalone verifier through the shared
owned verifier commands, and release the runtime root only after those
command families have settled.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_016y53PdpyMAp9RSW65fatHe

* style(artifacts): format Bun reader integration

* fix(contracts): describe static Bun inventory facts

* fix(artifacts): bound Bun metadata reads and combined path growth

---------

Co-authored-by: Matt Van Horn <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: gensokyo-dev <[email protected]>
2026-10-29 22:31:50 +08:00
gensokyo-dev cac6e0613e fix(ci): enforce architecture guards before dependency setup (#8027)
* fix(ci): enforce architecture guards before dependency setup

* test(ci): bind cache policy to preceding Node setup
2026-10-29 21:29:15 +08:00
a108a1b817 fix(cli): report incomplete debugger cleanup as typed outcomes (#8021)
* fix(cli): report incomplete debugger cleanup as typed outcomes

debug-with-gdb and debug-with-rizin hand-rolled final cleanup in
try/finally blocks: a failed or interrupted final close only logged
and assigned the process exit status, so callers still received
complete successful Evidence with no typed error or partial
observation.

Both commands now route through the shared close owner used by the
other one-shot CLI commands (deliverCliAnalysisEvidence /
finishAnalysisCommand) via a small closeDebuggerCommandSession
helper. A rejected closeAll projects as ProviderCleanupError, which
the owner turns into cleanup_incomplete retaining the completed
Evidence as partial observation; a signal observed after finalization
projects as cancelled with the same retained observation. The
per-session close failure still only logs, preserving the rule that
the retained owner's closeAll decides completion.

Fixes #7378. Fixes #7429.

* test(cli): reject unmodeled GDB requests in cleanup regressions

* test(cli): match modeled GDB requests exactly

* Update tests/boundary/cli/gdbStartupCleanupCli.test.ts

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

---------

Co-authored-by: morluto <[email protected]>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-10-29 20:52:03 +08:00
gensokyo-dev b3d31ed106 fix: preserve nullable source-map facts and streamline model tests (#8018)
* test: repair MCP validation assertions and reuse model sessions

* test(ci): audit constrained guidance bundle fingerprints

* fix(browser): distinguish empty and absent source-map declarations

* refactor(browser): encode script metadata groups once

* test: assert structured path errors without freezing prose
2026-10-29 20:35:25 +08:00
morlutoandCursor Agent 34577eb1cc fix(filesystem): preserve undecodable symlink targets (#7935)
* fix(firmware): preserve symlink target bytes

* fix(apple): preserve undecodable dylib link targets

* style(tests): fix CI formatting

* test(firmware): select the named contract directly

* test(firmware): record raw bytes for a decodable symlink target

* test(mcp): assert bundle path issues by field

compare_bundles reports each relative path as an invalid_format issue
instead of one joined diagnostic sentence.

* test(firmware): stat raw links without following them

access follows a symlink, so a published dangling raw-link still looked
absent. lstat and readlink inspect the link itself.

---------

Co-authored-by: Cursor Agent <[email protected]>
2026-10-29 20:07:30 +08:00
morlutoandCursor Agent e124bdfc54 fix(cli): preserve literal Markdown rendering (#7938)
* fix(cli): preserve literal Markdown rendering

* fix(cli): retain literal Markdown attributes

* fix(cli): preserve literal punctuation in markdown

* test(cli): update escaped empty object expectations

* style(cli): format the literal empty-object Markdown expectation

* test(mcp): assert bundle path issues by field

compare_bundles reports each relative path as an invalid_format issue
instead of one joined diagnostic sentence.

* test(flutter): assert ABI selector refusals as structured issues

inspect_dart_aot reports the ABI label as an issue on path abi. The
message is the selector contract, without an abi: prefix.

---------

Co-authored-by: Cursor Agent <[email protected]>
2026-10-29 20:07:17 +08:00
记得吃中饭andgensokyo-dev 22cab8215a feat(artifacts): inspect and extract .NET single-file bundles (#3787)
* feat(artifacts): inventory and extract .NET single-file bundles

* fix(artifacts): retain bundle source I/O failure reasons

* docs(artifacts): record Linux bundle regression verification

* fix(artifacts): preserve bundle source failures and cancellation

* docs(artifacts): record synchronized bundle verification

* fix(artifacts): bind bundle extraction to inventoried host

* docs(artifacts): record bundle verification outcome

* fix(artifacts): bound bundle paths and suggest extraction

* fix(artifacts): preserve nested executable container boundaries

* test(artifacts): verify projected .NET members and simplify extraction admission

* fix(artifacts): explain fixed bundle limits at their producing boundary

---------

Co-authored-by: gensokyo-dev <[email protected]>
2026-10-29 20:06:58 +08:00
morlutoandCursor Agent 5c05eb88e5 fix(javascript)!: preserve operator influence in semantic traces (#7939)
* fix(javascript)!: preserve operator influence in semantic traces

* test(javascript): align semantic influence regressions

* style(javascript): format semantic IR

* test(javascript): follow literal argument seed influence

* fix(javascript): keep one literal node per operator operand

Reuse an existing literal for operator influence, and treat TypeScript
assertion wrappers as transparent when deciding whether a call result is used.

* test(mcp): assert bundle path issues by field

compare_bundles reports each relative path as an invalid_format issue
instead of one joined diagnostic sentence.

* fix(javascript): index operator literals by range and initializer

Repeated string operands rescanned every retained literal and every
initializer flow. Lookup now uses those indexes, and a 1200-declaration
file still projects one literal per operand.

* fix(javascript): keep literal and call facts on their own occurrences

Operator literals match their own source range, so a nested function does
not inherit the enclosing declaration's literal. A direct argument still
reaches the call, while the assigned result follows modeled returns.
for-loop initializers and updates are discarded call results.

* test(javascript): expect conditional operand literals beside binding values

A conditional binding keeps one candidate literal per union value and one
operator literal per operand token. Those are different facts, so the
aggregate payload check counts four nodes and compares literal seeds in
query order.

* perf(javascript): index call return flows by call site

projectCalls filtered every call return for each assigned result. Group
those flows once while projecting returns-to-call edges.

---------

Co-authored-by: Cursor Agent <[email protected]>
2026-10-29 20:06:55 +08:00
morlutoandCursor Agent 6fa48f201f docs(apk): clarify rotated signer agreement (#7936)
* docs(apk): clarify rotated signer agreement

* test(mcp): assert bundle path issues by field

compare_bundles reports each relative path as an invalid_format issue
instead of one joined diagnostic sentence.

* fix(javascript): ignore worker frames for a different source job

A failure frame for another job was dequeued as an output error, so a
later caller abort lost the race and was not reported as cancellation.

* test(flutter): assert ABI selector refusals as structured issues

inspect_dart_aot reports the ABI label as an issue on path abi. The
message is the selector contract, without an abi: prefix.

* fix(javascript): check each worker frame condition once

A ready frame is rejected on its own. A non-ready frame for another job
keeps waiting. The following id comparison repeated that skip, so it
never ran.

---------

Co-authored-by: Cursor Agent <[email protected]>
2026-10-29 20:00:02 +08:00
morlutoandCursor Agent 1ef5918179 fix(browser): preserve source-map declaration uncertainty (#7945)
* fix(browser): preserve source-map declaration uncertainty

* style(tests): fix CI formatting

* fix(browser): retain known source map differences

* fix(web): bind map comparisons to script metadata

* fix(browser): match source maps with partial script metadata

Keep each script's observed hash and declaration constraints instead of
dropping a metadata field for the whole URL group.

* test(flutter): assert ABI selector refusals as structured issues

MCP input validation reports the ABI path and the schema message separately.
The old concatenated "abi: ..." sentence is no longer the delivered error.

* fix(web): prove large source-map changes without factorial search

Groups larger than eight scripts were reported as unexplained, so nine
x.map declarations plus one omission compared as unknown against ten
y.map declarations. Declaration counts reject impossible placements, and
augmenting paths assign the rest without retrying identical scripts.

* test(mcp): assert bundle path issues by field

compare_bundles reports each relative path as an invalid_format issue
instead of one joined diagnostic sentence.

* fix(web): match repeated source-map scripts by count

Identical declarations share one group, and a shared omission pool is
counted once. A 2000-script self-compare no longer builds a dense
assignment graph.

---------

Co-authored-by: Cursor Agent <[email protected]>
2026-10-29 18:50:26 +08:00
51ad84516e feat(environment): serve sorted creates, listed references and toggles (#7600)
* feat(environment): serve the writes callers make without recorded form fields

- A create member a recording sent empty ("", null, false, [] or {}) is no
  longer required; a call that leaves it out is applied as sending that value
  (body.blank).
- List views render envelope members that list the listed entities' ids
  (Paperless's `all`), so creates in such collections are confirmed.
- Updates move their entity in collections kept sorted by a field they write,
  and a failed whole-collection read after an update can teach that order.
- A later create that stores an unruled field otherwise is withheld at the
  create, and an item read may fill values a create's answer had not loaded.
- An unsent field respelled between null, [] and {} is unchanged.
- The skill asks for one create with every field and one with only what a
  person must give.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(skill): show how to read signed-in pages before recording

Agents guessed capture keys (artifacts, dom, final) to see pages behind
sign-in, which inspect_web_page cannot reach.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): accept every empty spelling recorded creates sent

A create that fills only what a person must give sends `null` where the
full create sent a string, and `null` or `false` for the same flag. The
merged plan now accepts each recorded empty value, and a call that leaves
the member out is applied as the earliest call sent it.

Also adds the spec folder for this change.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(skill): keep the vary-fields step neutral on UI or API writes

Agents read "the form" as a cue to write through the UI, where some
applications' request bodies are not exposed; a body-unusable UI write
now re-records once with request steps.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(environment): record verification for blank members and sorted writes

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): address review of blank members and sorted writes

- Bulk updates move their entities in turn against the others' earlier
  values and install the final order once, as replay does.
- A member's recorded empty values stay accepted once a later call teaches
  its default; a left-out member with a default keeps the default.
- Write answers spell empty lists and maps as their recorded answers did.
- A rename can teach a sort when the creates chose one by a field the
  update did not write, such as ids that ascend with titles.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* feat(environment): serve the writes that later reads explain

- An item read whose object is marked deleted (deleted, isDeleted or
  is_deleted true) agrees with an absent entity, as EspoCRM answers a
  deleted record; such an object never seeds or adopts an entity.
- maxSize, max_results and maxResults are page sizes, for whole-collection
  list lines and the list query's limit role.
- An item view also shows the fields only reads after its type's first write
  show, when no initial entity holds them (Vikunja's task subscription),
  without requiring them; write answers leave such a field out for entities
  without it, and it no longer withholds the write as incomplete-entities.
- The skill asks updates to change the fields a person would change, and
  expects a deleted object to read back as 404 or marked deleted.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): answer sent empty members as sent, and keep a placed create's order

- A write answer spells an empty list or map the call did not send as its
  recorded answer did; one the call sent is answered as sent, so a create
  sending `[]` is not answered `null` because the first recorded create was.
- A read listing the whole collection that placed a create leaves the create
  only the sorted inserts whose order that read follows, so a later rename
  cannot teach a sort an earlier read ruled out.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): answer bulk updates without fields only later reads show

A bulk update's answer renders each entity with the same allowance a single
update's answer has: a field only reads after the type's first write showed,
which no initial entity holds, is left out for an entity without it, so a
bulk update of a seeded task is no longer refused for lacking it.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(environment): correct how the EspoCRM contact update task passes

The agent trajectories show gpt-oss passes it by creating a replacement contact
and deleting the original, since the served update refuses emailAddress; the
update itself is refused in both builds.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): answer a read of a served-deleted entity marked deleted

An application that soft-deletes still answers a deleted record by id, marked
deleted. Serving now keeps an entity a served delete removed from a type whose
recorded item reads show that, until a reset or load and within the size
data/add loads, and answers a read of it with its last fields and the flag
true, instead of refusing the read.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): keep unloaded create answers, check bulk answers with sent members, narrow inserts only on collection-order reads

- A create whose item read loaded values its answer had not records them
  (`observation.read.unloaded`), and a served create's answer keeps them as
  the recorded answer showed them, a zero-time owner included, while the state
  holds the read's.
- Replay checks a bulk update's recorded answers with each target's sent
  members, as serving answers them, so echoed `null` and `[]` no longer make
  the plan body-unusable.
- A whole-collection read whose query sorts it shows the query's order, so it
  no longer narrows a create's sorted insert candidates.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(environment): record the census with main's unknown-clock rule merged

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* feat(environment): serve query numbers, counted values and made objects

- A query parameter accepts a number or boolean, which serving sends as its
  JSON text, as agents send page sizes and flags.
- A sequence may start at the value its first create took above every value
  its type held (Vikunja's per-project task index), and a later create's
  composed, template or started-sequence rule is adopted when it also spells
  the first create's value.
- A made-object rule explains an object the application makes with each
  created entity: members the request stamped, members holding the entity's
  own id, and counts one past the held objects' values.
- A create's answer names the id of the one object a one-element array member
  holds, so bulk creates link to their type.
- The skill reads each listed object by id before writing.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* feat(environment): let a linked create take path values it stores

A create linked to its type's own collection, whose path has no parameters,
accepts any path values when its plan stores each one in a field, as a Vikunja
task stores its project; replay and serving apply it alike.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): learn a sort from a rename only when moving its entities reaches it

Serving moves only the entities a write changes in a collection kept sorted,
so replay now tries a sort after a rename only when the entities no write the
read exercised changed already follow it. Seeded folders listed out of title
order no longer teach a title sort that serving would answer with another
order.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): keep unloaded create answers a sent value did not echo, and learn no sort from query-sorted reads

- A served create keeps a value its recorded answer had not loaded unless the
  call sent that member and the plan stores it as sent, so a create sending
  `active: false` is answered `false` as recorded while the state holds the
  read's `true`.
- A rename no longer teaches a sort from a whole-collection read its query
  sorts, whose ties need not follow the collection's own order.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(environment): verify query values, counted values and made objects

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): learn the soft-delete flag in the state view and serve by it

Extraction now records the member later item reads set true on a record the
application soft-deleted, and the item view carries it as `deleted_flag`.
Serving reads that fact instead of scanning recorded bodies itself, so one
owner decides what a soft delete looks like.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(environment): record the call-values checks at #7274 93821024

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): learn the soft-delete flag from item reads with a query too

A client may read a deleted record back only with a query, such as a
cache-busting parameter. Every successful read of the item tool now shows the
flag; only plain later reads still add fields.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(environment): record the call-values checks at #7274 45832406

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): learn sorts only from orders a list read confirmed

- A sort retry after a failed whole-collection read requires the entities no
  write changed since a list last showed them to follow it already, rather than
  excusing every entity the update's tool ever wrote: an earlier rename a list
  confirmed in place stays where it was.
- An update takes its creates' sort only from a create a list read showed
  placed; replay still tries a sort a create guessed from the initial order.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): drop entities read back as deleted and answer soft deletes as deleted

- A held entity a later read shows marked deleted is dropped, not adopted with
  the marked object's fields.
- Bulk-update replay renders answers with the optional and later-only fields
  serving uses, so a bulk update of created and seeded entities replays.
- A served delete of a soft-deleting type answers with the entity marked
  deleted, single and bulk.
- A deleted flag only query reads showed is a field of the item view, so a
  served tombstone keeps it where live records omit it.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): check made objects as recorded and keep their ids and clocks honest

- A made object's clock counts as a write clock, so a capture with no clock
  anchor withholds the create instead of stamping 1970.
- Members holding the entity's identity follow the id written, generated or
  sent, through the type's id field.
- A later create's made object is checked as recorded, before its own members
  are rewritten, so one naming another entity withholds the plan.
- A later create's template is adopted only when it spells that create's value
  too, not just when the first create's text holds its source value.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): move updated entities only by placed sorts and keep query-sorted reads from confirming order

Replay moved an updated entity by a sort a create only guessed, while the
update plan exported only placed sorts, so a read could confirm an update
by a move serving would not make. Both now use the placed sort. A passing
list read clears its entities from the moved set only when the state
decides the order of every entity it lists, so a read whose query sorts
it no longer hides an earlier move from a later sort check.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(environment): count six withheld plans that explain more, not seven

The bulk task create counted among them is served on this branch.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): try a learned sort by moving each pending write in turn, as serving does

#retrySort sorted the whole collection, which a list could match while
serving, moving each renamed entity in turn against the others' values
before their own writes, gave another order. The state now records the
values each write replaced, and the retry replays the writes since a
list in collection order last showed their entities.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): answer a served delete with the deleted flag its recorded answer showed

An application may answer a delete with the record as it was and mark it
deleted only on later reads. Delete answers, single and bulk, now take
the flag's value from the recorded answer; reads of a served-deleted
entity still show it marked deleted.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* chore(environment): set aside two hunks next to lines main's saved-values change rewrites

Main's #7267 changes the lines beside the blank-member docs and the
empty-spelling check in explainUpdate. Setting them aside lets that merge
apply cleanly; the commit after it puts them back on top of main's text.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* chore(environment): restore the set-aside hunks on main and bump the skill to 51

Puts the blank-member docs and the empty-spelling check back beside main's
slug and composed text and heldSlugRule. Main is at skill 50 with
different text, so this branch's skill changes take 51.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* chore(environment): bump the skill to 52 above #7260's 51

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* chore(environment): put back the call-values hunks main's merge took its side for, and bump the skill to 53

The merge of main kept main's side where it overlapped this branch, so it
could commit without a conflict. This restores this branch's side next to
main's: made-object agreement beside the & slug spellings, sequence starts
in the earlier evidence (now built by earlierOf), the combined value
imports, and recording step 2 beside main's UI-first step 3. Main is at
skill 50 and #7274 at 52, so this branch takes 53.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): keep sorts a list showed in place ruled out, and created answers' nested members as recorded

A whole list in collection order that showed a renamed entity where a
sort by the renamed field would have moved it now rules that sort out, so
a later rename of the same entity cannot teach it. A create's answer
leaves out members its item read added inside an object the answer
showed, as the recorded answer did; the state still stores them.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* feat(environment): serve writes whose calls send different members

Recordings now vary what a type's two creates and updates send, and REA
withheld writes where a later call left out what the first sent. An update
plan's required members narrow to those every call sent; a member one create
sent and another left for the application to number is a sequence that
takes the value a call sends; a PUT's left-out member the application
cleared becomes that member's default; and an update's clock is held as
the read after it spells it. The skill spaces creates (skill 54).

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): keep a later create's made object only with clocks its request stamped

followsMadeObjects only checked that a made object's clocks parsed, so a
later create whose object showed an older clock still passed, while serving
stamps the write's clock. Its clocks must now be in the rule's format and
within the stamp window of that create's request.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): accept sent values either way, check defaults against every call, and store update clocks as reads spell them

A sequence that takes sent values now accepts one a later create sent
after the first left the member to be numbered. A default an update learned
must agree with every update that left its member out, else the plan is
withheld. An update plan whose read-back spells a stamped clock at a coarser
precision keeps that read as observation.read, and serving stores the
update's clocks in its spelling while answers keep the response's.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): check recorded member and sequence values

* fix(environment): compare soft-deleted read fields

* fix(environment): reject contradictory learned sequences

* fix(environment): preserve initially deleted item observations

* fix(environment): index stored deleted entities separately

* fix(environment): replay lists that include deleted records

* feat(environment): serve sorted creates, listed references and toggles

A create a whole listing placed last is placed again by the sort a later
listing shows. A field holding the entities a sent list of ids names is a
reference with `many`, and a create that sent the member empty agrees with
one that named entities. A scenario request whose body Playwright did not
expose and that names no Content-Type sent no body. An item action calls
show flipping a boolean both ways learns a `toggle` rule. Skill 55 asks
recordings to run an action twice on one object.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): count a sequence number a create sends, as replay does

Serving now records a number a call sends for a `sequence` with `sent` in the
same count history it generates from, so after that entity is deleted the next
generated number still follows it, as replay's takeSequences does.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(environment): record the final gate run in the verification

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): make no count of a made-object member earlier creates held alike

A count only a later create shows starts at the exemplar's value, which is
right only while the exemplar is the one create accepted: creates accepted
after it showed the member unchanged, which a count would have moved. The
merge now refuses such a count, as the top-level sequence check does.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(environment): restate the verification after merging #7539

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): check PUT defaults against reads, and respell update clocks by serving's rule

A default a PUT learns must also agree with what the read after an earlier or
the current call showed for the member its answer left out, so a call whose
read showed the member kept withholds a later clearing default.

Replay and serving now choose an update clock's stored spelling by one rule,
readSpellsCoarser: the read after it at a coarser precision in the same zone
and separator. A read in another zone or separator is no longer adopted by
replay, which serving could not store, and a create's plain clocks stay as
answered, as serving makes them.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): keep sorts earlier listings ruled out, and empty added references

A sort a whole listing that placed a create did not follow stays ruled out for
that create, though the entity that showed it is later deleted. A create that
sends a reference's member empty after a later create added the reference
agrees with it, as with a reference the first create showed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(environment): restate the verification after the review round

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): leave out a reference the observed create left out

A create the plan observes that left out a reference's member stored no such
field, so a served call leaving the member out stores none either, though a
later recorded create sent it empty; a call sending it empty still stores that.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(environment): restate the verification after #7539 merged main

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): keep the largest sequence value in replay, and store update clocks by the kept read

Replay now keeps the largest value a sequence took, as serving's commitWrite
does, so a lower sent value no longer lowers the next number.

Serving stores every update's clocks in the spelling of the read the plan
keeps as observation.read; replay now does the same through
readSpelledClocks, instead of adopting each update's own read, so a later
update whose read spells its clock otherwise contradicts the plan. The plain
clock branch of restampedFields, which no caller used any more, is gone.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(environment): restate the verification after merging #7539 3b800a10

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): keep learned sorts, references, and toggles faithful

Do not adopt a sort an earlier update already ruled out. Learn a list
reference only when every element shows the same nonempty fields. Promote
a toggle only when every call started from a boolean.

* test(environment): compare list-reference fields with matching values

The mixed-field case uses the held tag's color, so rejection comes from
the field set rather than a value the recording never stored.

* test(mcp): assert bundle path issues by field

The compare_bundles rejection reports each relative path as an
invalid_format issue instead of one joined diagnostic sentence.

* test(mcp): assert field issues for bundle paths and Dart ABI

Relative bundle paths and rejected ABI selectors are reported as
input issues on those fields, not as one joined diagnostic sentence.

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: morluto <[email protected]>
Co-authored-by: Cursor Agent <[email protected]>
2026-10-29 18:46:18 +08:00
01e00dae72 fix(process): corroborate exiting Linux ownership candidates (#7958)
* fix(process): corroborate Linux exits after procfs environ read races

A token read failing with ENOENT/ESRCH may race with process exit while
native identity observation still sees a live PID. Recheck native lifetime
and accept absence only when corroborated; keep unverifiable or reused
PIDs fail closed.

Refs #7790

* fix(process): recheck Linux exit after final token retry

A process can exit after the final retry of a failed procfs environment
read. Re-observe native identity before producing final cleanup failures,
and keep PID replacement and uncertain identity fail-closed.

Add five integration-facing sweep regression cases for absence, live,
unavailable, replaced and zombie-replaced candidates.

Refs #7790

* fix(process): bound native exit settlement for token-open races

After a terminal ENOENT/ESRCH run-token read, allow a tightly bounded
native-only settlement window to corroborate pending Linux process exit.
Retain an explicit failure for live/unavailable or changed PID identities,
without inferring exit from the token error itself.

Cover delayed absence, delayed replacement and bounded live retries.

Refs #7790

* fix(process): share native token exit settlement

* fix(process): signal only groups with validated members

* fix(process): retain lineage through exit settlement

* fix(process): type mixed identity observations in exit checks

Map inference collapsed a readable/absent union onto the absent
overload after main tightened ProcessIdentityObservation.

* fix(process): refresh groups whose members exit during revalidation

A same-group child forked after the revalidation snapshot is not an
empty group, so cleanup reads the table again before skipping the signal.

* test(process): arm group-refresh exit from the revalidation snapshot

The regression follows the table read after planning observed the
launcher token, and requires a later refresh to see the forked child.

* test(mcp): assert bundle path issues by field

The compare_bundles rejection reports each relative path as an
invalid_format issue instead of one joined diagnostic sentence.

* test(mcp): assert field issues for bundle paths and Dart ABI

Relative bundle paths and rejected ABI selectors are reported as
input issues on those fields, not as one joined diagnostic sentence.

---------

Co-authored-by: Harshit Sharma <[email protected]>
Co-authored-by: Cursor Agent <[email protected]>
2026-10-29 18:40:12 +08:00
860fdd8fbe feat(macho): report per-slice encryption status in inspect_macho (#5359)
* refactor(macho): extract non-throwing container and slice load-command readers

Split readMachoImage into readMachoContainer, which classifies a file as
thin, FAT or not Mach-O and keeps FAT range issues per slice, and
readSliceLoadCommands, which reads one slice header and frames its load
commands, returning every command framed before a failure.

readMachoImage keeps its strict policy on top of them: commands framed
before a framing defect still decode first, an unsupported FAT slice
still yields to a later malformed one, and the first FAT range issue is
still reported. A characterization test pins that precedence for inputs
with several defects; it passes against both the previous and the new
implementation.

Part of #4306.

* feat(macho): report per-slice encryption status in inspect_macho

inspect_macho now reports the LC_ENCRYPTION_INFO(_64) state of every
slice in a new `encryption` collection, read in process from load
commands without reading code bytes.

- Each slice is encrypted, not-encrypted, absent, malformed or unknown.
  Absent requires a completed load-command walk; malformed and unknown
  carry a reason and keep commands read before the failure.
- The named range is end-exclusive in slice and input-file coordinates
  and maps to file-backed segment fragments, VM addresses and the
  overlapping nonempty sections.
- A ciphertext range adds a limitation naming its input-file bytes.
- The in-process read and the native tools are bracketed by one regular
  file descriptor and stat identity checks; a change reports
  artifact_changed. A missing or inaccessible target keeps the tool
  facts and reports encryption as non-exhaustive, never absent.
- Retained Evidence recorded before this field is parsed by the owning
  schema and reports encryption as unknown in the macho view and in
  managed/native verification.

Fixes #4306

* fix(macho): keep slice facts and failures under large tables and close errors

- Frame load commands through a visitor instead of retaining every
  command, restoring the pre-refactor memory profile (2M eight-byte
  commands: peak heap 370 MiB -> 0.9 MiB).
- Confirm a large FAT container from a valid slice's Mach-O header so a
  sibling's range fault stays per slice instead of hiding healthy slices.
- Name only sections whose bytes the range and their segment both cover.
- Throw inspection failures inside the owned descriptor callback so a
  close failure keeps them as its primary error.

* fix(macho): map encrypted ranges only within segment VM extents

mapRange gave every file-backed segment fragment VM addresses, even for a
segment with no VM size, whose file bytes dyld never maps, and past vmsize
when a segment declared more file bytes than memory.

Read vmsize with the segment. A segment with no VM size keeps its fragment
with null VM addresses. A file size beyond the VM size, or a VM range past
the address space, makes the mapping unknown with a reason while the slice
keeps its encryption status.

The docs also claimed the encryption read never reads code bytes. Like
every Mach-O container read, a 0xcafebabe input of at most 16 MiB is read
whole to rule out a JVM class file; say so.

* fix(macho): read encryption through the admitted descriptor facts

Main's withArtifactRegularFile now hands callers the admitted handle and
its initial stats. Use those facts instead of re-stating the handle, and
pass the legacy Evidence payload straight to createEvidence.

* fix(macho): retain a finished report when the target close fails

The file owner keeps the completed callback value when the descriptor close
fails or cancellation arrives during close. inspect_macho now validates that
value and retains the report as the error's partial observation, as
inspect_signature does, instead of returning only the cleanup or
cancellation error.

* test(native): supply readEncryption in merged dyld refusal cases

main's new dyld refusal cases build NativeMachoInspectionContext without
the encryption reader this branch requires.

* test(views): bind the Mach-O encryption page to its facet

main's facet-ownership case builds inspect_macho Evidence without the
encryption coverage this branch requires; the fixture now carries one
slice row and the loop checks the encryption page like the other facets.

* fix(macho): bound encryption commands and range mapping across slices

A slice retains at most two encryption load commands, since a second one
already makes it malformed; the reason keeps the full count. Range mapping
in every slice shares one budget of segment and section records, charged
from each segment command's capacity before it is decoded. A slice past
the budget keeps its encryption status and reports an unknown mapping.

* style(macho): format encryption reader and binding test

* fix(macho): stop retaining segments past the mapping budget

Segment commands are collected during framing only while their mapping
records still fit in the budget shared by all slices, and the budget is
charged only when the slice maps its range.

* docs(macho): clarify retained mapping allowance

* fix(ci): own the Mach-O encryption test in Apple scope

---------

Co-authored-by: t <t@t>
Co-authored-by: gensokyo-dev <[email protected]>
2026-10-29 18:19:39 +08:00
morlutoandCursor Agent d3ecf68000 docs(website): clarify platform setup requirements (#7925)
* fix(website): clarify platform setup requirements

* style(website): format platform guidance

* docs(website): clarify Ghidra Linux host support

* style(website): format the Linux platform guidance paragraph

* docs(website): refresh gettext locations after guidance formatting

The wrapped get-started paragraph shifted source lines. Message ids and translations are unchanged.

---------

Co-authored-by: Cursor Agent <[email protected]>
2026-10-29 16:55:17 +08:00
c88e1b0b43 feat(guidance): search installed workflow sections (#6996)
* feat(guidance): search bundled REA workflow guidance

* fix(guidance): address review findings

* test(guidance): fix boundary and dispatcher coverage

* test(setup): use supported doctor fixture runtime

* fix(guidance): resolve CI conformance, coverage, and review findings

* fix(guidance): own complete requests through shutdown

* test(guidance): bound lifecycle gates and join failed work

---------

Co-authored-by: Deepak-Kambala <[email protected]>
Co-authored-by: gensokyo-dev <[email protected]>
2026-10-29 16:47:10 +08:00
131fbe0754 feat(environment): serve writes whose calls send different members (#7539)
* feat(environment): serve the writes callers make without recorded form fields

- A create member a recording sent empty ("", null, false, [] or {}) is no
  longer required; a call that leaves it out is applied as sending that value
  (body.blank).
- List views render envelope members that list the listed entities' ids
  (Paperless's `all`), so creates in such collections are confirmed.
- Updates move their entity in collections kept sorted by a field they write,
  and a failed whole-collection read after an update can teach that order.
- A later create that stores an unruled field otherwise is withheld at the
  create, and an item read may fill values a create's answer had not loaded.
- An unsent field respelled between null, [] and {} is unchanged.
- The skill asks for one create with every field and one with only what a
  person must give.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(skill): show how to read signed-in pages before recording

Agents guessed capture keys (artifacts, dom, final) to see pages behind
sign-in, which inspect_web_page cannot reach.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): accept every empty spelling recorded creates sent

A create that fills only what a person must give sends `null` where the
full create sent a string, and `null` or `false` for the same flag. The
merged plan now accepts each recorded empty value, and a call that leaves
the member out is applied as the earliest call sent it.

Also adds the spec folder for this change.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(skill): keep the vary-fields step neutral on UI or API writes

Agents read "the form" as a cue to write through the UI, where some
applications' request bodies are not exposed; a body-unusable UI write
now re-records once with request steps.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(environment): record verification for blank members and sorted writes

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): address review of blank members and sorted writes

- Bulk updates move their entities in turn against the others' earlier
  values and install the final order once, as replay does.
- A member's recorded empty values stay accepted once a later call teaches
  its default; a left-out member with a default keeps the default.
- Write answers spell empty lists and maps as their recorded answers did.
- A rename can teach a sort when the creates chose one by a field the
  update did not write, such as ids that ascend with titles.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* feat(environment): serve the writes that later reads explain

- An item read whose object is marked deleted (deleted, isDeleted or
  is_deleted true) agrees with an absent entity, as EspoCRM answers a
  deleted record; such an object never seeds or adopts an entity.
- maxSize, max_results and maxResults are page sizes, for whole-collection
  list lines and the list query's limit role.
- An item view also shows the fields only reads after its type's first write
  show, when no initial entity holds them (Vikunja's task subscription),
  without requiring them; write answers leave such a field out for entities
  without it, and it no longer withholds the write as incomplete-entities.
- The skill asks updates to change the fields a person would change, and
  expects a deleted object to read back as 404 or marked deleted.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): answer sent empty members as sent, and keep a placed create's order

- A write answer spells an empty list or map the call did not send as its
  recorded answer did; one the call sent is answered as sent, so a create
  sending `[]` is not answered `null` because the first recorded create was.
- A read listing the whole collection that placed a create leaves the create
  only the sorted inserts whose order that read follows, so a later rename
  cannot teach a sort an earlier read ruled out.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): answer bulk updates without fields only later reads show

A bulk update's answer renders each entity with the same allowance a single
update's answer has: a field only reads after the type's first write showed,
which no initial entity holds, is left out for an entity without it, so a
bulk update of a seeded task is no longer refused for lacking it.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(environment): correct how the EspoCRM contact update task passes

The agent trajectories show gpt-oss passes it by creating a replacement contact
and deleting the original, since the served update refuses emailAddress; the
update itself is refused in both builds.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): answer a read of a served-deleted entity marked deleted

An application that soft-deletes still answers a deleted record by id, marked
deleted. Serving now keeps an entity a served delete removed from a type whose
recorded item reads show that, until a reset or load and within the size
data/add loads, and answers a read of it with its last fields and the flag
true, instead of refusing the read.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): keep unloaded create answers, check bulk answers with sent members, narrow inserts only on collection-order reads

- A create whose item read loaded values its answer had not records them
  (`observation.read.unloaded`), and a served create's answer keeps them as
  the recorded answer showed them, a zero-time owner included, while the state
  holds the read's.
- Replay checks a bulk update's recorded answers with each target's sent
  members, as serving answers them, so echoed `null` and `[]` no longer make
  the plan body-unusable.
- A whole-collection read whose query sorts it shows the query's order, so it
  no longer narrows a create's sorted insert candidates.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(environment): record the census with main's unknown-clock rule merged

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* feat(environment): serve query numbers, counted values and made objects

- A query parameter accepts a number or boolean, which serving sends as its
  JSON text, as agents send page sizes and flags.
- A sequence may start at the value its first create took above every value
  its type held (Vikunja's per-project task index), and a later create's
  composed, template or started-sequence rule is adopted when it also spells
  the first create's value.
- A made-object rule explains an object the application makes with each
  created entity: members the request stamped, members holding the entity's
  own id, and counts one past the held objects' values.
- A create's answer names the id of the one object a one-element array member
  holds, so bulk creates link to their type.
- The skill reads each listed object by id before writing.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* feat(environment): let a linked create take path values it stores

A create linked to its type's own collection, whose path has no parameters,
accepts any path values when its plan stores each one in a field, as a Vikunja
task stores its project; replay and serving apply it alike.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): learn a sort from a rename only when moving its entities reaches it

Serving moves only the entities a write changes in a collection kept sorted,
so replay now tries a sort after a rename only when the entities no write the
read exercised changed already follow it. Seeded folders listed out of title
order no longer teach a title sort that serving would answer with another
order.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): keep unloaded create answers a sent value did not echo, and learn no sort from query-sorted reads

- A served create keeps a value its recorded answer had not loaded unless the
  call sent that member and the plan stores it as sent, so a create sending
  `active: false` is answered `false` as recorded while the state holds the
  read's `true`.
- A rename no longer teaches a sort from a whole-collection read its query
  sorts, whose ties need not follow the collection's own order.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(environment): verify query values, counted values and made objects

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): learn the soft-delete flag in the state view and serve by it

Extraction now records the member later item reads set true on a record the
application soft-deleted, and the item view carries it as `deleted_flag`.
Serving reads that fact instead of scanning recorded bodies itself, so one
owner decides what a soft delete looks like.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(environment): record the call-values checks at #7274 93821024

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): learn the soft-delete flag from item reads with a query too

A client may read a deleted record back only with a query, such as a
cache-busting parameter. Every successful read of the item tool now shows the
flag; only plain later reads still add fields.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(environment): record the call-values checks at #7274 45832406

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): learn sorts only from orders a list read confirmed

- A sort retry after a failed whole-collection read requires the entities no
  write changed since a list last showed them to follow it already, rather than
  excusing every entity the update's tool ever wrote: an earlier rename a list
  confirmed in place stays where it was.
- An update takes its creates' sort only from a create a list read showed
  placed; replay still tries a sort a create guessed from the initial order.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): drop entities read back as deleted and answer soft deletes as deleted

- A held entity a later read shows marked deleted is dropped, not adopted with
  the marked object's fields.
- Bulk-update replay renders answers with the optional and later-only fields
  serving uses, so a bulk update of created and seeded entities replays.
- A served delete of a soft-deleting type answers with the entity marked
  deleted, single and bulk.
- A deleted flag only query reads showed is a field of the item view, so a
  served tombstone keeps it where live records omit it.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): check made objects as recorded and keep their ids and clocks honest

- A made object's clock counts as a write clock, so a capture with no clock
  anchor withholds the create instead of stamping 1970.
- Members holding the entity's identity follow the id written, generated or
  sent, through the type's id field.
- A later create's made object is checked as recorded, before its own members
  are rewritten, so one naming another entity withholds the plan.
- A later create's template is adopted only when it spells that create's value
  too, not just when the first create's text holds its source value.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): move updated entities only by placed sorts and keep query-sorted reads from confirming order

Replay moved an updated entity by a sort a create only guessed, while the
update plan exported only placed sorts, so a read could confirm an update
by a move serving would not make. Both now use the placed sort. A passing
list read clears its entities from the moved set only when the state
decides the order of every entity it lists, so a read whose query sorts
it no longer hides an earlier move from a later sort check.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(environment): count six withheld plans that explain more, not seven

The bulk task create counted among them is served on this branch.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): try a learned sort by moving each pending write in turn, as serving does

#retrySort sorted the whole collection, which a list could match while
serving, moving each renamed entity in turn against the others' values
before their own writes, gave another order. The state now records the
values each write replaced, and the retry replays the writes since a
list in collection order last showed their entities.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): answer a served delete with the deleted flag its recorded answer showed

An application may answer a delete with the record as it was and mark it
deleted only on later reads. Delete answers, single and bulk, now take
the flag's value from the recorded answer; reads of a served-deleted
entity still show it marked deleted.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* chore(environment): set aside two hunks next to lines main's saved-values change rewrites

Main's #7267 changes the lines beside the blank-member docs and the
empty-spelling check in explainUpdate. Setting them aside lets that merge
apply cleanly; the commit after it puts them back on top of main's text.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* chore(environment): restore the set-aside hunks on main and bump the skill to 51

Puts the blank-member docs and the empty-spelling check back beside main's
slug and composed text and heldSlugRule. Main is at skill 50 with
different text, so this branch's skill changes take 51.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* chore(environment): bump the skill to 52 above #7260's 51

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* chore(environment): put back the call-values hunks main's merge took its side for, and bump the skill to 53

The merge of main kept main's side where it overlapped this branch, so it
could commit without a conflict. This restores this branch's side next to
main's: made-object agreement beside the & slug spellings, sequence starts
in the earlier evidence (now built by earlierOf), the combined value
imports, and recording step 2 beside main's UI-first step 3. Main is at
skill 50 and #7274 at 52, so this branch takes 53.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): keep sorts a list showed in place ruled out, and created answers' nested members as recorded

A whole list in collection order that showed a renamed entity where a
sort by the renamed field would have moved it now rules that sort out, so
a later rename of the same entity cannot teach it. A create's answer
leaves out members its item read added inside an object the answer
showed, as the recorded answer did; the state still stores them.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* feat(environment): serve writes whose calls send different members

Recordings now vary what a type's two creates and updates send, and REA
withheld writes where a later call left out what the first sent. An update
plan's required members narrow to those every call sent; a member one create
sent and another left for the application to number is a sequence that
takes the value a call sends; a PUT's left-out member the application
cleared becomes that member's default; and an update's clock is held as
the read after it spells it. The skill spaces creates (skill 54).

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): keep a later create's made object only with clocks its request stamped

followsMadeObjects only checked that a made object's clocks parsed, so a
later create whose object showed an older clock still passed, while serving
stamps the write's clock. Its clocks must now be in the rule's format and
within the stamp window of that create's request.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): accept sent values either way, check defaults against every call, and store update clocks as reads spell them

A sequence that takes sent values now accepts one a later create sent
after the first left the member to be numbered. A default an update learned
must agree with every update that left its member out, else the plan is
withheld. An update plan whose read-back spells a stamped clock at a coarser
precision keeps that read as observation.read, and serving stores the
update's clocks in its spelling while answers keep the response's.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): check recorded member and sequence values

* fix(environment): compare soft-deleted read fields

* fix(environment): reject contradictory learned sequences

* fix(environment): preserve initially deleted item observations

* fix(environment): index stored deleted entities separately

* fix(environment): replay lists that include deleted records

* fix(environment): count a sequence number a create sends, as replay does

Serving now records a number a call sends for a `sequence` with `sent` in the
same count history it generates from, so after that entity is deleted the next
generated number still follows it, as replay's takeSequences does.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): make no count of a made-object member earlier creates held alike

A count only a later create shows starts at the exemplar's value, which is
right only while the exemplar is the one create accepted: creates accepted
after it showed the member unchanged, which a count would have moved. The
merge now refuses such a count, as the top-level sequence check does.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): check PUT defaults against reads, and respell update clocks by serving's rule

A default a PUT learns must also agree with what the read after an earlier or
the current call showed for the member its answer left out, so a call whose
read showed the member kept withholds a later clearing default.

Replay and serving now choose an update clock's stored spelling by one rule,
readSpellsCoarser: the read after it at a coarser precision in the same zone
and separator. A read in another zone or separator is no longer adopted by
replay, which serving could not store, and a create's plain clocks stay as
answered, as serving makes them.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): keep the largest sequence value in replay, and store update clocks by the kept read

Replay now keeps the largest value a sequence took, as serving's commitWrite
does, so a lower sent value no longer lowers the next number.

Serving stores every update's clocks in the spelling of the read the plan
keeps as observation.read; replay now does the same through
readSpelledClocks, instead of adopting each update's own read, so a later
update whose read spells its clock otherwise contradicts the plan. The plain
clock branch of restampedFields, which no caller used any more, is gone.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): keep a left-out default from numbering, a read's sub-millisecond digits, and references named by member

A member a call left out and the application stored a fixed value for is no
longer turned into a numbered sequence by a later call that left it out.
Serving shortens an update's clock to the read's precision from the answer's
own digits, keeping microseconds. A PUT that names a reference field's entity
by its member supplies that field, for the default check and in serving.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): count a reference a later PUT names toward the default an earlier one cleared

The default check now reads the reference rules the current call shows as
well as the plan's, so a PUT naming the owner by ownerId after one that
cleared it is served, as in the other order.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): number a left-out member past every sent value, and never fill it with a sent blank

A later create the application numbered at or below a value an earlier create
sent no longer turns the echo into a numbered sequence, since serving counts
past every sent value. A member a numbered sequence covers is not filled with
an empty value another call sent when a call leaves it out.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): count sent values in the sequence's scope before numbering a left-out member

The largest value earlier creates sent is kept by type and by collection, and
a collection-scoped sequence adopted later checks only its own collection, so
a number sent in another collection no longer withholds a create numbered
within its own.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: morluto <[email protected]>
2026-10-29 16:14:05 +08:00
morlutoandCursor Agent 3503578f91 fix(session): preserve cancellation during mutation retirement (#7959)
* fix(session): preserve cancellation during mutation retirement

* fix(session): retain exact cleanup owners through retry

---------

Co-authored-by: Cursor Agent <[email protected]>
2026-10-29 16:05:43 +08:00
morlutoandCursor Agent bd3792566d docs(skill): clarify scenario pause deadlines (#7961)
Co-authored-by: Cursor Agent <[email protected]>
2026-10-29 16:05:03 +08:00
f7cd4e5256 fix(adb): validate screen capture PNG headers (#7962)
* fix(adb): validate screen capture PNG headers

* fix(adb): state screen header validation limits

* style(adb): format screen capture assertion

Match the current formatter after integrating main.

Co-authored-by: morluto <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
2026-10-29 16:04:49 +08:00
morlutoandCursor Agent 2b85a4a2ba fix(native): preserve bookmark address uncertainty (#7966)
fix(analysis): preserve bookmark address uncertainty

Co-authored-by: Cursor Agent <[email protected]>
2026-10-29 15:54:33 +08:00
gensokyo-dev e3d8d20236 fix(native): preserve dispatch metadata format refusals (#8013)
* fix(native): preserve dispatch metadata format refusals

* test(native): normalize CLI format refusal envelopes

* test(native): assert admitted metadata target paths
2026-10-29 15:48:42 +08:00
17ceb77ca5 fix(mcp): preserve input validation issue attribution (#7981)
* fix(mcp): preserve input validation issue attribution

* fix(mcp): charge nested issue projection before allocation

* style(mcp): format server declarations

* fix(mcp): avoid duplicate fallback diagnostics

* test(mcp): assert structured validation issues

* test(mcp): align browser input issue assertions

* fix(mcp): retain SDK diagnostic parts once

* style(mcp): format diagnostic projection

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
2026-10-29 15:39:10 +08:00
STDquantum f25d1e3e9f fix(cli): preserve Amp MCP registration settings (#7995)
* fix(cli): preserve Amp registration environment and report failures (#7538, #7536)

* fix(cli): normalize equals-form mcp add agents before Incur parses them (#7538)

* fix(cli): remove the Amp settings backup after a verified registration (#7538)

* fix(cli): secure Amp registration backups

* test(cli): cover Amp registration backup permissions

* fix(cli): retain Amp registration recovery backups

* fix(cli): create private Amp recovery copies

* test(cli): fix Amp backup assertion typing

* fix(cli): preserve typed Amp backup failures

* fix(cli): handle Amp backup cleanup failures
2026-10-29 15:23:42 +08:00
Dragolone 124e97eac5 fix(native): explain categories whose class reference names no class (#8006)
An Objective-C category whose class field is null, or is bound to a
symbol other than `_OBJC_CLASS_$_`, was recorded as partial with
`category_class_unresolved`, but no issue was raised, so the
`objc_properties_categories` coverage facet reported `partial` with a
null reason. swiftc emits such categories for @objc members of an
extension on another module's Swift class and binds the class field to
the class metadata symbol (`_$s…CN`); Keynote and Notability carry them.

Report the reference as a `category_class_unresolved` issue naming the
bound symbol, or stating that the reference is null, matching the issue
already raised for an unreadable local class reference.
2026-10-29 15:14:10 +08:00
Dragolone 95637a3d70 fix(artifacts): name every U-Boot uImage type in the pinned image.h (#8007)
TYPE_NAMES stopped at flat_dt (8), while the U-Boot include/image.h
commit that the tables are pinned to defines image types 0-47. A header
declaring a later type, such as kernel_noload (14) or tfa-bl31 (45),
was reported with type_name "unknown".

Add types 9-47 with their mkimage -T short names from boot/image.c at
the same commit. IH_TYPE_LOADABLE (22) has no short name there and is
named "loadable", as other tables already name identifiers U-Boot does
not list. Identifiers past the enum stay "unknown".
2026-10-29 15:13:37 +08:00
ShuaixinHuang ca666ec130 fix(pe): reject invalid certificate bit-string padding (#8011) 2026-10-29 15:11:28 +08:00
gensokyo-dev 45cc935964 fix(native): accept walkable Mach-O command tables (#8002)
* fix(native): accept walkable Mach-O command tables

* test(native): verify large command tables through macOS CLI and MCP
2026-10-29 14:39:20 +08:00
morluto d82ba11ce2 test(ci): allow more time for Windows CLI subprocesses 2026-10-29 14:35:12 +08:00
LN Bodicherlaandgensokyo-dev c9bf6a9df7 feat(managed)!: show readable type names in member signatures (#5309)
feat(managed): show readable type names in member signatures

Co-authored-by: gensokyo-dev <[email protected]>
2026-10-29 14:34:42 +08:00
morluto afc18131f4 fix(ci): reuse shared JavaScript AST child traversal 2026-10-29 14:33:14 +08:00
morluto 54f5cf7e14 fix(javascript): retain static facts under projection pressure (#7986)
* fix(javascript): retain static facts under projection pressure

* fix(javascript): scope IPC ambiguity to omitted projections

* fix(javascript): preserve IPC uncertainty for omitted sources

* fix(javascript): qualify IPC pairing by wrapper coverage

* fix(javascript): retain provisional IPC wrapper matches
2026-10-29 14:02:35 +08:00
gensokyo-dev f60e42a9ae fix(mcp)!: bound validation work before tool dispatch (#7905)
Reuse the pinned SDK's combined array-element and object-member admission
before canonical validation, deferred runtime loading and tool execution.
Share a configurable finite allowance across eager/deferred HTTP and stdio,
and project refusals to bounded, actionable transport resource errors.

Preserve canonical CLI/application contracts, caller-selected input values,
ordinary validation semantics and complete Evidence access. Exercise actual
transport boundaries, real artifact/TShark workflows, packaged installation,
continued service and downstream identity/capacity regressions.

Fixes #7882

BREAKING CHANGE: MCP tool arguments now default to 1,024 combined array
elements and object members per call. Select a larger positive finite
REA_MCP_MAX_INPUT_ELEMENTS allowance and restart for larger inline inputs,
or use retained/file selectors where supported. Inputs are never truncated.

Assisted-by: Reimu (gensokyo-skills:reimu-incident-triage)
Assisted-by: Kogasa (gensokyo-skills:kogasa-surprise-testing)
Assisted-by: Yukari (gensokyo-skills:yukari-boundary-analysis)
2026-10-29 05:56:38 +00:00
morluto ddefba83ef test(artifacts): handle blocked open-parent renames (#7904)
* test(artifacts): handle blocked open-parent renames

* test(artifacts): exercise blocked parent rename cleanup
2026-10-29 13:45:12 +08:00
gensokyo-dev b966ebeb22 docs(readme): align Linux x64 analysis prerequisites (#7997) 2026-10-29 13:44:07 +08:00
RRFZ fca331804a fix(javascript): select JSON and addon loaders by exact suffix (#7821)
Node's CommonJS loader matches registered extensions case-sensitively, so
require("./data.JSON") and require("./addon.NODE") run as JavaScript.
Derive target_json_status and the native loads relation from the exact
loader suffix instead of the inventory's case-folded file kind.

Fixes #7623
2026-10-29 13:43:55 +08:00
RRFZ 4fbce5c122 fix(cli): reject empty IPS and PE import targets before resolution (#7963)
inspect-apple-ips and inspect-pe-imports were the two commands in the
binary diagnostics group whose positional target was still a plain
string. An empty argument passed CLI validation, was resolved to the
working directory, and failed later as a non-file input, unlike their
siblings (inspect-elf-notes, inspect-pe-certificates and others) that
already reject it at validation.

Declare both targets with the shared nonempty, NUL-free local path
schema the rest of the file uses. Explicit "." is unchanged.

Fixes #7421
2026-10-29 13:43:25 +08:00
ShuaixinHuang 35ea02d27e fix(xml): reject ambiguous namespace attribute identities (#7991) 2026-10-29 13:38:37 +08:00
吴佳浩Alben 2bfd84b2c8 fix(javascript): emit no fork when a literal stdio array omits the IPC channel (#7992)
* fix(javascript): emit no fork when a literal stdio array omits the IPC channel

Node's fork() requires an explicit stdio array to carry the IPC channel:

    if (!ArrayPrototypeIncludes(options.stdio, 'ipc'))
      throw new ERR_CHILD_PROCESS_IPC_REQUIRED('options.stdio')

The throw is synchronous, before any child process exists, but the analysis
reported a resolved fork with stdioMode "array" — byte-identical to the valid
`[..., "ipc"]` form — which projects to a child-process node, a resolved spawns
relation and resolved connects-stdio.

Measured on Node 24.22.1:

    fork({stdio: ["inherit","inherit","inherit"]}) -> THROWS ERR_CHILD_PROCESS_IPC_REQUIRED
    fork({stdio: ["pipe","pipe","pipe"]})          -> THROWS ERR_CHILD_PROCESS_IPC_REQUIRED
    fork({stdio: ["ipc"]})                         -> SPAWNED connected=true
    fork({stdio: ["inherit","inherit","inherit","ipc"]}) -> SPAWNED connected=true
    fork({stdio: "pipe"})                          -> SPAWNED connected=true
    spawn({stdio: ["inherit","inherit","inherit"]})-> SPAWNED connected=false

A string stdio is normalised with "ipc" appended, so only the array form can omit
it. The same array is valid for spawn, which has no IPC requirement.

Drop a fork candidate only when its stdio array is fully literal and provably
lacks "ipc"; a spread or a non-literal element may still supply the channel and
stays a candidate.

* chore: retrigger title and readiness checks after the title was corrected
2026-10-29 13:38:29 +08:00
d39b3000ec feat(artifacts): inventory and extract U-Boot FIT images (#7771)
* feat(artifacts): inventory and extract U-Boot FIT images

open_binary refused U-Boot Flattened Image Tree files as an unsupported
format, so inspect_artifact and extract_artifact could not list or
recover a FIT's kernel, devicetree, ramdisk and loadable images.

Recognize an FDT v16/v17 with a top-level /images node and parse it with
the existing parseDevicetree. Each /images child becomes an inventory
entry named images/<unit-name>.bin that extracts its stored bytes from
embedded data, data-offset (past the 4-aligned devicetree end) or
data-position (from the file start). Image format_metadata records the
declared type, arch, os, compression, load and entry; root metadata
records the description, timestamp and configurations.

crc32, md5, sha1, sha256, sha384 and sha512 hash nodes are verified in
one streamed pass per image; other algorithms are reported unverified.
Signatures are reported, not verified, and nothing is decompressed,
decrypted or executed. Data outside the file, overlapping or duplicate
image data, a missing or repeated data source and hash mismatches fail
closed. The devicetree is bounded by the fixed 16 MiB allowance;
external image data is streamed from disk.

Refs #7129

* fix(artifacts): bound FIT configuration references and suggest analysis only for plain images

* fix(artifacts): retain buffered FIT stream cancellation

* test(artifacts): gate FIT faults on the observed read phase

* fix(artifacts): verify FIT links and summary identities

* fix(artifacts): bound FIT diagnostics and retain unknown signatures

* fix(artifacts): compare FIT declarations and resolved data links

* fix(artifacts): admit FIT raw probes through the shared loader contract

* fix(artifacts): preserve FIT field availability independently

* fix(artifacts): isolate nested entry metadata from reader facts

---------

Co-authored-by: gensokyo-dev <[email protected]>
Co-authored-by: gensokyo-dev <342580529+users.noreply.github.com>
2026-10-29 13:33:26 +08:00
gensokyo-dev d157560d40 fix(cli): reject empty Evidence file arguments (#7988) 2026-10-29 12:34:46 +08:00
morluto 429355ca4d fix(ci): repair native fixture and unused export 2026-10-29 11:29:10 +08:00
gensokyo-dev 50b2ab137f fix(browser): preserve DOM length units in captures and comparisons (#7971)
* fix(browser): declare DOM node value length units

* fix(browser): preserve unknown DOM length comparisons
2026-10-29 11:27:01 +08:00
gensokyo-dev cf9f010dcd feat(managed): filter and project retained member views (#7968)
* feat(managed): filter and project retained member views

* test(managed): guard relationship endpoint filter semantics
2026-10-29 10:39:34 +08:00
morluto c19f13ca03 fix(setup): preserve YAML annotations during refresh (#7944)
* fix(setup): preserve YAML annotations during refresh

* fix(setup): preserve yaml argument comment ownership
2026-10-29 10:12:09 +08:00
morluto 62ce357f16 fix(artifacts): preserve known metadata without content hashes (#7932)
* fix(artifacts): preserve known metadata without content hashes

* fix(artifacts): keep missing relation projections unknown
2026-10-29 10:10:41 +08:00
morluto 953a7689df fix(uninstall): preserve layered OpenCode registrations (#7969)
* fix(uninstall): preflight layered OpenCode fallback

* fix(uninstall): preserve inherited foreign registration facts

* fix(uninstall): preflight legacy OpenCode layer removal
2026-10-29 10:07:27 +08:00
morluto 3dcb40dc26 fix(uninstall): protect ZCode fallback registrations (#7970)
fix(uninstall): preserve inactive ZCode fallback servers
2026-10-29 10:05:35 +08:00
morluto 7b9bcf724f style: fix native documentation and test formatting 2026-10-29 10:02:22 +08:00
gensokyo-dev 7ca216a266 fix(docs): restore translated offline provider ownership notes (#7964) 2026-10-29 09:38:51 +08:00
morluto 419a211304 fix(cli): preserve caller paths in format inspections (#7855)
* fix(cli): preserve caller paths in format inspections

* fix(cli): retain caller path on partial evidence

* test(cli): preserve caller path in adapter parity checks

* test(rust): account for retained caller path

* fix(wasm): compare shared cli and mcp evidence
2026-10-29 09:26:22 +08:00
morluto 63f05d1cad fix(javascript): preserve uncertainty for unexamined sources (#7911)
* fix(javascript): preserve uncertainty for unexamined sources

* fix(javascript): compare observed package parsing status
2026-10-29 09:25:25 +08:00
morluto 95f0625781 fix(javascript): preserve uncertain with-scope member writes (#7914)
* fix(javascript): preserve uncertain with-scope member writes

* fix: retain nested initializer candidates

* fix(javascript): admit initializer values in worker transport

* fix(tests): remove duplicate MCP test imports
2026-10-29 09:23:34 +08:00
morluto 611c30cb20 fix(browser): deduplicate scenario coverage guidance (#7933)
* fix(browser): deduplicate scenario coverage guidance

* style(tests): fix CI formatting
2026-10-29 09:23:22 +08:00
morluto 78a1e1956e fix(pcap): validate selected resource limiter before capture (#7947)
* fix(pcap): validate selected resource limiter before capture

* fix(pcap): verify limiter capabilities and preserve probe output
2026-10-29 09:23:07 +08:00
morluto 59a895a193 fix(electron): retain unknown external module ownership (#7950) 2026-10-29 09:20:53 +08:00
morluto 3536ea2f49 fix(discovery): report debugger start host restrictions (#7965) 2026-10-29 09:20:33 +08:00
morluto 70a8325c38 fix(setup): preserve existing configuration mode (#7942) 2026-10-29 08:59:56 +08:00
morluto 9e1a1e700a fix(cli): prevent mcp discovery from registering clients (#7919)
* fix(cli): prevent mcp discovery from registering clients

* fix(cli): use shared output parsing for discovery errors
2026-10-29 08:57:38 +08:00
morluto 33d6975749 fix(certificates)!: disclose undecoded unsigned attributes (#7920)
* fix(certificates): disclose undecoded unsigned attributes

* test(certificates): use parser-owned limitation text
2026-10-29 08:56:07 +08:00
morluto 0386efd1e1 fix(pcap): retain producer contradiction diagnostics (#7937)
* fix(pcap): retain producer contradiction diagnostics

* test(pcap): align DNS fixture with empty captures
2026-10-29 08:43:14 +08:00
morluto c92322b473 fix(process): await Swift frontend lifetime pipe (#7948) 2026-10-29 08:43:04 +08:00
morluto 0448f318e6 fix(outputs): use neutral destination diagnostics (#7949) 2026-10-29 08:42:56 +08:00
morluto 640e19d79d fix(native)!: own Mach-O load commands in binary reader (#7952)
* fix(native): retain selected Mach-O slice identity

* fix(native)!: own Mach-O load commands in binary reader

* fix(native): preserve malformed Mach-O and image base limits

* fix(native): preserve leading BOM in Mach-O names

* test(native): update Mach-O provenance verification

* fix(native): retain completed Mach-O inspection on cleanup failure

* fix: bound Mach-O projected section output

* fix(native): own retained Mach-O command bytes

* fix(native): retain Mach-O outcomes on final cancellation

* fix(cli): preserve caller path in partial evidence

* fix: reserve Mach-O projection envelope budget
2026-10-29 08:37:36 +08:00
RRFZ 6db024dc96 fix(cli): reject empty native and managed targets before resolution (#7956)
Local file commands in the core binary, native, managed and native-tool
CLI groups declared their positional target as a plain string. An empty
argument therefore reached path admission, where it resolved to the
working directory, as docs/cli.md says it never should.

Declare those targets with the shared nonempty, NUL-free local path
schema already used by analyze, so the CLI rejects them as a
VALIDATION_ERROR naming the field before any analysis runner is called.
Explicit "." is unchanged.

Fixes #7436
Fixes #7404
2026-10-29 08:31:12 +08:00
吴佳浩Alben 4671947c97 test(firmware): pin the staging ceiling at the request boundary (#7775)
* test(firmware): exercise the staging ceiling through the emitted refusal

The ceiling recovery added in #6971 was covered through the wording helper only, so a wiring change that passed the schema ceiling where the selected value belongs, or that dropped the remediation, would have stayed green.

Adds boundary coverage that calls inventoryFirmwareOutput itself: a sparse file reaches the real byte ceiling without storing the bytes and asserts the projected refusal carries the ceiling recovery, and a one-byte tree below the ceiling asserts the raise advice. Verified red on both regressions — passing the schema ceiling for the selected value fails the below-ceiling case, and restoring the single-form remediation fails both.

* test(firmware): compare the emitted recovery with its owner

The wiring test matched three phrases of the emitted remediation, so an ad-hoc or truncated action that kept those phrases would still pass while the emitted recovery had diverged from firmwareStagingBudgetRemediation.

Assert exact equality with the owner's output for the selected value in both cases instead. Verified red: replacing the emitted action with a divergent sentence that keeps the ceiling phrases now fails both cases, where the phrase matching passed.

* chore: retrigger title and readiness checks after the title was corrected

* test(firmware): drive the staging ceiling through the request boundary

greptile and codex both flagged that the ceiling tests called
inventoryFirmwareOutput directly, so they would still pass if request parsing or
the provider wiring dropped or replaced max_output_bytes.

Add a request-level test that records the parsed request a provider receives:
the caller's max_output_bytes arrives unchanged at the schema ceiling and below
it, the documented default applies when omitted, and a budget above the schema
maximum is rejected before the provider runs. That is the wiring the previous
tests assumed rather than asserted.
2026-10-29 08:24:10 +08:00
吴佳浩Alben b7442c1958 fix(javascript): match resource release methods to the acquired handle (#7773)
* fix(javascript): match resource release methods to the acquired handle

Every acquisition family admitted every release method, so `h.close()`,
`h.destroy()` and `h.end()` each resolved as a complete release of whatever the
binding held. Node exposes a different subset per acquisition, and the rejected
pairs throw at the call site instead.

Measured on Node 24.22.1:

| acquisition | value | release members |
| --- | --- | --- |
| `fs.openSync` | number | none |
| `fs.open` (callback) | undefined | none |
| `fs.createReadStream` | ReadStream | `close`, `destroy` (`end` is the byte-offset field) |
| `fs.createWriteStream` | WriteStream | `close`, `destroy`, `end` |
| `fs/promises.open` | FileHandle | `close` |
| `net.connect` | Socket | `destroy`, `end` |
| `tls.connect` | TLSSocket | `destroy`, `end` |

So `fs.openSync(...)` + `fd.close()`, `createReadStream(...)` + `.end()`,
`net.connect(...)` + `.close()` and the FileHandle cases emitted a `releases` /
`resolved` fact for a call that throws.

Carry the specifier with each acquisition candidate, list the release methods
each pair actually has, and emit no release when the pair is not one of them. A
release stays `complete` only when the binding held exactly one acquisition and
that acquisition admitted the method.

`tls.createConnection` is not a Node export at all: `'createConnection' in
require('node:tls')` is false and the ESM import does not link. Drop it from the
table so a `tls.createConnection` call yields no acquisition.

* chore: retrigger title and readiness checks after the title was corrected

* fix(javascript): cover a mixed-family binding before completing a release

greptile asked for the mixed-family case: a binding that can hold either a
FileHandle or a socket, then calls close(). The FileHandle is the only linked
acquisition and the release stays partial.

That case also showed the `candidates.length === 1` guard was dead: an
acquisition of another family is not in `linked`, so `holdsOnlyAcquisitions`
already returns false for it and the release already stayed partial without the
guard. Removing the guard and keeping the test means the behaviour is now pinned
by a test rather than by two overlapping conditions.
2026-10-29 08:23:53 +08:00
Ha1baraA1 a965cbab06 fix(ghidra): omit duplicate load-image result (#7777)
* fix(ghidra): omit duplicate load-image result

* docs(mcp): describe load-image evidence projection
2026-10-29 08:23:38 +08:00
64d282b0a6 fix(process): normalize descendants of slash-spelled directory roots (#7803)
* fix(process): normalize descendants of slash-spelled directory roots

A working directory or observation path ending in / or /. kept its
trailing separator as the alias, so descendant paths never met the
delimiter lookahead and stayed literal. Strip those spellings before
building aliases and version the path normalization contract as v3.

Fixes #7584

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(process): normalize filesystem root descendants

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: morluto <[email protected]>
2026-10-29 08:23:19 +08:00
RRFZ 30936d7596 fix(managed): report member signature issues at artifact offsets (#7879)
Field, MethodDef and MemberRef signature issues reported the invalid operand's offset inside its blob, while local signature issues in the same coverage array used artifact file offsets. Translate both through one helper that adds the blob's artifact base.

Fixes #7385
2026-10-29 08:23:04 +08:00
ShuaixinHuangandmorluto 3a46047aa7 fix(pe): reject surrogate code units in BMPString names (#7817)
* fix(pe): reject surrogate code units in BMPString names

* chore: preserve base browser imports

---------

Co-authored-by: morluto <[email protected]>
2026-10-29 08:20:32 +08:00
morluto 7c5700542e test(reference): pin excluded package main resolution (#7941) 2026-10-29 08:18:13 +08:00
morluto ef9ab91e42 fix(il2cpp): validate retained declaring type relationships (#7946) 2026-10-29 08:16:57 +08:00
morluto 5147b6e306 fix(environment)!: attribute source errors to caller fields (#7900)
* fix(environment): attribute source file errors to caller fields

* style(environment): format source admission tests
2026-10-29 08:12:09 +08:00
morluto c7722c8eeb fix(cli): honor token controls for builtin documents (#7897) 2026-10-29 08:11:19 +08:00
morluto b0b25ad93e fix(ida): report registration timeout bounds (#7893) 2026-10-29 08:11:07 +08:00
morluto 3a512303d9 fix(cli)!: refuse lossy text output (#7852)
* fix(cli)!: refuse lossy text output

* fix(cli): retain failure identity on text refusal

* fix(cli): check failed TOON token windows

* test(cli): bound keyed archive case duration
2026-10-29 08:09:49 +08:00
morluto 794907fa3d fix(javascript): trace boundary results to assigned bindings (#7912) 2026-10-29 07:58:57 +08:00
morluto 2129c17925 fix(native): retain selected Mach-O slice identity (#7940) 2026-10-29 07:57:19 +08:00
morluto 0179d94420 fix(browser): disclose opaque redirect coverage gaps (#7943) 2026-10-29 07:57:00 +08:00
Hasan Zayni bbd4989654 fix(config): treat a blank analyzer override as absent beside an existing Ghidra project (#7923)
REA_GHIDRA_DISABLED_ANALYZERS parses a blank or comma-only value to no override, but the existing-project conflict check tested the raw value. MCP startup exited and doctor reported config_drift for an input that selects nothing. The check now uses the parsed value.

Fixes #7422
2026-10-29 07:56:50 +08:00
Hasan Zayni 32b3722d2f fix(ghidra): report unusable existing-project selections as REA_GHIDRA_PROJECT_PATH configuration errors (#7922)
A selected .gpr marker that does not exist and a target that existing-project mode cannot serve both surfaced as execution_failure with no setting and a doctor action that cannot help. Both now return configuration_invalid naming REA_GHIDRA_PROJECT_PATH. The ENOENT diagnostic of the missing marker is kept.

Fixes #7417
Fixes #7416
2026-10-29 07:56:38 +08:00
morluto 844bb46351 fix(javascript): retain Electron switch conditions (#7910)
* fix(javascript): retain Electron switch conditions

* fix(javascript): bound switch path traversal
2026-10-29 07:56:15 +08:00
morluto 086abf3996 fix(javascript): distinguish bundled script locations (#7909) 2026-10-29 07:56:06 +08:00
morluto 9b4546276c fix(browser): preserve frame tree root identity (#7915) 2026-10-29 07:55:57 +08:00
morluto f256492ff3 fix(apple): recognize AppKit cell ownership (#7916) 2026-10-29 07:55:05 +08:00
morluto 63491645fd fix(ledger): withhold readiness for unresolved evidence (#7928) 2026-10-29 07:54:41 +08:00
Hasan Zayni a4bd924772 fix(call-path): reject incompatible coordinate mappings (#7930)
* fix(call-path): reject incompatible coordinate mappings

Use the existing provider-owned coordinate context comparison before composing dossiers. Preserve same-mapping profiles and missing-dossier uncertainty. Fixes #7345.

* test(call-path): assert the structured rejection instead of its wording

docs/testing.md asks for structured error checks. Assert the issue path and reason, and keep the MCP category and code.
2026-10-29 07:54:09 +08:00
morluto 66f5b650d9 fix(apk): explain unknown signer comparison (#7927) 2026-10-29 07:53:38 +08:00
morlutoandgreptile-apps[bot] 1631dcb09a docs: include Kuna in native provider guidance (#7929)
* docs: include Kuna in native provider guidance

* docs(i18n): include Kuna in native provider guidance

* docs: align provider guide links

* Update README.md

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* docs: align provider links and translated formatting

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-10-29 07:53:28 +08:00
morluto 42d5425417 fix(browser): enforce initial navigation default deadline (#7934) 2026-10-29 07:53:18 +08:00
hhangandhhang 07dfd12613 fix(pe): reject non-MZ inputs before image inspection (#7709)
Co-authored-by: hhang <[email protected]>
2026-10-29 07:45:32 +08:00
吴佳浩Alben f1da7f7670 fix(javascript): read the spawn env option as Node reads it (#7713)
* fix(javascript): read spawn env as Node reads it

Node's normalizeSpawnArguments starts with `const env = options.env ||
{ ...process.env }`, so a falsy env is replaced by a copy of the parent
environment. A child started with env: null, undefined, 0, "" or false
therefore inherits everything, exactly as if no env were passed.

REA tested the key's presence alone, so every one of those reported
environment_supplied: true and the graph emitted a resolved supplies-env
relation. The presence test also missed the opposite direction: an env
supplied through an inline object spread reported false although Node uses
it.

Read the declared value the way stdioMode already reads stdio: an explicit
property whose statically known value is falsy does not supply an
environment, and an inline spread that carries env does.

Verified on Node v24.22.1 with a child that prints a parent marker variable:
no options, env: null, undefined, void 0, 0, "" and false all print the
marker, while env: {} prints nothing and env: { A: "1" } prints only that.

* fix(javascript): read falsy numeric literals as Node does

greptile flagged that environmentSupplied still reported a supplied environment for
`env: -0` and `env: 0n`: staticJavaScriptTruthiness recognized neither form, so it
returned undefined and `!== false` turned that into true. Node's
normalizeSpawnArguments uses `options.env || { ...process.env }`, so both inherit.

Extend the shared truthiness reader to cover a BigInt literal and a negated numeric
literal, and add regression cases for `-0`, `0n` and `0.0`.

The reader is shared with the route scan and the state tracker; both treat
`undefined` as "unknown", so a more precise `false` only strengthens them.
2026-10-29 07:45:22 +08:00
Wenyu Chiou 3142f7b0dd fix(adb): align CLI selector schemas with canonical inputs (#7722)
* fix(adb): align CLI selector schemas with canonical inputs

Reuse canonical selector constraints for ADB CLI admission and cover schema, parser, and inert provider boundaries. Independent source review approved tree bc722f8d1b1f8d014be29533e6845c6f897d5918; full contribution checks remain pending.

* test(adb): expect CLI serial validation refusal

Independent review approved staged diff 35418e6994339af838b490be51278fd41b9f8e3e. Focused serial refusal test, lint, and formatting passed with existing dependencies.

* test(adb): assert CLI selector validation stage

Independent review approved staged diff a1164a8dcf1df1f0d60323d7a53a69ee2bfdffeb. Focused dispatch tests, targeted lint and formatting passed using existing dependencies.

* fix(adb): retain safe selector validation constraints

Independent review: review_ci_delta APPROVE for diff 18748a3b4b112dc96b95f7c1099bd479e1d1587b. Focused tests, typecheck and targeted lint passed; the aggregate check remains limited by a matching baseline Knip finding.
2026-10-29 07:44:43 +08:00
ShuaixinHuangandmorluto fc24b08851 feat(artifacts): read Zstandard ZIP members (#7780)
* feat(artifacts): read Zstandard-compressed ZIP members

* fix(artifacts): classify ZIP dictionary failures

---------

Co-authored-by: morluto <[email protected]>
2026-10-29 07:44:10 +08:00
ShuaixinHuang edc908af58 fix(xml): reject ambiguous namespace attribute identities (#7785) 2026-10-29 07:38:46 +08:00
PARK HYEONMIN (박현민) b4923f85c5 docs(ghidra): count 27 read-only operations in current guides (#7794)
The inventory registry gained read_binary_inventory_summary, so the
read-only inventory is fifteen inventory and twelve function operations.
Update the adapter summary and the Windows lane descriptions to match the
registries the Windows verifier derives its coverage from. The historical
Windows verification record keeps its observed 26-operation count.

Fixes #7611
2026-10-29 07:38:33 +08:00
ShuaixinHuang 9ac9f91ab5 fix(firmware): validate Intel HEX start-record offsets (#7806) 2026-10-29 07:37:39 +08:00
ShuaixinHuang c2cc7e6eb0 test(cli): cover literal Markdown heading output (#7866) 2026-10-29 07:37:20 +08:00
morluto a001231256 fix(adb)!: report local filesystem admission failures (#7885)
* fix(adb)!: report local filesystem admission failures

* test(adb): assert filesystem error provenance
2026-10-29 07:37:08 +08:00
RRFZandmorluto 8c83886891 fix(pe): report exhausted certificate decoder allowances as resource limits (#7881)
* fix(pe): report exhausted certificate decoder allowances as resource limits

A PKCS#7 payload that exhausted the fixed 64 KiB decoded-text or 16,384-value ASN.1 allowance was refused as malformed input with advice to correct request fields. Keep the allowance and limit on the decoder failure and report it as a nonretryable resource_constraint with fixed-allowance recovery.

Fixes #7531

* test(pe): cover certificate allowances through MCP

---------

Co-authored-by: morluto <[email protected]>
2026-10-29 07:36:56 +08:00
morluto fd297b1b17 fix(apple): preserve bounded archive diagnostics (#7890)
* fix(apple): preserve bounded archive diagnostics

* test(apple): assert diagnostic facts without freezing prose
2026-10-29 07:36:29 +08:00
morluto 16cfe30dab fix(setup): identify invalid launch environment overrides (#7899)
* fix(setup): identify invalid launch environment overrides

* test(setup): cover merged environment recovery
2026-10-29 07:34:43 +08:00
morluto 473cfa36b8 fix(evidence)!: distinguish inline records from bundles (#7895)
* fix(evidence): distinguish inline records from bundles

* fix(evidence): preserve bundle integrity failure reasons
2026-10-29 07:34:31 +08:00
morluto 5082b50f07 fix(decompile): retain limitations across failed resume (#7913) 2026-10-29 07:30:44 +08:00
morluto 31c028f496 fix(mcp): align retained view handoff guidance (#7926) 2026-10-29 07:29:44 +08:00
Hasan Zayni e0719cffa4 fix(ledger): honor every selected fixture proof (#7931)
Prevent a passing fixture entry from hiding applicable failures in another selected entry. Fixes #7412.
2026-10-29 07:27:57 +08:00
morluto 21b192706d docs(website): correct client and IDA support guidance (#7918)
* docs(website): correct client and IDA support guidance

* style(website): format setup support guidance
2026-10-29 05:37:06 +08:00
morluto 286629b570 fix(test): remove duplicate unknown session imports (#7917) 2026-10-29 05:30:05 +08:00
morluto 50d731d456 fix(tests): remove duplicate MCP test imports 2026-10-29 05:19:41 +08:00
morluto f9acfdcf49 fix(cli): retain committed evidence receipts on output failure (#7906) 2026-10-29 05:11:02 +08:00
morluto 9b880e0e3e fix(setup): report concurrent skill destination changes (#7898)
* fix(setup): report concurrent skill destination changes

* fix(setup): avoid rollback after exclusive link refusal
2026-10-29 05:10:45 +08:00
morluto 76c7698ac9 fix(cli)!: preserve native value input diagnostics (#7889) 2026-10-29 05:10:28 +08:00
morluto 83cd1e04c9 fix(javascript)!: report ambiguous artifact paths (#7864)
* fix(javascript): report ambiguous artifact paths

* fix(javascript): retain unavailable colliding sources
2026-10-29 05:10:18 +08:00
morluto 29945d435d fix(process): preserve availability when macOS ownership preparation fails (#7856)
* fix(process): preserve availability when macOS ownership preparation fails

* test(process): verify affected macOS provider availability
2026-10-29 05:10:04 +08:00
morluto 6c205d8a79 fix(evidence): align bundle receipts and conflict recovery (#7849) 2026-10-29 05:09:54 +08:00
morluto 4e663beeb8 fix(javascript): retain guards after labelled breaks (#7841)
* fix(javascript): retain guards after labelled breaks

* fix(javascript): bound labelled exit inspection

* fix(javascript): preserve local break completion

* fix(javascript): avoid exits through unknown control flow

* fix(javascript): retain guards after bounded jump scan
2026-10-29 05:09:45 +08:00
morluto 1e088c19ad fix(environment)!: report recorded reads and target uncertainty (#7834)
* fix(environment)!: report recorded reads and target uncertainty

BREAKING CHANGE: Environment state and coverage can report method-replay for non-GET safe reads; exhaustive reason consumers must handle it.

* docs(environment): include safe-method replay reason
2026-10-29 05:09:31 +08:00
morluto cc5a4062ff fix(browser): preserve unknown WebMCP comparison facts (#7833) 2026-10-29 05:09:22 +08:00
morluto 6c76a7ffb0 fix(wasm): retain core facts with invalid custom names (#7822)
* fix(wasm): retain core facts with invalid custom names

* fix(wasm): retain objdump output on parse failure
2026-10-29 05:08:41 +08:00
morluto 114731eb1e fix(process): retain uncertain cleanup ownership (#7845)
* fix(process): retain uncertain cleanup ownership

* fix(process): retain moved members through cleanup retry

* test(process): reconstruct ownership ledger in cleanup fixture
2026-10-29 05:08:29 +08:00
morluto 583a316e11 fix(macho): preserve POSIX app executable names (#7888) 2026-10-29 05:08:17 +08:00
morluto f6380282fb fix(web)!: retain invalid browser selection (#7903)
fix(web): retain invalid browser selection
2026-10-29 05:08:02 +08:00
morluto 2aa0d45e13 fix(mcp): guide oversized unknown recovery (#7876) 2026-10-29 05:07:35 +08:00
morluto 5f146b3b19 fix(ida): retain selected endpoint on connection failures (#7894)
* fix(ida): retain selected endpoint on connection failures

* fix(ida): redact encoded endpoint credentials and preserve causes
2026-10-29 05:07:02 +08:00
morluto 553f19590f fix(harmony): exclude symlinks from unavailable files (#7886) 2026-10-29 05:05:35 +08:00
morluto e03ff93913 fix(frida): retain selected discovery endpoint on failure (#7892) 2026-10-29 05:04:53 +08:00
morluto 78a350e0b6 fix(skill): clarify packet capture tool selection (#7901) 2026-10-29 05:04:35 +08:00
morluto 52617bd50e fix(model): recognize safetensors ZIP signature collisions (#7816) 2026-10-29 04:59:25 +08:00
morluto 675c73fb4d fix(apple): preserve post-table plist objects (#7820) 2026-10-29 04:59:14 +08:00
morluto bd687bbc40 fix(ghidra)!: distinguish unlisted instruction addresses (#7823)
* fix(ghidra)!: distinguish unlisted instruction addresses

BREAKING CHANGE: Ghidra now returns not-in-listing for in-memory addresses without listed instructions or defined data. Consumers matching the former undecodable value must handle the new status.

* fix(ghidra): invalidate snapshots with old instruction status

* test(ghidra): update raw profile commitment goldens
2026-10-29 04:59:04 +08:00
morluto b0880d660f fix(qlog): exclude hidden custom events from incomplete facts (#7825) 2026-10-29 04:58:53 +08:00
morluto 2dd9a3092e fix(artifacts): extract readable inventoried entries (#7827)
* fix(artifacts): extract readable inventoried entries

* test(artifacts): align directory omission coverage
2026-10-29 04:58:43 +08:00
morluto 5c60720ad7 fix(pe): distinguish bounded imphash name reads (#7830) 2026-10-29 04:58:33 +08:00
morluto 9b7c2bb1ac fix(javascript): retain anchored route constants under reserved prefixes (#7838) 2026-10-29 04:58:22 +08:00
morluto 0759fb9ad5 fix(build): explain unsupported source loader runtimes (#7839) 2026-10-29 04:58:11 +08:00
morluto 46c457f74c docs(ghidra): fix DOL guide PowerPC link (#7857) 2026-10-29 04:57:59 +08:00
morluto 1b636c890f fix(macho): retain inspection when vtool rejects universal container (#7865)
* fix(macho): retain inspection when vtool rejects universal container

* test(macho): assert facts retained after vtool refusal
2026-10-29 04:57:22 +08:00
morluto fe52ea12c3 fix(evidence): explain fixed analysis view page recovery (#7860)
* fix(evidence): explain fixed analysis view page recovery

* fix(evidence): explain unpublished view recovery
2026-10-29 04:55:53 +08:00
morluto 00bd92c56e fix(ida): align workspace discovery with readiness (#7863) 2026-10-29 04:55:44 +08:00
gensokyo-devandEntrpi 37827af591 feat(artifacts): compare inventories from Evidence sources (#7869)
* feat(artifacts): compare artifacts by Evidence reference and from the CLI

compare_artifacts made agents send back the complete inventory Evidence
that REA already retains, and the CLI had no artifact comparison.

Each side of compare_artifacts now takes exactly one of: complete inline
Evidence (unchanged), {kind: "retained-evidence", evidence_id}, or
{kind: "evidence-file", path, evidence_id?}. The two references are the
sources inspect_analysis_view accepts; both tools now compose one
definition in evidenceReference.ts. A summary still resolves to its exact
retained parent, and every source form gives the same comparison Evidence.
An unresolvable retained reference is refused with a typed missing
reference error.

The application layer owns source resolution and the comparison Evidence,
so MCP and the new `rea compare-artifacts` command share one workflow.
readEvidenceFileSource reports bundle-selection refusals at the caller's
own field path.

* refactor(artifacts): resolve compare_artifacts sides like compare_functions

compare_functions, compare_wasm_interfaces and the JavaScript application
comparisons name each side with evidenceInputSchema and resolve it with
resolvePairedEvidenceRequest. compare_artifacts reached the same result
for inline and retained sides through its own dispatch.

evidenceSourceInputSchema now extends evidenceInputSchema with the
Evidence-file reference, and resolvePairedEvidenceSources sends inline and
retained sides to the same resolveEvidenceInput and file sides to the
shared Evidence file reader. compare_artifacts uses both, so the forms it
shares with compare_functions are parsed and resolved by one owner. The
advertised input schema is unchanged.

* docs(cli): point rea compare to compare-artifacts for inventory contents

rea compare (compare_bundles) compares records by Evidence identity and
explicit pairs and never reads artifact contents, so two build
inventories stay unresolved there. Its help and the CLI guide now name
compare-artifacts for that comparison.

* fix(artifacts): resolve summary parents within Evidence source bundles

* perf(evidence): reuse file-owned immutable authentication proofs

* test(evidence): inject failures at both authentication boundaries

---------

Co-authored-by: Entrpi <[email protected]>
2026-10-29 03:12:03 +08:00
gensokyo-dev 2f3c71f39e docs(errors): align byte entropy recovery catalog (#7884) 2026-10-29 03:03:04 +08:00
RRFZ b75419b61e test(firmware): keep producer fixtures within supported Node limits (#7048)
* test(firmware): bound fixture hashing and avoid Node startup warnings

* test(firmware): apply the Wasm flag only on Node 22
2026-10-29 02:42:21 +08:00
gensokyo-dev 8f9af920ef fix(pcap): redact HTTP credentials in failed capture diagnostics (#7871)
Project bounded JSON diagnostics using shared URI roles and source spans.
Keep complete parser input unchanged and compute safe diagnostics lazily.
Preserve byte facts, error classification, path data and cleanup ownership.
Reuse JSON escape-coordinate mapping with IDA diagnostics.

Fixes #7812

Assisted-by: Reimu (gensokyo-skills:reimu-incident-triage)
Assisted-by: Kogasa (gensokyo-skills:kogasa-surprise-testing)
Assisted-by: Yukari (gensokyo-skills:yukari-boundary-analysis)
2026-10-28 18:14:22 +00:00
Rudy Mizrahi Celekli e23cb3b240 fix(browser): preserve script exports with filesystem-unrepresentable paths (#7526)
fix(browser): isolate unrepresentable script export paths

Signed-off-by: Rudy Celekli <[email protected]>
2026-10-29 02:13:54 +08:00
gensokyo-dev 4b0ff68e2e fix(browser): retain prior-hop response facts in session timelines (#7807)
* fix(browser): retain prior-hop response facts in session timelines

* fix(browser): share response URL classification

* fix(browser): preserve malformed response status failures

* fix(browser): account for filtered redirect response coverage

* test(browser): supply prior response URLs in redirect fixtures

* fix(browser): retain malformed orphan response status coverage
2026-10-29 02:04:15 +08:00
Jiakai Xu 19032204aa fix(javascript): resolve relative ESM backslash paths (#4323)
Normalize raw URL separators after admitting an original relative ESM
request, before artifact path validation and root confinement. Preserve
the authored declaration and the existing encoded-separator checks.

Retain Node-backed static and dynamic loader controls and compare the
application graph with the historical importer already present on main.
Narrow PR #4323 to this remaining case after the upstream #4269 fix.

Signed-off-by: Jiakai Xu <[email protected]>
2026-10-29 02:03:00 +08:00
Dragolone f19436c156 fix(artifacts): split U-Boot script uImages on their size table (#7805)
U-Boot's mkimage writes the multi-file size table for script images
(`mkimage -T script`, the boot.scr format) exactly as it does for multi
images, `mkimage -l` lists the script as "Image 0", and the `source`
command skips the table before running the script ("scripts are just
multi-image files with one component", boot/image-board.c).

The uImage reader parsed the table only for IH_TYPE_MULTI, so a script
image reported sub_image_count 0 and exposed only payload.bin, whose
first bytes are the binary size table rather than the script text.

Treat IH_TYPE_SCRIPT like IH_TYPE_MULTI: parse the same bounded table,
expose image-<n>.bin components and report the size-table limitation.
payload.bin is unchanged. A script payload without a valid table now
fails closed, as a multi image already does.
2026-10-29 01:57:09 +08:00
morluto 44331d04d2 fix(browser): retain launch failure diagnostics (#7826) 2026-10-29 01:56:39 +08:00
morluto 0a272d5cf5 fix(capture): bound preview headlines by source format (#7832)
* fix(capture): bound preview headlines by source format

* test(capture): cover scalar HAR preview extensions
2026-10-29 01:56:07 +08:00
morluto 0747e46401 fix(cli): recognize integrity failures with partial observations (#7836) 2026-10-29 01:55:57 +08:00
morluto 183e2e2d1e fix(il2cpp): validate structural record extents (#7861) 2026-10-29 01:55:47 +08:00
morluto 5119d08daf fix(web): preserve portable captured script paths (#7862) 2026-10-29 01:55:38 +08:00
morluto 1e421242b9 fix(javascript): preserve uncertainty for incomplete runtime captures (#7854) 2026-10-29 01:53:03 +08:00
morluto 5bef2b48b6 fix(input)!: classify unresolvable source paths (#7847) 2026-10-29 01:52:34 +08:00
morluto e79ad6ca8e fix(jeb): preserve selected endpoint in failures (#7835)
* fix(jeb): preserve selected endpoint in failures

* fix(jeb): retain endpoint on typed failures
2026-10-29 01:30:24 +08:00
morluto 128c2cf632 fix(sqlite): report SQL statement resource allowance (#7829) 2026-10-29 01:30:08 +08:00
morluto aaf4d21068 fix(source-map): clarify policy and evidence provenance (#7828) 2026-10-29 01:29:59 +08:00
morluto aba158506b fix(javascript): preserve repeated native resolver calls (#7824) 2026-10-29 01:29:48 +08:00
morluto 477f63ae21 fix(session)!: classify unavailable snapshot prerequisites (#7837)
fix(session): classify unavailable snapshot prerequisites
2026-10-29 01:28:57 +08:00
morluto 60ab4da329 fix(cli): correct completion options and setup guidance (#7851)
* fix(cli): align MCP doctor completion with its options

* fix(cli): explain zsh completion initialization

* fix(cli): include launcher timing in doctor completion

* style(cli): format doctor completion schema
2026-10-29 01:28:04 +08:00
morluto f9e7571a3d feat(javascript): discover semantic call literals in feature traces (#7840)
* feat(javascript): discover semantic call literals in feature traces

* fix(javascript): bound retained call literals by transfer capacity

* test(javascript): align feature no-match diagnostic
2026-10-29 01:25:23 +08:00
morluto 685f6db960 fix(browser): preserve unknown CDP source coordinates (#7842) 2026-10-29 01:23:56 +08:00
morluto b21a975faf fix(update): distinguish launcher failures from registry errors (#7843) 2026-10-29 01:23:29 +08:00
morluto d49a1504ca fix(erlang): respect legacy atom table layout (#7844) 2026-10-29 01:23:17 +08:00
morluto 4109271bcb fix(process)!: reject unsupported one-column terminals (#7848)
* fix(process)!: reject unsupported one-column terminals

* test(process): exercise actual two-column resize
2026-10-29 01:23:02 +08:00
morluto a6295e433b fix(python): preserve code objects after STORE_SLICE (#7815) 2026-10-29 01:11:27 +08:00
morluto af1ccb6506 fix(android): preserve manifest component spelling (#7818) 2026-10-29 01:10:45 +08:00
morluto 7dbebb4e7d fix(browser): preserve prefixed XHTML link hints (#7819) 2026-10-29 01:10:36 +08:00
morluto dfa0689890 fix(process): preserve comparison uncertainty and attribution (#7853)
* fix(process): preserve comparison uncertainty and attribution

* fix(process): retain known file differences with unknown effects

* fix(process): preserve proven filesystem path differences

* fix(process): preserve trace and symlink differences
2026-10-29 01:01:16 +08:00
morluto 251e27ef3b fix(website): align Electron guide with downloadable source (#7858)
* fix(website): align Electron guide with downloadable source

* test(website): detect renderer example drift
2026-10-29 00:56:14 +08:00
Entrpiandgensokyo-dev 64196b08ca feat(artifacts): inventory and extract ESP32 firmware images (#7498)
* feat(artifacts): inventory and extract ESP32 firmware images

ESP32-family app and bootloader images (magic 0xE9) had no reader, so
open_binary refused OTA downloads, app partitions and bootloaders.

Add the esp-image container format behind inspect_artifact and
extract_artifact. A pure decoder reads each byte once and keeps only
fixed-size fields. Derived facts cover the chip, from a table cited to
ESP-IDF v6.1; segment classification as ESP-IDF's should_map and
should_load; checksum and appended SHA-256 results; app and bootloader
descriptors; Secure Boot v2 signature blocks, observed and unverified;
and undescribed trailing byte ranges. Truncation and undescribed bytes
make coverage unknown. ESP8266 images are refused with a specific
reason, and ciphertext stays unrecognized.

Add the format to every target, Evidence, snapshot, live-summary and
artifact vocabulary, document it, and add an optional verify:esp-images
lane that compares esptool-built images with esptool image-info and
espsecure signature-info-v2.

Refs #3615

* test(artifacts): run the ESP image lane on esptool v5.6.0

The lane compared descriptor fields only when esptool image-info printed
one, so a descriptor that REA reports and esptool does not print passed
without comparison. Compare the descriptor kind in both directions.

Record that the lane passed for all 13 images with esptool and espsecure
v5.6.0, the version the layout facts cite, as well as with 4.12.0.

* refactor(artifacts): define ESP image layout sizes once

The decoder and the derived facts each defined the segment-header,
appended-digest and flash-sector sizes, the erased-byte scan and the
offset formatter. Define them once with the other fixed-layout structures
and import them in both places. The signature slot count is the one
constant that drives the slot scan.

Correct the observedEnd comment: it is the end of the last chunk read.

* fix(artifacts): report ESP-IDF's segment length limit by chip

ESP-IDF's verify_segment_header rejects a segment of 16 MiB or more
unless the chip maps flash through 32-bit cache addresses
(SOC_SPI_MEM_SUPPORT_CACHE_32BIT_ADDR_MAP: ESP32-S3, P4, C5, C61, H4 and
S31 in v6.1). Record that capability in the chip table and add a
limitation for such segments; unknown chips get none.

`loaded` repeats should_load. It does not mean the bootloader accepts the
segment: verify_load_addresses still rejects one outside the chip's RAM,
which REA does not model. Say so in the docs, the skill reference and the
inventory limitation.

* test(artifacts): check ESP lane tools first and verify extraction

The lane started work before checking its tools, so a missing RISC-V
target, ld.lld, esptool or espsecure surfaced as a raw spawn or command
error. Check each one before building and name the variable to set. Warn
when esptool is not v5.6.0, the release the layout facts cite.

Assert each segment's class for the ESP32-C3 app, its variants and the
bootloader, as the Xtensa image already did. Extract the app and the
signed image through the CLI and compare every file with the bytes at
esptool's segment offsets or in the appended signature sector; the
source's SHA-256 must be unchanged and equal the reported root_sha256.

* docs(artifacts): state what the ESP image lane does not establish

The Verification section listed the lane's checks but not its gaps: only
ESP32-C3 and ESP32 images come from a real producer, no real project
build, OTA download or flash dump is inspected, no signature is verified,
and the lane does not run in CI.

Record the timing of the final lane run on esptool v5.6.0.

* fix(artifacts): compare ESP segment declarations

Segment entries hash only their data, so compare_artifacts reported a
segment that moved to another load address with the same bytes as
unchanged; only the image itself changed. Compare each segment's
load_addr and data_len as its declaration, as Node SEA and PyInstaller
members are compared, and count a changed declaration as a metadata
change, also when content is unavailable.

Header offsets are layout and the classification is derived, so neither
is compared.

* test(artifacts): run ESP lane commands as owned process trees

The lane ran its tools through execFile. A timeout killed only the direct
child, so a linker or assembler that Clang or GCC had started could keep
running while the lane removed its workspace. Run every command through
createVerifierCommands, check the tools inside withVerifierCleanup, and
remove the workspace only after commands.close() has stopped every tree;
a failed close keeps the workspace.

Only a nonzero exit now counts as a refusal or an expected esptool
failure; timeouts and spawn failures propagate. Supervising each command
roughly doubles the lane's run time.

* test(artifacts): cancel ESP decoding after its first decoded chunk

The cancellation test requested cancel or close on setImmediate, which
can land while the file is still being opened and checked, so it never
showed that decoding had started. It also passed against a reader that
ignored both requests while decoding. Acknowledge the decoder's first
chunk, then cancel or close, join the operation, and assert that no
later chunk reaches the decoder and that the reader ends closed.

* docs(artifacts): record which ESP metadata comparison ignores

Segments are compared by their declaration. The image's and the
signature blocks' format_metadata are not: every field comes from their
own bytes, which content comparison already covers, and a block's
image_digest_matches follows the image's content.

* docs(artifacts): state that ESP mmu_page_size is a base-2 logarithm

The app descriptor's mmu_page_size keeps ESP-IDF's name and value, the
MMU page size in log base 2 as esp_app_desc_t declares it, so 16 means
64 KiB. Say so in docs/esp-image.md and the schema comment; a reader
could otherwise take 16 as 16 KiB.

* fix(esp-image): retain member consumption and share lifecycle regressions

* test(artifacts): scope MCP catalogs and budget full-schema validation

* test(artifacts): verify compact output profiles and document bounded catalogs

* test(artifacts): close readers when cleanup-test admission fails

* test(artifacts): compare ESP image limitations with their owners

The decoder and reader tests froze complete limitation sentences, so a
harmless rewording failed them. The wording now has two owners:
ESP_IMAGE_LIMITATIONS in espImageFacts.ts, which the decoder, the inventory
and the reader use, and ESP_DESCRIPTOR_LIMITATIONS beside the descriptor
decoder. Tests compare delivered limitations with the owner, called with the
facts each case establishes (segment index, declared and available bytes,
offsets, chip, page range), next to the structured assertions they already
make. Delivered text is unchanged.

* test(esp-image): assert structured facts and owned guidance

* fix(esp-image): keep spi_flash_mode and descriptor fields before a truncated tail

ESP-IDF v6.1's esp_app_desc_t declares spi_flash_mode at byte 181, after
mmu_page_size, as the committed reference layout shows; the descriptor now
keeps it as stored. The decoder also required all 256 descriptor bytes,
though it reads none past byte 181, so a file ending in the reserved tail
lost the version, project, digest and revision facts it does hold. The app
descriptor now needs only its fields; the segment's truncation is reported
as before.

* test(esp-image): pin the P-192 public key digest to espsecure's layout

espsecure packs ECDSA signature blocks as "<BBBx32sB64s64s1031x" and
zero-pads a 48-byte P-192 point to 64 bytes, so the signature starts at
byte 101 for every SHA-256 curve. signature_info_v2 hashes sig_block[36:101]
and _digest_sbv2_public_key the curve ID with the padded point. No test built
a P-192 block; this one checks public_key_sha256 against that definition.

* fix(esp-image): keep each app descriptor field the file holds in full

A file can end anywhere inside the 256-byte esp_app_desc_t. The decoder
needed every field byte, so a cut at byte 176 dropped the version, project
and app_elf_sha256 it does hold. Once the 4-byte magic identifies an app
descriptor, each field is now kept when the file holds all of its bytes and
is null otherwise; observed_length records where the descriptor ends, so a
cut field is told apart from text that is not UTF-8. The fields after the
magic become nullable in the metadata schema.

* fix(esp-image): report a signature block whose image digest does not match

A CRC-valid Secure Boot v2 block can carry another image's digest. The
block recorded image_digest_matches false, but no limitation said so, unlike
a checksum or appended-SHA mismatch, so CLI and MCP summaries showed only
the generic unverified note. The mismatch is now a limitation; coverage
stays complete and signature_status stays unverified. The three other
signature-sector limitations move into ESP_IMAGE_LIMITATIONS with their
text unchanged, so all ESP image wording has one owner.

* refactor(esp-image): remove redundant descriptor byte guards

---------

Co-authored-by: gensokyo-dev <[email protected]>
2026-10-28 23:18:22 +08:00
gensokyo-dev 6c895eeb8e fix(process)!: redact sensitive terminal echoes before retaining Evidence (#7804)
Seal sensitive terminal chunks before deferred rendering, apply one bounded
capture-local literal matcher, and retain exact redacted byte accounting.
Keep private input facts in explicit fields and separate comparison policies.
Retain distinct terminal failures once and verify structured refusal facts.

Fixes #7782

BREAKING CHANGE: sensitive interaction data is empty with sensitive and input_bytes fields instead of a byte-count placeholder. Sensitive raw_data is also redacted. Read input_bytes and recapture both comparison sides when migrating older private-input Evidence.

Assisted-by: Reimu (gensokyo-skills:reimu-incident-triage)
Assisted-by: Kogasa (gensokyo-skills:kogasa-surprise-testing)
Assisted-by: Yukari (gensokyo-skills:yukari-boundary-analysis)
2026-10-28 14:39:34 +00:00
ShuaixinHuang 8f97a45483 fix(plist): end OpenStep comments at native line separators (#7793) 2026-10-28 22:23:16 +08:00
Dragolone 6fd5d347c7 fix(browser): ignore repeated CSP directives within one policy (#7798)
CSP3 parsing keeps the first directive of each name in a policy and ignores
later repeats; Chrome reports "Ignoring duplicate Content-Security-Policy
directive". parseCsp flattened every policy's directives into one stream, so
a header such as `script-src 'self'; script-src 'unsafe-inline'` reported the
ignored `'unsafe-inline'` directive as declared and counted nonces and hashes
from it. Track directive names per policy so only the effective directive is
retained, while the same name in separate comma-separated or repeated-field
policies is still listed once per policy.
2026-10-28 22:23:00 +08:00
ShuaixinHuang b14b94f0f5 fix(asn1): reject non-minimal OID subidentifiers (#7800) 2026-10-28 22:22:43 +08:00
Ysopwnableandgensokyo-dev 7b6d872a25 docs: celebrate 400,000 GitHub stars 🎉 (#7801)
Co-authored-by: gensokyo-dev <[email protected]>
2026-10-28 22:12:43 +08:00
gensokyo-dev b9a8f8e9f2 docs(update): align error catalog with current recovery (#7795) 2026-10-28 21:04:12 +08:00
gensokyo-dev 23e7c4e8e2 fix(setup): preserve specific skill path recovery (#7791) 2026-10-28 20:49:05 +08:00
gensokyo-dev ac545adae2 fix(apk): classify fixed inspection allowances as nonretryable (#7784) 2026-10-28 20:13:58 +08:00
ShuaixinHuang 9593bc536b fix(protobuf): reject malformed descriptor field declarations (#7730) 2026-10-28 19:52:11 +08:00
JG Tromp 7c389cbefc fix(javascript): keep both-direction feature traces from crossing shared containers (#7743)
* fix(javascript): keep both-direction feature traces from crossing shared containers

trace_application_feature with direction "both" walked the application
graph as if it were undirected. From a seed, one outgoing or incoming step
into a shared container (an asset or the artifact root) followed by steps
in the other direction reached every sibling of that container, so a trace
seeded on one storage key returned the whole connected graph.

"both" now returns the union of the outgoing and incoming closures. Each
walk keeps its own predecessor chains, so a terminal path never changes
direction midway. "outgoing" and "incoming" are unchanged.

* test(javascript): check both-direction trace paths end to end

The existing tests checked predecessor maps but no returned path. Add a
trace whose seed reaches one storage terminal outgoing and another
incoming, and assert each path's complete node_ids and edge_ids. A
pathTo that reads only the first walk's map returns a one-node path for
the incoming terminal and fails this test.

* fix(javascript): confine only ascended containers in both-direction traces

Making "both" the union of separate outgoing and incoming walks dropped
modules that a feature reaches by crossing directions. In the Notes
known-answer evaluation, the notes:export channel is pointed at by both
the preload asset (invokes) and the handler (handles); its preload,
renderer and csv modules are reached only through relationships that
change direction, so the trace kept main.js alone and the feature_modules
claim failed in tests/boundary/mcp/agentKnownAnswerMcp.test.ts.

Walk both directions in one traversal again, and confine only the
containment case the change was meant to stop: a node reached by stepping
up a contains edge from one of its children follows only further contains
steps up. A shared asset or artifact root then no longer leads into its
other children, while relationships across the feature are kept. The
Notes trace returns csv.js, main.js, preload.js and renderer.js again
(30 nodes; the previous main returned the whole 32-node graph).
outgoing and incoming traces are unchanged, and paths come from one
predecessor map again, as before this branch.
2026-10-28 19:51:59 +08:00
JG Tromp 8b74f30bf9 fix(javascript): stop double-expanding application projection estimates (#7744)
* fix(javascript): stop double-expanding application projection estimates

estimateJavaScriptStaticApplicationProjection multiplied the per-record
metadata allowance (2 KB plus the path) by the 8x expansion that it also
applies to checkpoint bytes. A projected record serializes to about 0.46 KB,
so the allowance alone already covers retention; expanded again, the
charge was about 25x the serialized application relationships.

The charge accumulates in the parent's shared result budget. On a
synthetic 2.8 MB Vite-style build (88 files, one 456 KB chunk), the first
10 files used 2.0 GB of a 2.08 GB budget while the parent heap held about
300 MiB. The other 78 files lost their application relationships and were
offered a 0-byte semantic budget. On a 201-file build the charges exceeded
the budget at file 93 and the analysis failed with resource_constraint,
leaving the remaining sources without static facts.

Keep the 8x expansion for checkpoint and repeated bytes and count the
per-record allowance once. The same builds now refuse 14 and 102
projections instead of 78 and a failed run; peak RSS stays at 1.2 GB and
2.0 GB under a 4.2 GB parent heap limit.

workerFailureComposition simulated 64 KiB of parent result budget, which
the corrected charge for its one-finding checkpoint fits. Lower that
headroom to 32 KiB so the case still exercises a refused projection.

* test(javascript): cover application projections near the shared parent budget

The estimate test compares one file's charge with its serialized graph,
but no test put several sources against one parent result budget. Replay
one recorded static checkpoint for five sources against an exact budget:
sources 1-3 fit, source 4 misses by half a source and keeps its static
facts with the reported required and budget bytes, and the last source
fits again once no checkpoint reserve is held back. A worker that charges
the projection twice, or not at all, passes the existing tests and fails
this one.

* test(javascript): cover a malformed source before a valid one under the shared budget

The testing guide asks representation-expansion and capacity changes for
a large malformed input followed by a valid input, and for escaped or
expanded representations.

Under an exact parent budget, a 3 MiB checkpoint cut off inside a string
comes first and is rejected as unreadable output with no static facts.
The valid source after it keeps its application relationships; it fits
only if the rejected bytes took no charge. A worker that charges the
static checkpoint on receipt, before parsing, passes the existing tests
and fails this one.

The estimate ratio test also runs with a nested source path and storage
keys holding quotes, backslashes, a tab and multibyte text, which expand
in serialized JSON; the charge stays within 3x to 8x of the serialized
application graph (5.3x).
2026-10-28 19:51:38 +08:00
JG Tromp 7aa943c14b fix(javascript): resolve Vite build-output preload dependencies (#7745)
* fix(javascript): resolve Vite build-output preload dependencies

With an absolute base, Vite writes __vite__mapDeps entries relative to the
build output directory (for example assets/chunk.js), and its preload helper
prefixes the configured base at runtime. REA emitted these entries as
dynamic-import module specifiers, so a scheme-less path without a leading
dot resolved as a bare package and every entry was reported as an
unresolved "external" module, even when the build output directory was
selected.

The static analyzer now marks such entries with a vite-output: specifier,
as Webpack chunk loads are marked with chunk:. Entries starting with ./,
../ or /, and scheme-qualified entries, keep their previous handling. The
graph resolves a marked entry as a URL path under each ancestor directory
of the importing file; exactly one ancestor holding the file establishes
the build output directory. Otherwise the entry is not-found (or
unavailable when several ancestors match) with a limitation that names
the build output directory.

On a synthetic 7-file Vite build, the 12 preload entries that were
"external" now resolve when the build output directory is selected, and
are not-found with that guidance when only assets/ is selected.

* fix(javascript): keep unanalyzed Vite output candidates

resolveViteOutputDependency considered only resolved ancestors. A file
that exists under an ancestor but was not analyzed (for example
dist/assets/lazy.css for the entry assets/lazy.css) resolves there as
unavailable, and that result was replaced by the root's not-found with a
limitation saying that no ancestor holds the file.

Keep resolved and unavailable ancestor candidates together. One candidate
is returned with its own path and limitations; several are reported as
unavailable with every candidate path and limitation. Only when no
ancestor can hold the file does the root's not-found result stand.
2026-10-28 19:46:45 +08:00
Dragolone 33698e6b31 fix(apktool): read Apktool 2.9+ metadata in its line-oriented dialect (#7747)
Apktool 2.9.0 replaced SnakeYAML with the YamlWriter that 3.x still uses:
apktool.yml starts with an untagged `version: 2.x.y` line and strings are
written unquoted with Java-style escapes. Only a `version: 3.x.y` header
selected the producer dialect, so 2.9.0-2.12.x output went through YAML
core parsing: a versionName of `1.0 #beta` became `1.0`, escaped quotes kept
their backslashes, an empty name became null, and `1.0: beta` made the whole
decode fail as invalid metadata.

Select the line-oriented reader for 2.9.0 and later as well. Older tagged
SnakeYAML documents keep YAML scalar semantics.
2026-10-28 19:46:11 +08:00
Dragolone fea4f2e397 fix(javascript): test literals for Next page paths in linear time (#7748)
Every source string literal without an established Next root was tested
against /(?:^|\/)app\/(?:[\s\S]*\/)?page(?:\.[cm]?[jt]sx?|-[^/?#]+\.js|)(?:[?#].*)?$/u.
The unanchored search restarted at every app/ segment and rescanned to the
end of the literal, and each page? or page# candidate scanned to the next
line terminator before $ failed. A long literal therefore took quadratic
time on the analysis thread: about 6 s for a 256 KB literal of repeated
app/ segments, and 3.5 s for 224 KB of x/page? followed by a newline.

The first app/ segment admits every later page segment, and . cannot cross
a line terminator, so a query or fragment after the page filename starts on
the last line. Test the tail after the first app/ segment one page segment
at a time instead; results are unchanged.
2026-10-28 19:45:59 +08:00
ShuaixinHuang 09225b08e1 feat(artifacts): read Godot 3 PCK version 1 archives (#7781) 2026-10-28 19:43:43 +08:00
gensokyo-dev c77735fa83 fix(setup)!: bind managed registrations to selected installation
Use one command owner for setup, client writers, Gemini policy and CLI defaults.
Migrate recognized runner registrations with backups and remove runner prewarm.
Retain unknown identities and exercise real offline npm, CLI and MCP workflows.

Fixes #7757

BREAKING CHANGE: managed MCP registrations use the selected absolute runtime
and REA entry script, including package-runner cache paths. Keep that installation
available or restore it before setup; unknown registrations require confirmed
manual repair. Setup no longer returns prewarm or emits its progress events.

Assisted-by: Reimu (gensokyo-skills:reimu-incident-triage)
Assisted-by: Kogasa (gensokyo-skills:kogasa-surprise-testing)
Assisted-by: Yukari (gensokyo-skills:yukari-boundary-analysis)
2026-10-28 11:34:28 +00:00
Cipherandgensokyo-dev 18a8e71c9f feat(artifacts): add U-Boot legacy uImage reader (part of #7129) (#7234)
* feat(artifacts): add U-Boot legacy uImage reader (part of #7129)

Recognize legacy uImages by their 0x27051956 magic and inventory them as containers: 64-byte header facts (arch/os/type/compression raw and named, load/entry addresses, name, timestamp) with header and data CRC32 verified, stored payload extractable as payload.bin, and multi-file images split from the stored size table into image-<n>.bin. Compressed payloads stay as stored; nothing is executed or decompressed. Wires the format through classification, the artifact reader dispatch, extraction, and the evidence/snapshot format vocabularies. Related to #7129 (uImage part only); HEX/SREC and FIT are out of scope.

* fix(uimage): preserve aligned component ranges and exact header facts

* fix(uimage): retain buffered members through consumption and cancellation

* fix(uimage): preserve active read and validation failure precedence

---------

Co-authored-by: gensokyo-dev <[email protected]>
2026-10-28 19:11:41 +08:00
gensokyo-dev 06d1247836 fix(evidence): preserve residual unknown constraint diagnostics (#7770) 2026-10-28 18:50:33 +08:00
ShuaixinHuang f483eab543 fix(captures): exclude normalized native numeric values (#7764) 2026-10-28 18:46:30 +08:00
gensokyo-dev df7119aeed fix(mcp): distinguish validation from connection loss (#7761)
* fix(mcp): distinguish validation from connection loss

* fix(mcp): reuse parsed HTTP disconnect facts
2026-10-28 18:17:45 +08:00
gensokyo-dev 60bad9ed06 fix(hopper)!: admit macOS arguments before application handoff (#7732)
Admit the final macOS Hopper launcher arguments before application handoff.
Keep prepared-image source identity, return precise typed refusal locations,
and preserve bootstrap/lease cleanup. Update native CLI/MCP recovery
coverage and the installation guide.

Fixes #7727

BREAKING CHANGE: macOS Hopper launches refuse double quotes, backslashes
and line breaks in final helper paths or arguments before application
handoff. Select literal-safe launcher/runtime/database paths and loader
arguments, or a compatible provider. Original executable names can remain
unchanged when their prepared image path is literal-safe.

Assisted-by: Reimu (gensokyo-skills:reimu-incident-triage)
Assisted-by: Kogasa (gensokyo-skills:kogasa-surprise-testing)
Assisted-by: Yukari (gensokyo-skills:yukari-boundary-analysis)
2026-10-28 09:12:30 +00:00
gensokyo-dev 3b242491a4 fix(cli): attribute keyed archive errors to archive argument (#7731)
* fix(cli): attribute keyed archive errors to archive argument

* test(cli): assert input-error projection reference reuse
2026-10-28 16:48:20 +08:00
blankymandgensokyo-dev e78ac1a55b feat(artifacts): read UnityFS AssetBundle containers (#7699)
* feat(artifacts): read UnityFS AssetBundle containers

inspect-artifact and extract-artifact now recognize the UnityFS signature
and treat format versions 6 through 9 as containers. Header facts (format
version, player/engine strings, declared size, dataflags, blocks-info
bounds and the declared 16-byte data hash) are retained alongside a
directory of CAB-* and .resS members. Member bytes stream on demand:
only the blocks covering the requested member are read and decompressed
with bounded expansion and cancellation.

Bundles declaring asset-bundle encryption keep header facts and report
unknown directory coverage. LZMA blocks-info reports unknown coverage;
LZMA member reads fail as unavailable while the directory stays
readable. Malformed counts, out-of-stream members and declared sizes
beyond the file fail as format errors. UnityFS classifies by bytes in
root detection, inventory graph construction and desktop inspection,
which emits a unityfs-header fact.

Validation: tsc --noEmit, oxlint (no new findings on touched files),
17 reader tests covering LZ4HC/uncompressed tables, blocks-info at end,
format 6 without padding alignment, multi-block members, malformed
input, unsupported compression, cancellation and abandoned streams;
full artifacts/domain/conformance suites pass. Verified against one
real YooAsset bundle (Unity 2021.3.32f1, format 8, LZ4HC): the
streamed SerializedFile contained its managed assembly byte-identical
to a UnityPy extraction. Other producer variants unverified.

* fix(unityfs): own bounded member reads and preserve partial evidence

* refactor(unityfs): clarify producer revision flag decisions

* fix(artifacts): preserve coverage authority and bound UnityFS block work

---------

Co-authored-by: gensokyo-dev <[email protected]>
2026-10-28 16:47:39 +08:00
gensokyo-dev 6a226bd092 fix(apple): validate keyed archive selectors consistently (#7728) 2026-10-28 16:01:17 +08:00
gensokyo-dev 2b8fb387b6 fix(windows): resolve default tools before process launch (#7725)
Resolve Windows ADB, Apktool and update commands against the selected
PATH/PATHEXT before launch. Keep explicit paths and relative PATH choices,
report lookup failures, and reuse absolute batch invocation and ownership.

Add native launch/argv regressions, real npm metadata probes and focused
CI ownership so these boundaries retain Windows verification on the fast tier.

Fixes #7724

Assisted-by: Reimu (gensokyo-skills:reimu-incident-triage)
Assisted-by: Kogasa (gensokyo-skills:kogasa-surprise-testing)
Assisted-by: Yukari (gensokyo-skills:yukari-boundary-analysis)
2026-10-28 07:27:48 +00:00
gensokyo-dev 0e4b806db1 fix(cli): preserve missing-selection Markdown token windows (#7726) 2026-10-28 15:23:00 +08:00
gensokyo-dev a0a1962e06 fix(cli): validate byte queries before opening targets (#7723) 2026-10-28 15:00:01 +08:00
gensokyo-dev b0fb51cb4c fix(environment): share inline state byte admission (#7721)
Reuse shared padded-base64 facts and derive encoded admission from the existing decoded-byte budget. Refuse overflow before decoded allocation and retain padded syntax. Cover capacity, compact HTTP replies and live MCP state readback.

Fixes #7719

Assisted-by: Reimu (gensokyo-skills:reimu-incident-triage)
Assisted-by: Kogasa (gensokyo-skills:kogasa-surprise-testing)
2026-10-28 06:48:26 +00:00
5ae77656e7 feat(workflows): expose guided prompts through CLI and MCP (#6995)
* feat(workflows): expose guided prompts through CLI and MCP

* fix(workflows): address guided workflow review findings

* fix(workflows): preserve scoped contracts and actionable validation

* test(workflows): allow bounded CLI startup on shared hosts

* test(workflows): audit guidance effects and content provenance

* perf(workflows): defer rendering and separate catalog scope

---------

Co-authored-by: Deepak-Kambala <[email protected]>
Co-authored-by: gensokyo-dev <[email protected]>
2026-10-28 14:12:39 +08:00
gensokyo-dev b594fbc3e7 fix(apple): scope application roots to the inventory format (#7714) 2026-10-28 13:28:03 +08:00
gensokyo-dev 8397a0c9eb fix(plugins): align listing metadata with REA branding (#7711)
Use one packaged icon for Codex and Claude, and align listing descriptions with the repository About.
2026-10-28 05:16:41 +00:00
gensokyo-dev fbb0e79354 fix(cli): reject empty observation duration selections (#7710) 2026-10-28 12:57:14 +08:00
Aadityaandgensokyo-dev c36971953c feat(artifacts): inventory and extract Intel HEX and Motorola S-record regions (#7136)
* feat(artifacts): inventory and extract Intel HEX and Motorola S-record regions

open_binary refused Intel HEX and S-record programming files as an
unsupported format, so inspect_artifact and extract_artifact could not
answer which bytes load at which addresses.

Recognize both encodings by a fully valid first record and decode them
statically into address-contiguous regions, each an inventory entry named
0x<start>-0x<end>.bin and extractable as raw bytes. Root format_metadata
records the encoding and variant, record counts, unique and duplicate
bytes, address bounds, termination, declared start address, S0 header and
validated S5/S6 count. Gaps are never filled; start addresses are declared
facts, not proven entry points.

Checksum, record-type, byte-count, line-length, overlap-conflict, address
wrap and post-termination errors fail closed with the line number; a
missing termination record leaves coverage unknown. Decoding is bounded by
a 128 MiB file ceiling (decoded bytes are at most half the file and are
reserved after the size check) and 65,536 address-discontiguous runs.

Refs #7129

* fix(artifacts): refuse Intel HEX extended address records with a non-zero address

* fix(artifacts): bound HEX region copies and retain stream cancellation

---------

Co-authored-by: gensokyo-dev <[email protected]>
2026-10-28 12:35:55 +08:00
gensokyo-dev ad7d4c1f4d docs(errors): align MCP validation status and verification columns (#7708) 2026-10-28 12:19:25 +08:00
gensokyo-dev b69d8c5467 docs(changelog): repair historical release heading links (#7707) 2026-10-28 12:12:42 +08:00
gensokyo-dev c01ed783ac fix(elf): share section-table presence validation with notes (#7706) 2026-10-28 12:08:25 +08:00
d3b31a0e29 feat(cli): report opt-in command timings (#7042)
* feat(cli): report opt-in command timings

* fix(cli): time standalone MCP doctor

* fix(cli): retain timing on doctor errors

* fix(cli): preserve formatted version output with command timings

---------

Co-authored-by: dhruvac29 <[email protected]>
Co-authored-by: gensokyo-dev <[email protected]>
2026-10-28 11:53:29 +08:00
gensokyo-dev 3d5c73f35c fix(cli): retain JSONL parser diagnostics under token counting (#7705) 2026-10-28 11:48:31 +08:00
gensokyo-dev e556134302 fix(deps): patch fast-uri, qs and HTTP/3 certifi (#7704)
Refresh compatible security fixes and verified dependency notices. Keep
historical HTTP/3 guest verification separate from the refreshed trust store.

Fixes #7700

Assisted-by: Aya (gensokyo-skills:aya-source-investigation)
2026-10-28 03:34:36 +00:00
aa6f6436c9 feat(cli): add CSV and TSV analysis views (#7254)
* feat(cli): add CSV and TSV analysis views

Co-authored-by: Copilot <[email protected]>

* Update tests/boundary/cli/filteredArrayOutput.test.ts

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* Update src/cli.ts

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* fix(cli): address delimited export review feedback

* fix(cli): format delimited output block in cliResultOutput

* fix(cli): retain delimited view uncertainty and align completions

---------

Co-authored-by: Copilot <[email protected]>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: gensokyo-dev <[email protected]>
2026-10-28 11:27:40 +08:00
40b9c03f99 feat(environment): serve query numbers, counted values and made objects (#7332)
* feat(environment): serve the writes callers make without recorded form fields

- A create member a recording sent empty ("", null, false, [] or {}) is no
  longer required; a call that leaves it out is applied as sending that value
  (body.blank).
- List views render envelope members that list the listed entities' ids
  (Paperless's `all`), so creates in such collections are confirmed.
- Updates move their entity in collections kept sorted by a field they write,
  and a failed whole-collection read after an update can teach that order.
- A later create that stores an unruled field otherwise is withheld at the
  create, and an item read may fill values a create's answer had not loaded.
- An unsent field respelled between null, [] and {} is unchanged.
- The skill asks for one create with every field and one with only what a
  person must give.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(skill): show how to read signed-in pages before recording

Agents guessed capture keys (artifacts, dom, final) to see pages behind
sign-in, which inspect_web_page cannot reach.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): accept every empty spelling recorded creates sent

A create that fills only what a person must give sends `null` where the
full create sent a string, and `null` or `false` for the same flag. The
merged plan now accepts each recorded empty value, and a call that leaves
the member out is applied as the earliest call sent it.

Also adds the spec folder for this change.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(skill): keep the vary-fields step neutral on UI or API writes

Agents read "the form" as a cue to write through the UI, where some
applications' request bodies are not exposed; a body-unusable UI write
now re-records once with request steps.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(environment): record verification for blank members and sorted writes

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): address review of blank members and sorted writes

- Bulk updates move their entities in turn against the others' earlier
  values and install the final order once, as replay does.
- A member's recorded empty values stay accepted once a later call teaches
  its default; a left-out member with a default keeps the default.
- Write answers spell empty lists and maps as their recorded answers did.
- A rename can teach a sort when the creates chose one by a field the
  update did not write, such as ids that ascend with titles.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* feat(environment): serve the writes that later reads explain

- An item read whose object is marked deleted (deleted, isDeleted or
  is_deleted true) agrees with an absent entity, as EspoCRM answers a
  deleted record; such an object never seeds or adopts an entity.
- maxSize, max_results and maxResults are page sizes, for whole-collection
  list lines and the list query's limit role.
- An item view also shows the fields only reads after its type's first write
  show, when no initial entity holds them (Vikunja's task subscription),
  without requiring them; write answers leave such a field out for entities
  without it, and it no longer withholds the write as incomplete-entities.
- The skill asks updates to change the fields a person would change, and
  expects a deleted object to read back as 404 or marked deleted.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): answer sent empty members as sent, and keep a placed create's order

- A write answer spells an empty list or map the call did not send as its
  recorded answer did; one the call sent is answered as sent, so a create
  sending `[]` is not answered `null` because the first recorded create was.
- A read listing the whole collection that placed a create leaves the create
  only the sorted inserts whose order that read follows, so a later rename
  cannot teach a sort an earlier read ruled out.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): answer bulk updates without fields only later reads show

A bulk update's answer renders each entity with the same allowance a single
update's answer has: a field only reads after the type's first write showed,
which no initial entity holds, is left out for an entity without it, so a
bulk update of a seeded task is no longer refused for lacking it.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(environment): correct how the EspoCRM contact update task passes

The agent trajectories show gpt-oss passes it by creating a replacement contact
and deleting the original, since the served update refuses emailAddress; the
update itself is refused in both builds.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): answer a read of a served-deleted entity marked deleted

An application that soft-deletes still answers a deleted record by id, marked
deleted. Serving now keeps an entity a served delete removed from a type whose
recorded item reads show that, until a reset or load and within the size
data/add loads, and answers a read of it with its last fields and the flag
true, instead of refusing the read.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): keep unloaded create answers, check bulk answers with sent members, narrow inserts only on collection-order reads

- A create whose item read loaded values its answer had not records them
  (`observation.read.unloaded`), and a served create's answer keeps them as
  the recorded answer showed them, a zero-time owner included, while the state
  holds the read's.
- Replay checks a bulk update's recorded answers with each target's sent
  members, as serving answers them, so echoed `null` and `[]` no longer make
  the plan body-unusable.
- A whole-collection read whose query sorts it shows the query's order, so it
  no longer narrows a create's sorted insert candidates.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(environment): record the census with main's unknown-clock rule merged

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* feat(environment): serve query numbers, counted values and made objects

- A query parameter accepts a number or boolean, which serving sends as its
  JSON text, as agents send page sizes and flags.
- A sequence may start at the value its first create took above every value
  its type held (Vikunja's per-project task index), and a later create's
  composed, template or started-sequence rule is adopted when it also spells
  the first create's value.
- A made-object rule explains an object the application makes with each
  created entity: members the request stamped, members holding the entity's
  own id, and counts one past the held objects' values.
- A create's answer names the id of the one object a one-element array member
  holds, so bulk creates link to their type.
- The skill reads each listed object by id before writing.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* feat(environment): let a linked create take path values it stores

A create linked to its type's own collection, whose path has no parameters,
accepts any path values when its plan stores each one in a field, as a Vikunja
task stores its project; replay and serving apply it alike.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): learn a sort from a rename only when moving its entities reaches it

Serving moves only the entities a write changes in a collection kept sorted,
so replay now tries a sort after a rename only when the entities no write the
read exercised changed already follow it. Seeded folders listed out of title
order no longer teach a title sort that serving would answer with another
order.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): keep unloaded create answers a sent value did not echo, and learn no sort from query-sorted reads

- A served create keeps a value its recorded answer had not loaded unless the
  call sent that member and the plan stores it as sent, so a create sending
  `active: false` is answered `false` as recorded while the state holds the
  read's `true`.
- A rename no longer teaches a sort from a whole-collection read its query
  sorts, whose ties need not follow the collection's own order.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(environment): verify query values, counted values and made objects

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): learn the soft-delete flag in the state view and serve by it

Extraction now records the member later item reads set true on a record the
application soft-deleted, and the item view carries it as `deleted_flag`.
Serving reads that fact instead of scanning recorded bodies itself, so one
owner decides what a soft delete looks like.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(environment): record the call-values checks at #7274 93821024

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): learn the soft-delete flag from item reads with a query too

A client may read a deleted record back only with a query, such as a
cache-busting parameter. Every successful read of the item tool now shows the
flag; only plain later reads still add fields.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(environment): record the call-values checks at #7274 45832406

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): learn sorts only from orders a list read confirmed

- A sort retry after a failed whole-collection read requires the entities no
  write changed since a list last showed them to follow it already, rather than
  excusing every entity the update's tool ever wrote: an earlier rename a list
  confirmed in place stays where it was.
- An update takes its creates' sort only from a create a list read showed
  placed; replay still tries a sort a create guessed from the initial order.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): drop entities read back as deleted and answer soft deletes as deleted

- A held entity a later read shows marked deleted is dropped, not adopted with
  the marked object's fields.
- Bulk-update replay renders answers with the optional and later-only fields
  serving uses, so a bulk update of created and seeded entities replays.
- A served delete of a soft-deleting type answers with the entity marked
  deleted, single and bulk.
- A deleted flag only query reads showed is a field of the item view, so a
  served tombstone keeps it where live records omit it.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): check made objects as recorded and keep their ids and clocks honest

- A made object's clock counts as a write clock, so a capture with no clock
  anchor withholds the create instead of stamping 1970.
- Members holding the entity's identity follow the id written, generated or
  sent, through the type's id field.
- A later create's made object is checked as recorded, before its own members
  are rewritten, so one naming another entity withholds the plan.
- A later create's template is adopted only when it spells that create's value
  too, not just when the first create's text holds its source value.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): move updated entities only by placed sorts and keep query-sorted reads from confirming order

Replay moved an updated entity by a sort a create only guessed, while the
update plan exported only placed sorts, so a read could confirm an update
by a move serving would not make. Both now use the placed sort. A passing
list read clears its entities from the moved set only when the state
decides the order of every entity it lists, so a read whose query sorts
it no longer hides an earlier move from a later sort check.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(environment): count six withheld plans that explain more, not seven

The bulk task create counted among them is served on this branch.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): try a learned sort by moving each pending write in turn, as serving does

#retrySort sorted the whole collection, which a list could match while
serving, moving each renamed entity in turn against the others' values
before their own writes, gave another order. The state now records the
values each write replaced, and the retry replays the writes since a
list in collection order last showed their entities.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): answer a served delete with the deleted flag its recorded answer showed

An application may answer a delete with the record as it was and mark it
deleted only on later reads. Delete answers, single and bulk, now take
the flag's value from the recorded answer; reads of a served-deleted
entity still show it marked deleted.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* chore(environment): set aside two hunks next to lines main's saved-values change rewrites

Main's #7267 changes the lines beside the blank-member docs and the
empty-spelling check in explainUpdate. Setting them aside lets that merge
apply cleanly; the commit after it puts them back on top of main's text.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* chore(environment): restore the set-aside hunks on main and bump the skill to 51

Puts the blank-member docs and the empty-spelling check back beside main's
slug and composed text and heldSlugRule. Main is at skill 50 with
different text, so this branch's skill changes take 51.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* chore(environment): bump the skill to 52 above #7260's 51

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* chore(environment): put back the call-values hunks main's merge took its side for, and bump the skill to 53

The merge of main kept main's side where it overlapped this branch, so it
could commit without a conflict. This restores this branch's side next to
main's: made-object agreement beside the & slug spellings, sequence starts
in the earlier evidence (now built by earlierOf), the combined value
imports, and recording step 2 beside main's UI-first step 3. Main is at
skill 50 and #7274 at 52, so this branch takes 53.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): keep sorts a list showed in place ruled out, and created answers' nested members as recorded

A whole list in collection order that showed a renamed entity where a
sort by the renamed field would have moved it now rules that sort out, so
a later rename of the same entity cannot teach it. A create's answer
leaves out members its item read added inside an object the answer
showed, as the recorded answer did; the state still stores them.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): keep a later create's made object only with clocks its request stamped

followsMadeObjects only checked that a made object's clocks parsed, so a
later create whose object showed an older clock still passed, while serving
stamps the write's clock. Its clocks must now be in the rule's format and
within the stamp window of that create's request.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): compare soft-deleted read fields

* fix(environment): reject contradictory learned sequences

* fix(environment): preserve initially deleted item observations

* fix(environment): index stored deleted entities separately

* fix(environment): replay lists that include deleted records

* fix(environment): make no count of a made-object member earlier creates held alike

A count only a later create shows starts at the exemplar's value, which is
right only while the exemplar is the one create accepted: creates accepted
after it showed the member unchanged, which a count would have moved. The
merge now refuses such a count, as the top-level sequence check does.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: morluto <[email protected]>
2026-10-28 07:51:03 +08:00
PM baec375c34 fix(cli): validate outline-depth before numeric coercion (#7505)
Fixes morluto/rea#7425

Signed-off-by: pqminh27 <[email protected]>
2026-10-28 05:16:49 +08:00
PM 423d190026 fix(cli): preserve Markdown table edge spaces (#7506)
Fixes morluto/rea#7432

Signed-off-by: pqminh27 <[email protected]>
2026-10-28 05:16:28 +08:00
Beqa 6efb384261 docs(uninstall): clarify malformed shared config retention (#7507)
Uninstall retains unverifiable shared registration storage without blocking
REA-owned purge; document that exception beside the stop-before-removal rule.

Fixes #7320
2026-10-28 05:16:13 +08:00
5fcf0d2dcc fix(browser): save from gzip, deflate and cached responses within a bound (#7361)
A saved response's decoded size is bounded by its received size, or the
declared length of one served from cache, times 1032 for each gzip or deflate
coding, the most DEFLATE expands. Grafana answers folder creates gzipped, so a
UI step saving a new folder's uid was refused. Other codings and unknown or
zero sizes stay refused before the body is read.

Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: morluto <[email protected]>
2026-10-28 05:08:27 +08:00
7bc6c5bf61 feat(environment): serve the writes callers make without recorded form fields (#7260)
* feat(environment): serve the writes callers make without recorded form fields

- A create member a recording sent empty ("", null, false, [] or {}) is no
  longer required; a call that leaves it out is applied as sending that value
  (body.blank).
- List views render envelope members that list the listed entities' ids
  (Paperless's `all`), so creates in such collections are confirmed.
- Updates move their entity in collections kept sorted by a field they write,
  and a failed whole-collection read after an update can teach that order.
- A later create that stores an unruled field otherwise is withheld at the
  create, and an item read may fill values a create's answer had not loaded.
- An unsent field respelled between null, [] and {} is unchanged.
- The skill asks for one create with every field and one with only what a
  person must give.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(skill): show how to read signed-in pages before recording

Agents guessed capture keys (artifacts, dom, final) to see pages behind
sign-in, which inspect_web_page cannot reach.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): accept every empty spelling recorded creates sent

A create that fills only what a person must give sends `null` where the
full create sent a string, and `null` or `false` for the same flag. The
merged plan now accepts each recorded empty value, and a call that leaves
the member out is applied as the earliest call sent it.

Also adds the spec folder for this change.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(skill): keep the vary-fields step neutral on UI or API writes

Agents read "the form" as a cue to write through the UI, where some
applications' request bodies are not exposed; a body-unusable UI write
now re-records once with request steps.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(environment): record verification for blank members and sorted writes

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): address review of blank members and sorted writes

- Bulk updates move their entities in turn against the others' earlier
  values and install the final order once, as replay does.
- A member's recorded empty values stay accepted once a later call teaches
  its default; a left-out member with a default keeps the default.
- Write answers spell empty lists and maps as their recorded answers did.
- A rename can teach a sort when the creates chose one by a field the
  update did not write, such as ids that ascend with titles.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): answer sent empty members as sent, and keep a placed create's order

- A write answer spells an empty list or map the call did not send as its
  recorded answer did; one the call sent is answered as sent, so a create
  sending `[]` is not answered `null` because the first recorded create was.
- A read listing the whole collection that placed a create leaves the create
  only the sorted inserts whose order that read follows, so a later rename
  cannot teach a sort an earlier read ruled out.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): keep unloaded create answers, check bulk answers with sent members, narrow inserts only on collection-order reads

- A create whose item read loaded values its answer had not records them
  (`observation.read.unloaded`), and a served create's answer keeps them as
  the recorded answer showed them, a zero-time owner included, while the state
  holds the read's.
- Replay checks a bulk update's recorded answers with each target's sent
  members, as serving answers them, so echoed `null` and `[]` no longer make
  the plan body-unusable.
- A whole-collection read whose query sorts it shows the query's order, so it
  no longer narrows a create's sorted insert candidates.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): learn a sort from a rename only when moving its entities reaches it

Serving moves only the entities a write changes in a collection kept sorted,
so replay now tries a sort after a rename only when the entities no write the
read exercised changed already follow it. Seeded folders listed out of title
order no longer teach a title sort that serving would answer with another
order.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): keep unloaded create answers a sent value did not echo, and learn no sort from query-sorted reads

- A served create keeps a value its recorded answer had not loaded unless the
  call sent that member and the plan stores it as sent, so a create sending
  `active: false` is answered `false` as recorded while the state holds the
  read's `true`.
- A rename no longer teaches a sort from a whole-collection read its query
  sorts, whose ties need not follow the collection's own order.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): learn sorts only from orders a list read confirmed

- A sort retry after a failed whole-collection read requires the entities no
  write changed since a list last showed them to follow it already, rather than
  excusing every entity the update's tool ever wrote: an earlier rename a list
  confirmed in place stays where it was.
- An update takes its creates' sort only from a create a list read showed
  placed; replay still tries a sort a create guessed from the initial order.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): move updated entities only by placed sorts and keep query-sorted reads from confirming order

Replay moved an updated entity by a sort a create only guessed, while the
update plan exported only placed sorts, so a read could confirm an update
by a move serving would not make. Both now use the placed sort. A passing
list read clears its entities from the moved set only when the state
decides the order of every entity it lists, so a read whose query sorts
it no longer hides an earlier move from a later sort check.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): try a learned sort by moving each pending write in turn, as serving does

#retrySort sorted the whole collection, which a list could match while
serving, moving each renamed entity in turn against the others' values
before their own writes, gave another order. The state now records the
values each write replaced, and the retry replays the writes since a
list in collection order last showed their entities.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* chore(environment): set aside two hunks next to lines main's saved-values change rewrites

Main's #7267 changes the lines beside the blank-member docs and the
empty-spelling check in explainUpdate. Setting them aside lets that merge
apply cleanly; the commit after it puts them back on top of main's text.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* chore(environment): restore the set-aside hunks on main and bump the skill to 51

Puts the blank-member docs and the empty-spelling check back beside main's
slug and composed text and heldSlugRule. Main is at skill 50 with
different text, so this branch's skill changes take 51.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): keep sorts a list showed in place ruled out, and created answers' nested members as recorded

A whole list in collection order that showed a renamed entity where a
sort by the renamed field would have moved it now rules that sort out, so
a later rename of the same entity cannot teach it. A create's answer
leaves out members its item read added inside an object the answer
showed, as the recorded answer did; the state still stores them.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: morluto <[email protected]>
2026-10-28 05:07:28 +08:00
aa210b7ccc feat(environment): serve the writes that later reads explain (#7274)
* feat(environment): serve the writes callers make without recorded form fields

- A create member a recording sent empty ("", null, false, [] or {}) is no
  longer required; a call that leaves it out is applied as sending that value
  (body.blank).
- List views render envelope members that list the listed entities' ids
  (Paperless's `all`), so creates in such collections are confirmed.
- Updates move their entity in collections kept sorted by a field they write,
  and a failed whole-collection read after an update can teach that order.
- A later create that stores an unruled field otherwise is withheld at the
  create, and an item read may fill values a create's answer had not loaded.
- An unsent field respelled between null, [] and {} is unchanged.
- The skill asks for one create with every field and one with only what a
  person must give.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(skill): show how to read signed-in pages before recording

Agents guessed capture keys (artifacts, dom, final) to see pages behind
sign-in, which inspect_web_page cannot reach.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): accept every empty spelling recorded creates sent

A create that fills only what a person must give sends `null` where the
full create sent a string, and `null` or `false` for the same flag. The
merged plan now accepts each recorded empty value, and a call that leaves
the member out is applied as the earliest call sent it.

Also adds the spec folder for this change.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(skill): keep the vary-fields step neutral on UI or API writes

Agents read "the form" as a cue to write through the UI, where some
applications' request bodies are not exposed; a body-unusable UI write
now re-records once with request steps.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(environment): record verification for blank members and sorted writes

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): address review of blank members and sorted writes

- Bulk updates move their entities in turn against the others' earlier
  values and install the final order once, as replay does.
- A member's recorded empty values stay accepted once a later call teaches
  its default; a left-out member with a default keeps the default.
- Write answers spell empty lists and maps as their recorded answers did.
- A rename can teach a sort when the creates chose one by a field the
  update did not write, such as ids that ascend with titles.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* feat(environment): serve the writes that later reads explain

- An item read whose object is marked deleted (deleted, isDeleted or
  is_deleted true) agrees with an absent entity, as EspoCRM answers a
  deleted record; such an object never seeds or adopts an entity.
- maxSize, max_results and maxResults are page sizes, for whole-collection
  list lines and the list query's limit role.
- An item view also shows the fields only reads after its type's first write
  show, when no initial entity holds them (Vikunja's task subscription),
  without requiring them; write answers leave such a field out for entities
  without it, and it no longer withholds the write as incomplete-entities.
- The skill asks updates to change the fields a person would change, and
  expects a deleted object to read back as 404 or marked deleted.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): answer sent empty members as sent, and keep a placed create's order

- A write answer spells an empty list or map the call did not send as its
  recorded answer did; one the call sent is answered as sent, so a create
  sending `[]` is not answered `null` because the first recorded create was.
- A read listing the whole collection that placed a create leaves the create
  only the sorted inserts whose order that read follows, so a later rename
  cannot teach a sort an earlier read ruled out.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): answer bulk updates without fields only later reads show

A bulk update's answer renders each entity with the same allowance a single
update's answer has: a field only reads after the type's first write showed,
which no initial entity holds, is left out for an entity without it, so a
bulk update of a seeded task is no longer refused for lacking it.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(environment): correct how the EspoCRM contact update task passes

The agent trajectories show gpt-oss passes it by creating a replacement contact
and deleting the original, since the served update refuses emailAddress; the
update itself is refused in both builds.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): answer a read of a served-deleted entity marked deleted

An application that soft-deletes still answers a deleted record by id, marked
deleted. Serving now keeps an entity a served delete removed from a type whose
recorded item reads show that, until a reset or load and within the size
data/add loads, and answers a read of it with its last fields and the flag
true, instead of refusing the read.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): keep unloaded create answers, check bulk answers with sent members, narrow inserts only on collection-order reads

- A create whose item read loaded values its answer had not records them
  (`observation.read.unloaded`), and a served create's answer keeps them as
  the recorded answer showed them, a zero-time owner included, while the state
  holds the read's.
- Replay checks a bulk update's recorded answers with each target's sent
  members, as serving answers them, so echoed `null` and `[]` no longer make
  the plan body-unusable.
- A whole-collection read whose query sorts it shows the query's order, so it
  no longer narrows a create's sorted insert candidates.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(environment): record the census with main's unknown-clock rule merged

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): learn a sort from a rename only when moving its entities reaches it

Serving moves only the entities a write changes in a collection kept sorted,
so replay now tries a sort after a rename only when the entities no write the
read exercised changed already follow it. Seeded folders listed out of title
order no longer teach a title sort that serving would answer with another
order.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): keep unloaded create answers a sent value did not echo, and learn no sort from query-sorted reads

- A served create keeps a value its recorded answer had not loaded unless the
  call sent that member and the plan stores it as sent, so a create sending
  `active: false` is answered `false` as recorded while the state holds the
  read's `true`.
- A rename no longer teaches a sort from a whole-collection read its query
  sorts, whose ties need not follow the collection's own order.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): learn the soft-delete flag in the state view and serve by it

Extraction now records the member later item reads set true on a record the
application soft-deleted, and the item view carries it as `deleted_flag`.
Serving reads that fact instead of scanning recorded bodies itself, so one
owner decides what a soft delete looks like.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): learn the soft-delete flag from item reads with a query too

A client may read a deleted record back only with a query, such as a
cache-busting parameter. Every successful read of the item tool now shows the
flag; only plain later reads still add fields.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): learn sorts only from orders a list read confirmed

- A sort retry after a failed whole-collection read requires the entities no
  write changed since a list last showed them to follow it already, rather than
  excusing every entity the update's tool ever wrote: an earlier rename a list
  confirmed in place stays where it was.
- An update takes its creates' sort only from a create a list read showed
  placed; replay still tries a sort a create guessed from the initial order.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): drop entities read back as deleted and answer soft deletes as deleted

- A held entity a later read shows marked deleted is dropped, not adopted with
  the marked object's fields.
- Bulk-update replay renders answers with the optional and later-only fields
  serving uses, so a bulk update of created and seeded entities replays.
- A served delete of a soft-deleting type answers with the entity marked
  deleted, single and bulk.
- A deleted flag only query reads showed is a field of the item view, so a
  served tombstone keeps it where live records omit it.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): move updated entities only by placed sorts and keep query-sorted reads from confirming order

Replay moved an updated entity by a sort a create only guessed, while the
update plan exported only placed sorts, so a read could confirm an update
by a move serving would not make. Both now use the placed sort. A passing
list read clears its entities from the moved set only when the state
decides the order of every entity it lists, so a read whose query sorts
it no longer hides an earlier move from a later sort check.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): try a learned sort by moving each pending write in turn, as serving does

#retrySort sorted the whole collection, which a list could match while
serving, moving each renamed entity in turn against the others' values
before their own writes, gave another order. The state now records the
values each write replaced, and the retry replays the writes since a
list in collection order last showed their entities.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): answer a served delete with the deleted flag its recorded answer showed

An application may answer a delete with the record as it was and mark it
deleted only on later reads. Delete answers, single and bulk, now take
the flag's value from the recorded answer; reads of a served-deleted
entity still show it marked deleted.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* chore(environment): set aside two hunks next to lines main's saved-values change rewrites

Main's #7267 changes the lines beside the blank-member docs and the
empty-spelling check in explainUpdate. Setting them aside lets that merge
apply cleanly; the commit after it puts them back on top of main's text.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* chore(environment): restore the set-aside hunks on main and bump the skill to 51

Puts the blank-member docs and the empty-spelling check back beside main's
slug and composed text and heldSlugRule. Main is at skill 50 with
different text, so this branch's skill changes take 51.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* chore(environment): bump the skill to 52 above #7260's 51

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): keep sorts a list showed in place ruled out, and created answers' nested members as recorded

A whole list in collection order that showed a renamed entity where a
sort by the renamed field would have moved it now rules that sort out, so
a later rename of the same entity cannot teach it. A create's answer
leaves out members its item read added inside an object the answer
showed, as the recorded answer did; the state still stores them.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): compare soft-deleted read fields

* fix(environment): preserve initially deleted item observations

* fix(environment): index stored deleted entities separately

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: morluto <[email protected]>
2026-10-28 05:05:21 +08:00
Wenyu Chiou ef11f77836 fix(evidence): Reject non-file destinations before overwrite advice (#7495)
Check destination kind before overwrite policy and cover directory recovery,
regular-file controls, symlink modes and preservation.

Fixes #7444.

Independent review: APPROVE for patch SHA-256
3d65eb23d01da9b6df786ad0db337dcff79d85923f23678a5168babb88c16080,
relayed by the coordinating thread (reviewer identity not supplied in the
delegated packet). Triggers: file I/O, error handling, three shipping files.

Validation: baseline/candidate runtime builds and actual synthetic CLI probes;
initial focused run 70 passed, 2 Windows EPERM fixture failures, 3 skipped.
Final changed filesystem file 42 passed, 5 skipped; symlink semantics and
full aggregate checks remain unverified. Targeted format/lint/discovery passed.
2026-10-28 03:41:26 +08:00
0cd7ef456b feat(plugins): make Claude Code and Codex installs self-contained (#7514)
Ship Claude Code and Codex from one release-pinned npm artifact with bundled runtime dependencies, extraction-only MCP qualification, plugin-aware recovery, and synchronized release metadata.

Integrates #2815 and the original proposal #1977. The original commits remain available on the PR branch; retain their author and assistant credit in this squash.

Validation: required CI passed, real Codex/Claude loading verified, and reviewed packaging/recovery findings addressed.

Co-authored-by: GamerDubz <[email protected]>
Co-authored-by: morluto <[email protected]>
Co-authored-by: OpenAI Codex <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-27 17:13:17 +00:00
gensokyo-dev 269630f4e1 docs: archive historical verification and release inventories (#7519)
Retire redundant historical JSON copies from the active documentation tree after verifying identical published release snapshots and an immutable route-report archive. Preserve current release data, original evidence, release publication logic and historical access.

Validation: 48 release tests; documentation conformance and rendered archive link/digest; Git blob/SHA-256 checks; selected current-head CI passed. Greptile: 5/5 with no actionable findings. Codex GitHub review unavailable due quota.

Closes #7517.
2026-10-28 00:54:54 +08:00
gensokyo-dev 65f1c44734 refactor: retire superseded test-only runtime entrypoints (#7515)
Migrate MCP session tests to the named lifecycle output schema and preserve raw reply envelopes. Move R8 type/alias checks to the production field resolver and recovery guidance checks to their scoped factory; remove superseded test-only exports and an unused import.

Validation: 99 focused tests, an exact-old-schema negative control, full selected CI including all 8 shards and source tests, and Greptile source review passed. Fresh-head real stdio MCP analyzed [email protected] and retained a complete authenticated Evidence bundle after close; compact/full catalogs are unchanged. Full local typed lint hit the 3 GiB memory guard; scoped typed lint passed. Codex review quota exhausted.

Fixes #7513.
2026-10-28 00:22:43 +08:00
gensokyo-dev 6eb37a7749 fix(ci): restore qualification and reduce validation overhead
Use one typed-checker worker by default without changing semantic scope or caller overrides. Reuse the bounded CLI worker for pure argument validation while retaining fresh-process coverage and all upstream cases.

Keep registration tests independent of real package-runner downloads while asserting the actual prewarm command and startup context. Pass the advertised binary_session definition by name to package verification, retaining default SDK validation and complete catalog/contract checks while avoiding compilation of unrelated tool outputs. Preserve all four installed-tarball scenarios and existing deadlines.

Strengthen Hermes preview operation/backup assertions and clarify physical-path routing documentation.

Assisted-by: Suika (gensokyo-skills:suika-scatter-gather-planning)
2026-10-28 00:20:07 +08:00
S1MonandS1Mon bcc6775dc1 fix(cli): reject non-decimal Android overload index tokens (#7493)
Android overload selection declared a plain number, so the CLI argument
parser coerced the raw token with Number before the schema ran. An empty
token therefore arrived as 0, and hexadecimal, exponent, signed, and
float spellings arrived as their numeric value, leaving both commands to
proceed to artifact admission on a selector the caller never wrote as a
decimal integer.

Reuse the existing decimal token parser that the other integer CLI
options already use. Omitted and explicit decimal zero keep their current
meanings, and the published schema for overloadIndex is unchanged.

Validation: npm run compile, the cli-boundary vitest project for
tests/boundary/cli/cliIntegerArguments.test.ts (56 passed), npm run
typecheck, npm run lint, npm run lint:typed, npm run format:check, npm
run metadata:check, npm run verify:architecture-guards, and npm run
knip:production. The new cases fail against the base source and pass with
the change.

Fixes #7430.

Co-authored-by: S1Mon <[email protected]>
2026-10-27 23:25:32 +08:00
Pirro Zani 313af7119c fix(website): keep phone navigation labels from splitting mid-word (#7497)
The phone header gave each link an equal minmax(0, 1fr) column and
allowed overflow-wrap: anywhere, so single-word labels such as
"Showcases", "Anleitungen", "Руководства" and "Посібники" broke between
letters at 320-430px. Links now share the row as flex items that never
shrink below their longest word. Labels wrap only at spaces, and a word
wider than the whole row can still break, preserving the enlarged-text
fit from #3598.

Refs #3597
2026-10-27 23:25:04 +08:00
gensokyo-dev a95b83c2b0 perf(javascript): cache callable result syntax by AST identity (#7508)
Cache direct return/yield source expressions by immutable AST identity for all existing static and semantic consumers. Weak keys bound retention to AST lifetime; mutable analysis facts and Evidence remain unchanged. Refs #6311; full CastleIO analysis still times out.

Validation: 225 focused tests; baseline cache-regression controls; build, serial check:fast and test discovery passed. CastleIO direct-syntax facts equal with 78,272 warm lookups reduced from 2.72-2.81s to 13.0-13.4ms. Complete sourcemap-codec CLI analyses retained byte-identical Evidence; no end-to-end speedup or token reduction claimed.

CI: source 20,888 passed / 15 skipped; seven built-dependent shards, static, docs, package builds, Linux native baseline and real JS recovery CLI/MCP passed. Shard 6 Hermes path-preview failures both reproduce on exact main b9d1a61dd; Portable tests fails downstream. Greptile 5/5, no findings; Codex quota exhausted. Authorized admin bypass for unrelated baseline CI and unchecked human readiness; no human review attestation made.
2026-10-27 22:53:42 +08:00
morluto 7f90549916 chore(android): remove unused type exports 2026-10-27 22:52:33 +08:00
morluto b3fd0aa3ee test(setup): align Hermes symlink preview expectations 2026-10-27 22:52:33 +08:00
Beqa b9d1a61dd5 docs(ghidra): clarify Objective-C Message Analyzer libobjc log (#7249)
Clarify the Objective-C analyzer message, coverage limitation, structured failure diagnostics, and existing opt-out/startup resource controls.

Verification: exact documentation change applied on main 79d2fc054; docs:check passed. Installed Ghidra 12.2 analyzer/scheduler and REA error/launch owners inspected. No real macOS target reproduction; original failure remains unknown. PR CI and Greptile passed; Codex review unavailable.
2026-10-27 22:28:29 +08:00
Adolanium 79d2fc0540 fix(doctor): classify an unreadable skill identity as unknown (#7090)
rea doctor reported a skill copy with an unreadable or unresolved path as identity state unknown but classified its skill:identity check as config_drift, which claims the copy has drifted. docs/installation.md keeps an unreadable skill identity unknown, and the sibling registration checks already classify unknown state as unknown. The skill check and the DoctorScope fallback now do the same.
2026-10-27 21:34:05 +08:00
Saiyam Jain 097cd2c988 fix(browser): report non-absolute export paths at their input field (#7097) 2026-10-27 21:33:49 +08:00
84f62f76e5 fix(hermes): preserve external YAML anchor consumers (#5152)
* fix(hermes): preserve consumers of anchored registrations

Keep original alias values and comments before setup or uninstall replaces an anchored REA node. Preserve original nested bindings and retained sharing through the common YAML writer.

Fixes #3325

* test(hermes): assert retained alias object identity

* fix(hermes): preserve removed keys and surviving alias owners

* test(cli): restore retained-view cancellation phase coverage

* fix(setup): retain copied Hermes anchor comments

* chore(hermes): reverse incidental merge hook changes

Reverse the merge relative to its included-main parent to restore exact upstream bytes. Reapply only the independently reviewed shared-owner and regression patch in a normal follow-up commit. Keep all ordinary history and normal hooks; no remote push of the rejected hook drift.

Refs #3325

* fix(hermes): retain reviewed shared-owner integration

* chore(hermes): reverse incidental merge hook changes

Reverse the merge relative to its included-main parent to restore exact upstream bytes. Reapply only the independently reviewed shared-owner and regression patch in a normal follow-up commit. Keep all ordinary history and normal hooks; no remote push of the rejected hook drift.

Refs #3325

* fix(hermes): retain exact reviewed shared-owner regression

* test(native): align plist command fixtures with main

Adopt the two maintained-main fixture files as of
dd198d037f. Distinguish exact XML lint
validation from conversion commands and retain truthful provenance,
source-value, format, limitations and unknown-scalar assertions.

The original seven provenance failures reproduce before this repair.
The affected three-file group passes all 86 checks afterward. Product
code and installed-package bytes remain unchanged.

Co-authored-by: morluto <[email protected]>

* fix(native): retain bound-target dispatch failure contracts

Adopt morluto's canonical Apple dispatch metadata owner and bounded MCP
and cleanup controls from 41e10b240c.
Keep fresh admission separate from already-bound target changes and
retain access-denied classification, exact physical causes and cleanup
ownership. Update the two stale unit expectations exposed by Source CI.

The original unit pair reproduces before the correction. Current focused
regressions, package verification and installed feature controls pass.
Only the canonical Apple owner and related controls are ported; this does
not integrate unrelated later-main changes or create another contribution.

Co-authored-by: morluto <[email protected]>

* test: align plist provenance and package listing fixtures

Keep the exact XML lint invocation in CLI provenance and include the
required CLI environment owner in the synthetic package member list.
Preserve binary provenance and all LF, CRLF, literal-CR and bytecode checks.

* revert(hermes): restore canonical main before reviewed replay

This reverts commit 575a10d194, reversing
changes made to bb17e90f27.

* test(cli): retain full cancellation oracle with prepared projection

---------

Co-authored-by: hhang <[email protected]>
Co-authored-by: gensokyo-dev <[email protected]>
Co-authored-by: morluto <[email protected]>
2026-10-27 21:33:17 +08:00
Afloat 095a51cf38 fix(browser): reject invalid UTF-8 in HTTP CDP discovery (#4524) 2026-10-27 21:33:01 +08:00
Jeff d47efe1a54 fix(native): reject mixed dyld refusal diagnostics (#5778)
* fix(native): classify confirmed dSYM dyld refusals

* style(native): format target refusal handling

* fix(native): confirm precise Mach-O layout refusals before export fallback

* test(cli): observe cancellation at stable source resolution boundary

* fix(native): reject contradictory dyld layout refusals

* fix(native): preserve mixed dyld diagnostic failures

* fix(native): retain the evidence behind contradictory layout refusals

When the imports and exports refusals disagree, or a refusal disagrees with
the parsed Mach-O header, inspect_macho returned a generic output error that
kept only one dyld_info capture. It dropped the header file type and the other
refusal, so the failure could not show why the admitted diagnostic was
rejected.

The contradiction now has its own error. Its diagnostics keep the parsed file
type and every admitted refusal (mode, admitted layout, exit code and output),
and the captured output is still the conflicting command. The test checks the
reason and the retained diagnostics for each contradiction case.

* test(native): allow time for real Mach-O CLI and MCP controls
2026-10-27 21:32:13 +08:00
Ahmed cbeeeda97b fix(javascript): resolve preceding exported variable initializers and computed method keys (#5387)
fix(javascript): resolve variable initializers before computed method keys
2026-10-27 21:31:59 +08:00
Chip Li b4118b9683 docs(hermes): explain nested-home routing and preview limits (#4145)
* docs(hermes): clarify sticky profiles for nested homes

* docs(hermes): qualify nested homes beneath default symlinks

* docs(hermes): cover canonicalized ancestor paths

* docs(hermes): clarify both missing-path containment errors
2026-10-27 21:31:40 +08:00
morluto ec3f84b626 test(ci): avoid pinning skip warning wording 2026-10-27 21:26:38 +08:00
1201c25542 fix(ci): align static checks and regression fixtures with current contracts (#7349)
* fix(ci): retire unused internal analysis declarations

* test: use an admitted app bundle for provider routing

(cherry picked from commit 00aa17f12a)

* test: keep bundle routing coverage outside oversized group

(cherry picked from commit 8658353b0d)

* fix(ci): align regression fixtures with current admission contracts

* test: bind extraction close fixtures to their ZIP digest

(cherry picked from commit 626d0250db)

* test(cli): match typed analysis outcomes in function detail fixture

* test(ghidra): preserve empty Java property separator framing

* test(electron): retain unknown IPC ownership for native module gaps

* fix(electron): share source-scoped call path identity

* test(captures): assert safe source identity on SAZ refusal

* test(apple): match root occurrence kind to inventory manifest

* fix(examples): align directory root occurrence identity

* test(electron): align equivalent fixture assertions with upstream

* test(process): assert retained capture root identity

* test(windows): declare the PE fixture optional header

* test(ci): refresh owned registration and verifier fixtures

* test(setup): use owned registrations for refresh fixtures

* test(process): control unavailable ownership in CLI cleanup fixture

* test(process): guard controlled compiler output arguments

* test(setup): align equivalent entrypoint guards with upstream

* test(process): release the cleanup target through an owned gate

* test(ci): align semantic identities and owned fixture facts

* test(pcap): retain the upstream DNS wire fixture branch

* test(pcap): use the canonical wire ownership fixture

* test(pcap): assert exact DNS wire query labels

* test(ci): preserve admission and ownership fixture prerequisites

* test(ci): align authenticated fixture and cleanup boundaries

* test(windows): align native artifact fixtures with upstream

* test(android): request complete evidence for inline MCP contracts

* test: avoid pinning diagnostic wording

* test: verify cleanup diagnostics at their source

* test: strengthen evidence and cleanup checks

* test: avoid pinning registration guidance

---------

Co-authored-by: Plastic-time <[email protected]>
Co-authored-by: morluto <[email protected]>
2026-10-27 21:24:30 +08:00
gensokyo-dev 570abe95f2 feat(android): link native declarations to static JNI candidates (#7472)
Link original DEX native declarations to per-ABI function exports and relocated static JNI table candidates through shared CLI/MCP workflows. Preserve global attribution ambiguity, exact provenance, partial coverage and retained Evidence; default MCP delivery to bounded summaries with filtered/batch pages.

Use one immutable ELF relocation decoder with streaming IL2CPP materialization, cooperative bounded export/version parsing and bounded JNI state. Reuse validated session inventories without loading target libraries or executing JNI code.

Validation: relevant type/lint/build and parser/domain/public CLI/MCP regressions; genuine four-ABI NDK fixtures and independent library/slot oracles; actual JDK/JADX Conscrypt workflows with retained-source removal. Full measurements and limitations are recorded in docs/verification/android-jni-bindings-2026-10-27.md.

Closes #6403.
2026-10-27 18:53:22 +08:00
gensokyo-dev 740c0f5615 fix(windows): restore native verification fixture coverage (#7479)
Restore independent PE fixtures and dependency-complete standalone artifact verifier tests. Execute existing real Windows native behavior and inspection recovery proofs before unrelated curated tests.

Validation: 101 focused tests; full typecheck; scoped lint, module/discovery/format/docs checks; valid/corrupt real-image verifier controls; real Windows artifact, behavior, and inspection recovery proof steps passed. Greptile found no actionable issues. Codex review quota exhausted.

CI remains red from exact-base failures: Source 9, Windows 7 (base 15; loader 8 repaired), and built shard 4's 7 matching failures; 7 other shards cancelled by fail-fast. Later Windows package/Node 22/24 checks skipped. User explicitly authorized bypass of established main failures. No claim of complete CI or unavailable coverage.

Fixes #7474.
2026-10-27 18:50:40 +08:00
gensokyo-dev d6f88becd5 fix(mcp): summarize copied browser observations in input declarations (#7469)
Declare copied complete browser observations as abbreviated object operands, keeping canonical validation and normalization controls unchanged. Preserve all other tool declarations and Evidence identities.

Refs #4973 and #4081.
2026-10-27 17:09:22 +08:00
morluto b8cdbdf0ef test(sqlite): gate projection checks on native limits 2026-10-27 15:47:54 +08:00
gensokyo-devand555aaditya 03f17cf41d feat(android): apply caller-selected R8 mapping evidence (#7456)
Apply caller-selected R8/ProGuard mappings to shared Android inspections, preserving raw identities, exact verification, ambiguity and provenance. Add retained summaries and bounded/cached parsing, with measured real R8/Conscrypt workflows and review fixes.

Fixes #6329.

Co-authored-by: 555aaditya <[email protected]>
2026-10-27 15:47:08 +08:00
Rudy Mizrahi Celekliandgensokyo-dev 98422997c0 fix(setup): report Kuna cache aliases consistently (#7207)
fix(setup): report Kuna cache aliases consistently (#7207)

Unify lexical default-cache reports without changing removal authority. Plan selections before deleting managed directories; resolve absolute POSIX paths independently of ambient cwd. Preserve parent traversal, literal backslashes, caller data, and result/removal order.

Co-authored-by: gensokyo-dev <[email protected]>
2026-10-27 15:13:47 +08:00
gensokyo-dev 2817d30ab2 fix(docs): preserve verification reports after experiment archival (#7462)
Move the retained allocation report and unchanged receipt JSON into verification documentation. Keep one report producer, repair current and archived links, document test-support ownership, and remove retired lint inputs.

Fixes #7458.

Validated focused/Python checks, report consistency, docs rendering, and real Bash CLI/MCP facts against an independent oracle. Greptile reviewed all seven files without comments. CI source/Windows/shard-8 failures match clean-main or existing-main controls; the requester authorized merging with those established failures. Codex review quota was unavailable.
2026-10-27 15:10:48 +08:00
吴佳浩Alben 0676ad254c docs(clients): note Droid's FACTORY_HOME_OVERRIDE (#7382)
768b58c6d (fix(setup): honor Droid home override) made factoryDirectory
resolve from FACTORY_HOME_OVERRIDE, so setup now writes the registration
and the personal skill copy under that home instead of ~/.factory. The
Factory Droid section still described only ~/.factory, and the page names
every other client's home override (KIMI_CODE_HOME, QODER_CONFIG_DIR,
QWEN_HOME, HERMES_HOME, CODEX_HOME, GEMINI_CLI_HOME, PI_CONFIG_DIR, DSH_HOME),
so Droid was the only one missing.
2026-10-27 14:30:18 +08:00
morluto dfe6aaf6da fix(gpu): preserve omitted metadata in projected views 2026-10-27 14:23:55 +08:00
morluto e99b6c2c3b fix(dotnet): retain malformed method body diagnostics 2026-10-27 14:15:20 +08:00
morluto 365fb03e73 chore: archive analysis efficiency experiments 2026-10-27 14:10:07 +08:00
morluto 5b14823353 fix(gdb): retain confirmed detach during close 2026-10-27 14:09:06 +08:00
morluto c602fe8828 refactor(schemas): select native and GPU variants by name 2026-10-27 14:04:53 +08:00
morluto d14f199736 fix(debugger): retain verified detach across cleanup retries 2026-10-27 14:04:49 +08:00
morluto 1fe92bff8e fix(pe): bound forwarders to export directory 2026-10-27 14:04:44 +08:00
morluto 50822e1eff fix(evidence): include every subject format in bundles 2026-10-27 13:59:32 +08:00
morluto 59dc48db20 fix(pcap): attribute DNS answers using wire identities 2026-10-27 13:54:20 +08:00
morluto a2e2c29b59 refactor(analysis): move client cleanup to application 2026-10-27 13:49:44 +08:00
morluto abfc42cfb1 fix(wasm): distinguish source and internal failures 2026-10-27 13:49:43 +08:00
morluto 6941d5ab92 fix(native): classify legacy MZ signatures before COFF 2026-10-27 13:49:41 +08:00
morluto df0e2f7b3e fix(desktop): read bounded MZ signature evidence 2026-10-27 13:46:36 +08:00
morluto cce244f037 fix(providers): reject invalid parsed MCP JSON 2026-10-27 13:46:34 +08:00
morluto 4726b19f98 fix(reference): retain ambiguous source identities 2026-10-27 13:46:32 +08:00
morluto 4055cc4df1 fix(verification): retain browser profile until shutdown 2026-10-27 13:42:36 +08:00
morluto d3af70fa27 fix(ida): preserve startup failure ownership 2026-10-27 13:42:35 +08:00
morluto 2733979b4d fix(analysis): validate native UI seed addresses 2026-10-27 13:42:33 +08:00
morluto c9df40f810 refactor(verification): own browser fixture lifecycle 2026-10-27 13:36:57 +08:00
Rudy Mizrahi Celekliandgensokyo-dev 7d6bccb86d fix(harmony): classify exact manifest names once (#7206)
Use one exact-case manifest path classifier for Harmony model hints and components. Preserve unavailable content identity, resource exclusions, Stage precedence and Evidence provenance.

33 boundary tests, docs checks, serial static checks and real pinned-HAP CLI/MCP before/after task passed. Canonical complete projection Evidence stays identical; uppercase derivative now reports unknown/no manifests. Shipped verifier source-ID assertion reproduces on unchanged main. CI7pass4fail19skip7cancel; exactmain reproduces17source and20shard8 failures. Greptile4/5soleupstreamfile-readercleanup scoped/resolvednotfixed. Requester authorized readiness and unrelated-main bypass; human review is not attested.

Co-authored-by: gensokyo-dev <[email protected]>
2026-10-27 13:30:24 +08:00
morluto f59fc19d21 fix(managed): unify native module identity matching 2026-10-27 13:24:11 +08:00
morluto 4e67bc3999 refactor(analysis): consolidate selection ownership 2026-10-27 13:21:35 +08:00
morluto 6d14e25345 refactor(ida): centralize capability admission 2026-10-27 13:21:33 +08:00
morluto a37dbb953c refactor(artifacts): select extraction reader from inventory 2026-10-27 13:21:32 +08:00
morluto 6ed97a22ed fix(export): retain verification cleanup failures 2026-10-27 13:19:25 +08:00
morluto 1ea6903595 fix(ida): preserve path identity across symlink traversal 2026-10-27 13:19:25 +08:00
morluto c29a93097a fix(pcap): preserve metadata on duration overflow 2026-10-27 13:17:00 +08:00
morluto 5b292b630e fix(export): report publication cleanup failures to callers 2026-10-27 13:14:50 +08:00
morluto 01a725ecfa fix(setup): preserve configuration directory requirements 2026-10-27 13:14:50 +08:00
morluto 235e8bc056 refactor(evidence): centralize provider identity comparison 2026-10-27 13:13:06 +08:00
morluto 21483a1a47 refactor(artifacts): reuse descriptor range reader 2026-10-27 13:13:05 +08:00
morluto 98e47e37a2 refactor(pcap): centralize DNS packet facts 2026-10-27 13:10:24 +08:00
morluto 5aa23c3d17 refactor(providers): move inspection scope to application 2026-10-27 13:06:57 +08:00
morluto 82f50f56db fix(macho): validate slice overlap at shared reader 2026-10-27 13:06:56 +08:00
morluto 47f8669c1e fix(export): retain publication and cleanup failures 2026-10-27 13:06:29 +08:00
morluto ddb5337674 fix(setup): resolve Hermes home ancestors before profile selection 2026-10-27 13:06:29 +08:00
morluto b61fe25068 refactor(electron): reuse source range identities 2026-10-27 13:04:25 +08:00
morluto 65aeb80222 refactor(providers): share artifact cleanup binding 2026-10-27 13:04:23 +08:00
morluto c7afedc3d2 refactor(macho): consolidate universal image selection 2026-10-27 13:04:22 +08:00
morluto df8c90df8f fix(browser): retain cancellation after failed worker stop 2026-10-27 13:01:18 +08:00
morluto 8a8331b082 fix(setup): preserve Hermes home traversal 2026-10-27 13:01:18 +08:00
morluto 8d747c2acd fix(macho): unify segment mapping admission 2026-10-27 13:00:33 +08:00
morluto 0a9d92a54b fix(browser): share admitted accessibility identities 2026-10-27 12:57:32 +08:00
morluto 4b6fd03146 refactor(evidence): consolidate import staging 2026-10-27 12:57:31 +08:00
morluto c66729eca9 fix(workers): retain cancellation through final cleanup 2026-10-27 12:56:10 +08:00
morluto 4572f31589 fix(pcap): preserve DNS class when attributing answers 2026-10-27 12:56:10 +08:00
morluto 8691e48e67 refactor(evidence): return import counts from ledger owner 2026-10-27 12:54:37 +08:00
morluto 1dc33c0543 fix(native): reuse PE header layout evidence 2026-10-27 12:54:35 +08:00
morluto 21b6ea7fbd refactor(ida): consolidate pagination progress checks 2026-10-27 12:49:07 +08:00
morluto 8f894546c8 fix(browser): retain evidence across cleanup errors 2026-10-27 12:49:05 +08:00
morluto 178ab26290 fix(pcap): account for SRV owner fields 2026-10-27 12:46:46 +08:00
morluto 7968ceb450 fix(process): normalize capture environment overrides 2026-10-27 12:45:57 +08:00
morluto 160648cc60 fix(filesystem): unify stable file identity checks 2026-10-27 12:45:57 +08:00
morluto 3e590e5b2f fix(evidence): retain malformed native observations 2026-10-27 12:45:57 +08:00
Rudy Mizrahi Celekliandgensokyo-dev 921225d4b2 fix(apk): match signer serials by signed integer identity (#7205)
Match v1 APK certificate and signer serials by signed integer value. Normalize each comparison identity once and preserve raw certificate bytes and Evidence.

37 source/integration tests, 23 real Android SDK verifier cases, SDK-valid padded APK CLI/MCP oracle and serial static checks passed. Hosted real-apk-signature passed. CI27pass7fail2skip7cancel; exact-main controls reproduce17source and14targeted existing-main assertions. NativeWindows/Mac/Node22 baseline gaps and canceled shards are documented. Greptile sole finding is unchanged upstream browser privacy code, scoped and resolved without a fix claim. Requester authorized readiness and unrelated-main failure bypass; no human review is attested.

Co-authored-by: gensokyo-dev <[email protected]>
2026-10-27 12:45:43 +08:00
morluto 25375924f7 fix(pcap): attribute DNS addresses by record owner 2026-10-27 12:42:51 +08:00
morluto e152349c5d fix(process): distinguish omitted byte filenames 2026-10-27 12:42:51 +08:00
morluto 9e4cf28cef refactor(cli): remove obsolete JSON read error mode 2026-10-27 12:42:25 +08:00
morluto a49a3553f4 refactor(managed): consolidate member comparison keys 2026-10-27 12:42:24 +08:00
morluto 397a5599e3 refactor(asar): retain admitted archive extents 2026-10-27 12:42:21 +08:00
morluto 39c7f7e359 fix(evidence): retain malformed function observations 2026-10-27 12:42:18 +08:00
morluto 44310bba32 fix(il2cpp): validate selected FAT slice identity 2026-10-27 12:37:39 +08:00
morluto 279fc04095 fix(snapshot): retain publication receipt on cleanup failure 2026-10-27 12:37:38 +08:00
gensokyo-devandHarsh 7e8f7f6101 feat(android): expose selected manifest component declarations (#7379)
Extend static manifest declarations and retain precise source locations and historical unknowns. Share bounded selected component views across CLI and MCP with complete parent Evidence.

Verified real ApiDemos, an AAPT2-built fixture, legacy Evidence, default-package metadata and relevant CLI/MCP contracts. Recorded whole-task token estimates, timing/resource limits and independent main-failure controls in the PR.

Co-authored-by: Harsh <[email protected]>
2026-10-27 12:37:10 +08:00
morluto 3d311d9eca fix(cli): report failed browser cleanup retries 2026-10-27 12:31:48 +08:00
morluto b9e5a9f463 fix(process): retain unrepresentable filename evidence 2026-10-27 12:31:48 +08:00
morluto dd2cbbf1a2 fix(il2cpp): require FAT container identity 2026-10-27 12:31:35 +08:00
morluto 7cbf375d01 fix(evidence): retain authenticated parents on cancellation 2026-10-27 12:31:33 +08:00
morluto dcfb61826d fix(native): share Swift symbol recognition 2026-10-27 12:31:31 +08:00
morluto dd9f5cdc4d refactor(providers): share client close options 2026-10-27 12:27:34 +08:00
morluto 21f12b896e fix(native): order Objective-C category records 2026-10-27 12:27:32 +08:00
morluto f5edef3c96 fix(hopper): share cancellable probe output waits 2026-10-27 12:25:03 +08:00
morluto ad1a57b65d fix(native): own Objective-C category identity 2026-10-27 12:25:01 +08:00
morluto c4242cb85e fix(evidence): own standalone command finalization 2026-10-27 12:24:59 +08:00
morluto 2e7a206b20 fix(process): preserve symlink target bytes 2026-10-27 12:23:56 +08:00
morluto ff6b3fb9a1 fix(evidence): admit rebound APK and WASM views 2026-10-27 12:23:56 +08:00
morluto 14dd07e740 fix(exports): preserve ELF name byte identity 2026-10-27 12:17:29 +08:00
Rudy Mizrahi Celekliandgensokyo-dev 1cd82597da fix(browser): normalize discovery wire facts at the shared boundary (#7204)
Normalize transport-specific discovery facts at their shared owner; preserve upstream duplicate-ID rejection and correct discovery-time attachment uncertainty in all consumers.

53 focused regressions, serial static checks and real Chrome CLI/MCP fault injection/inspection pass. Greptile final head 5/5. Exact-main controls reproduce all 17 source and 21 shard-8 failures; seven shards cancelled and remain unverified. Requester authorized readiness and unrelated-main CI bypass. Human review is not attested.

Co-authored-by: gensokyo-dev <[email protected]>
2026-10-27 12:16:50 +08:00
morluto e334a13c79 fix(cli): merge mixed array filter selections 2026-10-27 12:14:36 +08:00
morluto effb796027 fix(evidence): validate retained views by semantic identity 2026-10-27 12:14:36 +08:00
morluto 5f9f13d77b fix(evidence): preserve view identity and cancellation outcomes 2026-10-27 12:12:34 +08:00
morluto 6de60993b8 fix(adb): retain output on command failures 2026-10-27 12:06:29 +08:00
morluto 47a8000cce fix(cli): project nested array slices 2026-10-27 12:06:29 +08:00
morluto 2f27d3ac92 fix(evidence): separate source and presentation identity 2026-10-27 12:06:29 +08:00
morluto 3e8923c6eb fix(evidence): share queued cancellation handling 2026-10-27 12:06:15 +08:00
morluto 0793b6c3ca fix(rizin): retain rejected debugger shutdowns 2026-10-27 12:06:14 +08:00
morluto f89b03e738 fix(gdb): retain rejected shutdowns for retry 2026-10-27 12:01:12 +08:00
morluto 7557e39dcd fix(providers): preserve cleanup retry diagnostics 2026-10-27 11:59:44 +08:00
morluto 0eff039d08 fix(mcp): preserve Android cleanup failures 2026-10-27 11:56:01 +08:00
morluto 5d30f82417 fix(artifacts): validate directory source spelling 2026-10-27 11:54:58 +08:00
morluto 7cc33fcf7e fix(server): preserve cleanup failure diagnostics 2026-10-27 11:52:46 +08:00
morluto 8aa6df2d86 fix(native): share otool build tool parsing 2026-10-27 11:52:21 +08:00
morluto a6a69ec63e fix(ghidra): require Windows cleanup ownership 2026-10-27 11:49:23 +08:00
morluto cd7a0eefd8 fix(apple): validate JVM interface identity 2026-10-27 11:49:22 +08:00
morluto c79bd41b5d test(hopper): verify bootstrap cleanup ownership 2026-10-27 11:45:25 +08:00
morluto 4e33bd3e08 fix(browser): preserve discovery identity ambiguity 2026-10-27 11:45:25 +08:00
morluto a486cfe518 fix(windows): validate declared PE header identity 2026-10-27 11:45:25 +08:00
morluto a116f2a7ae fix(process): close lease peers during release 2026-10-27 11:45:02 +08:00
morluto d2da713859 fix(setup): recognize inherited owned launcher 2026-10-27 11:45:01 +08:00
morluto f2277454b8 fix(setup): preserve unowned client registrations 2026-10-27 11:39:32 +08:00
morluto 4de432d868 refactor(pyinstaller): preserve parsed compression flags 2026-10-27 11:35:47 +08:00
morluto f812dc4135 fix(hopper): retain lease after startup failure 2026-10-27 11:35:46 +08:00
morluto 319fee4691 fix(electron): select nested handlers by source coordinates 2026-10-27 11:30:37 +08:00
morluto 5d5fef7391 fix(process): retain lease recovery cleanup owners 2026-10-27 11:30:35 +08:00
morluto 86d4a21215 docs(search): clarify provider case folding 2026-10-27 11:24:10 +08:00
morluto 7b25049dee refactor(browser): centralize passive source deadlines 2026-10-27 11:24:09 +08:00
morluto fe95b7611f fix(process): retain lease directory cleanup ownership 2026-10-27 11:24:07 +08:00
morluto 76eecf7fa1 fix(mcp): advertise standard route path schemas 2026-10-27 11:23:39 +08:00
morluto 081f2e2559 refactor(process): share stable file identity checks 2026-10-27 11:16:44 +08:00
morluto a18297f7d1 refactor(electron): use required native module facts 2026-10-27 11:16:43 +08:00
morluto 76441ed31b fix(browser): preserve comparison evidence identity 2026-10-27 11:15:38 +08:00
morluto 52b3c63495 fix(ida): complete private input writes 2026-10-27 11:15:38 +08:00
morluto 0d1d6e858d fix(ghidra): unify Unicode literal search semantics 2026-10-27 11:10:10 +08:00
morluto 492860ed07 test(captures): assert retained snapshot evidence 2026-10-27 11:10:10 +08:00
morluto 1635e559d2 refactor(go): simplify build metadata decoding 2026-10-27 11:10:10 +08:00
morluto 92fb319b27 refactor(gdb): reuse inferior identity reader 2026-10-27 11:10:10 +08:00
morluto 17da88f117 fix(process): report retained capture root 2026-10-27 11:08:55 +08:00
morluto 768b58c6db fix(setup): honor Droid home override 2026-10-27 11:08:54 +08:00
morluto 6207843814 fix(ida): preserve Unicode literal search matches 2026-10-27 10:59:38 +08:00
morluto e58feb48c3 refactor(mcp): reuse reverse engineering admission 2026-10-27 10:59:36 +08:00
morluto 3c0e021cb3 refactor(captures): retain SAZ member classifications 2026-10-27 10:59:35 +08:00
morluto f8f7ac96de refactor(mcp): share entropy summary delivery 2026-10-27 10:55:00 +08:00
morluto 1fdd3df7b4 fix(hopper): preserve regex inventory facts 2026-10-27 10:54:59 +08:00
morluto 82d3ab7aa8 fix(browser): respect response metadata grammars 2026-10-27 10:51:50 +08:00
morluto e4cdf65e2c refactor(mcp): share diagnostic evidence handling 2026-10-27 10:51:50 +08:00
morluto 37d6e8ca9a refactor(macho): reuse admitted header identity 2026-10-27 10:51:50 +08:00
morluto bae84d1b93 fix(native): validate plist target identity 2026-10-27 10:48:42 +08:00
morluto 0e17aab451 fix(providers): deduplicate nested cleanup notifications 2026-10-27 10:45:54 +08:00
morluto b989e3c3a3 fix(browser): parse HTTP Link whitespace correctly 2026-10-27 10:45:01 +08:00
morluto 6fa8e2ee3d refactor(node-sea): reuse Mach-O segment facts 2026-10-27 10:44:51 +08:00
morluto 79b8219f29 fix(certificates): unify OID decoding and validate reports 2026-10-27 10:40:48 +08:00
morluto f061989ad2 fix(artifacts): validate bundle resource target identity 2026-10-27 10:39:56 +08:00
morluto b7be0d90a9 fix(firmware): retain nested dependency diagnostics 2026-10-27 10:35:50 +08:00
morluto 92213a9155 test(artifacts): verify evidence refresh after reopen 2026-10-27 10:32:58 +08:00
morluto ec40c10749 refactor(process): share comparison dimension projections 2026-10-27 10:30:59 +08:00
morluto 5093d51c05 refactor(browser): share CDP script source capture 2026-10-27 10:30:57 +08:00
morluto 413f24b532 fix(artifacts): unify selected path failure classification 2026-10-27 10:30:56 +08:00
morluto 3655dfd7d5 fix(elf): validate completed note coverage 2026-10-27 10:30:54 +08:00
morluto 30216468cc fix(browser): share media type interpretation 2026-10-27 10:26:32 +08:00
morluto 4f2fc7ca28 refactor(pe): rely on path input contracts 2026-10-27 10:26:31 +08:00
morluto b0c48e8b65 fix(native): preserve mapping and literal coverage 2026-10-27 10:26:29 +08:00
morluto ded6542d93 refactor(plist): unify number source association 2026-10-27 10:22:33 +08:00
morluto b7cae1ad8c fix(artifacts): validate partial inventory edges 2026-10-27 10:19:00 +08:00
Rudy Mizrahi Celekliandgensokyo-dev 6749bd090d fix(browser): keep opaque cookie partitions unaligned (#7203)
Keep cookies with opaque partitions distinct from unpartitioned identities while retaining value hashes and incomplete comparison coverage. Preserve known partition hashes and use the existing shared CLI/MCP fingerprint owner.

Validated with 63 source and 57 storage boundary tests, builds/static checks, and real Chrome156 CLI/MCP controls. Live opaque-cookie emission was not reproduced; its protocol regression is explicit. Hosted CI's unrelated failures were reproduced against exact main a905a71e3; seven fail-fast-cancelled shards remain unverified; Greptile's sole existing-main finding was scoped and documented. Human readiness remains unasserted; bypass authorized by the requester.

Co-authored-by: gensokyo-dev <[email protected]>
2026-10-27 10:16:15 +08:00
morluto f53c256886 fix(macho): unify fixed-width name decoding 2026-10-27 10:15:49 +08:00
morluto 25fc08189e fix(wasm): parse interface group heads consistently 2026-10-27 10:12:38 +08:00
morluto d1c88fab1a fix(artifacts): validate partial inventory root identity 2026-10-27 10:11:49 +08:00
morluto bfdde7ee4c refactor(web): centralize isolated script paths 2026-10-27 10:05:33 +08:00
morluto 8dc4421325 fix(artifacts): reject inventory counts beyond manifest 2026-10-27 10:04:29 +08:00
morluto 9aee546aba refactor(process): unify trace outcome comparison 2026-10-27 10:03:18 +08:00
morluto c81b7ecebc fix(electron): refuse linked ASAR parent declarations 2026-10-27 10:03:17 +08:00
morluto 650873e772 fix(artifacts): preserve mounted DMG traversal coverage 2026-10-27 10:03:15 +08:00
morluto 2146caeb3d fix(electron): share ASAR node validation 2026-10-27 09:59:58 +08:00
morluto ed62f14be4 refactor(ci): consolidate lint task ownership 2026-10-27 09:55:24 +08:00
morluto 8e0b2a9765 fix(wasm): share WAT comment framing 2026-10-27 09:55:22 +08:00
morluto c1e0f531e6 fix(firmware): admit caller-selected source directly 2026-10-27 09:55:01 +08:00
morluto a905a71e31 fix(plist): preserve binary UID and dictionary identity 2026-10-27 09:49:28 +08:00
morluto f878266503 fix(artifacts): admit selected app bundle traversal 2026-10-27 09:48:52 +08:00
morluto 15a8f6847c fix(browser): reject inexact storage values 2026-10-27 09:45:11 +08:00
morluto f3a301eca5 fix(artifacts): validate selected path traversal 2026-10-27 09:45:10 +08:00
morluto 27ac1b6781 refactor(contracts): consolidate tool effect definitions 2026-10-27 09:43:10 +08:00
morluto 443d0778be fix(browser): resolve JSON pointers through array elements 2026-10-27 09:40:11 +08:00
morluto 01d6a95fd7 fix(ghidra): preserve configured seed path traversal 2026-10-27 09:39:43 +08:00
morluto 7ba1d0161e fix(browser): retain ambiguous scenario request identity 2026-10-27 09:35:59 +08:00
morluto 6979c589b0 fix(ghidra): preserve project access failures 2026-10-27 09:35:15 +08:00
morluto 6734dee6a8 fix(managed): retain comparison target cleanup ownership 2026-10-27 09:35:15 +08:00
morluto 83bbad2b2f fix(browser): advance storage pages by examined rows 2026-10-27 09:32:32 +08:00
morluto 242a0f2880 fix(evidence): retain comparison cleanup ownership 2026-10-27 09:31:17 +08:00
morluto 9be640a8b4 fix(providers): preserve selected filesystem identities 2026-10-27 09:31:17 +08:00
morluto 9eb8ba2b29 refactor(cli): use builtin argument owner directly 2026-10-27 09:29:20 +08:00
morluto 55b4efcb88 fix(providers): retain completed cleanup observations 2026-10-27 09:26:42 +08:00
morluto fef83241b1 fix(setup): validate Hopper download channel URLs 2026-10-27 09:26:40 +08:00
morluto 25b69bb803 fix(javascript): qualify network receiver evidence 2026-10-27 09:24:22 +08:00
morluto a293738c66 fix(electron): preserve symlink directory requirements 2026-10-27 09:24:22 +08:00
morluto f68b261db4 refactor(process): remove unused worker PID state 2026-10-27 09:23:20 +08:00
morluto 212479aa02 refactor(environment): reuse recorded query parser 2026-10-27 09:23:19 +08:00
morluto e2da92f5d2 fix(javascript): unify runtime module declarations 2026-10-27 09:23:15 +08:00
morluto 01484bc95c fix(setup): unify macOS version eligibility 2026-10-27 09:23:13 +08:00
morluto a52914649a fix(native): use owned address grammar for coordinates 2026-10-27 09:19:56 +08:00
morluto 4266d32751 fix(cli): retain resources from both cleanup failures 2026-10-27 09:19:54 +08:00
morluto 329325049c refactor(cli): retain typed analysis outcomes through delivery 2026-10-27 09:16:02 +08:00
morluto 0e1500dc8d fix(javascript): qualify renderer endpoint observations 2026-10-27 09:15:24 +08:00
morluto 9cad06db10 fix(managed): retain qualified native candidates 2026-10-27 09:15:24 +08:00
morluto 9c9c64edfa refactor(views): centralize paging contracts 2026-10-27 09:07:50 +08:00
morluto 5cf7133f82 refactor(javascript)!: consolidate renderer and route ownership
Remove the derived package renderer property from new application graphs. Use renderer_declarations for source fields and package loads edges for the selected candidate; previously retained Evidence remains readable.
2026-10-27 09:05:24 +08:00
morluto 39179db68d refactor(artifacts): unify inventory integrity policy 2026-10-27 09:05:18 +08:00
morluto 6d7e627783 fix(cli): preserve literal evidence in Markdown output 2026-10-27 09:04:11 +08:00
morluto bf7721080f refactor(evidence): centralize value flow metadata schema 2026-10-27 09:02:32 +08:00
morluto 5cd61aac8e fix(artifacts): preserve cancellation during inventory cleanup 2026-10-27 08:59:57 +08:00
morluto 72cc2d30a3 fix(browser): preserve request and cleanup failures 2026-10-27 08:58:27 +08:00
morluto 339b82e0c8 fix(cutter): preserve bridge directory traversal 2026-10-27 08:58:27 +08:00
morluto b2df588b4a fix(gpu): retain AMD target declaration evidence 2026-10-27 08:55:43 +08:00
morluto aa253bd13f fix(electron): scope IPC pairing to application ownership 2026-10-27 08:55:34 +08:00
morluto 781eb1b28a fix(browser): validate basic credential usernames 2026-10-27 08:52:22 +08:00
morluto 4932e5615b fix(update): preserve Bun global directory identity 2026-10-27 08:52:19 +08:00
gensokyo-devandAce d109a39105 feat(cli): validate browser scenarios offline (#7343)
Add validate-browser-scenario for offline schema and semantic checks of inline JSON, local files and stdin. Return compact validity/action/secret counts without reading secret values or discovering, launching, attaching to or navigating a browser.

Execution and validation share CLI input admission and the canonical domain schema. Preserve existing capture lifecycle, diagnostics, cancellation and caller-relative executable paths.

Integrates #7149 with attribution. Fixes #5488. Validation includes 24 focused tests, static/build/docs checks, and the preserved real PocketBase scenario with its backend stopped and secret variables unset. Verification and resource/token measurements are recorded in docs/verification/browser-scenario-validation-2026-10-27.md.

Co-authored-by: Ace <[email protected]>
2026-10-27 08:47:36 +08:00
morluto 06642a48fb fix(hopper): preserve selected home and cache paths 2026-10-27 08:47:16 +08:00
morluto e3044645c7 fix(managed): retain local signature comparison coverage 2026-10-27 08:46:31 +08:00
morluto ad92e0f9c8 refactor(ghidra): consolidate function containment lookup 2026-10-27 08:46:27 +08:00
morluto 838f5f44d2 fix(reference): preserve incomplete resolution uncertainty 2026-10-27 08:40:36 +08:00
morluto 54fbfacfb1 fix(macho): preserve leading spaces in export identities 2026-10-27 08:34:42 +08:00
morluto 0781c3e99c fix(reference): validate Git metadata path traversal 2026-10-27 08:34:42 +08:00
morluto 87d023dcc8 fix(flutter): honor cancellation after cleanup retry 2026-10-27 08:34:42 +08:00
morluto f347713e24 refactor(session): share provider replay attribution 2026-10-27 08:32:13 +08:00
morluto 6b3a081f36 fix(flutter): retain snapshot cleanup ownership 2026-10-27 08:29:29 +08:00
morluto abc1351506 fix(artifacts): distinguish incomplete PyInstaller probes 2026-10-27 08:27:08 +08:00
morluto 0ddde829b2 fix(session): preserve failed target restoration 2026-10-27 08:21:41 +08:00
morluto 3431541073 fix(views): honor cancellation before admission 2026-10-27 08:18:40 +08:00
morluto e7f29f8b8f fix(zip): validate admitted source throughout reads 2026-10-27 08:18:40 +08:00
morluto cb0c73246d refactor(artifacts): reuse admitted sources for extraction 2026-10-27 08:18:04 +08:00
gensokyo-devandEdgar Arakelyan 3fcbabe593 feat(browser): reuse saved response values across scenario steps (#7267)
Save typed response fields and reuse generated IDs or authentication values across browser scenario requests, UI observations and navigation through the shared CLI/MCP workflow.

Preserve exact numbers, retained Evidence, explicit source/coverage limits and prospective page-value semantics. Bound response decoding, retained spellings and request serialization before allocation, and protect marked response values across source records, follow-up text and planned screenshots.

Integrates Edgar Arakelyan's #7162, including follow-ups through 9f5191ab0. Final local validation: 560 feature-branch tests and 561 current-main integration tests, static/build/docs checks and real PocketBase CLI/MCP privacy workflows. Whole-task comparison and verification limits are recorded in docs/verification/browser-saved-values-2026-10-26.md.

Co-authored-by: Edgar Arakelyan <[email protected]>
2026-10-27 08:15:52 +08:00
morluto 60822e0702 fix(doctor): preserve Homebrew application paths 2026-10-27 08:11:18 +08:00
morluto ee654e98a6 refactor(artifacts): retain PyInstaller carrier dialect 2026-10-27 08:10:55 +08:00
morluto 5b8e415483 refactor(snapshot): centralize resource cache eligibility 2026-10-27 08:10:48 +08:00
morluto 5d25aa4f52 refactor(artifacts): retain admitted Godot pack location 2026-10-27 08:06:23 +08:00
morluto 69b328eec8 test(browser): verify out-of-order response correlation 2026-10-27 08:04:42 +08:00
morluto 8c8a2752c0 refactor(artifacts): retain admitted PyInstaller cookie facts 2026-10-27 08:02:51 +08:00
morluto 9222526fe2 fix(electron): retain selected scenario paths in evidence 2026-10-27 08:02:45 +08:00
morluto 68533bc0a3 refactor(evidence): share subject architecture declarations 2026-10-27 07:57:37 +08:00
morluto 2ad599dace fix(snapshot): export only target-bound observations 2026-10-27 07:55:16 +08:00
morluto 7fa54e2d62 fix(process): preserve command completion and input ownership 2026-10-27 07:55:16 +08:00
morluto 8a47cbbcce fix(artifacts): deduplicate keyed archive selector choices 2026-10-27 07:54:09 +08:00
morluto b8f58fd7ca refactor(mcp): bind summary delivery to tool contracts 2026-10-27 07:54:01 +08:00
morluto eb24da4481 fix(session): retain unknown cleanup across mixed failures 2026-10-27 07:53:54 +08:00
morluto 25a08ed639 refactor(ida): reuse bounded registration reader 2026-10-27 07:45:45 +08:00
morluto 11b025ef97 fix(session): retain cleanup ownership during failed opens 2026-10-27 07:45:39 +08:00
morluto d1202c539d fix(web): preserve Next route filename patterns 2026-10-27 07:44:04 +08:00
morluto 5261340397 fix(evidence): retain analysis view environment 2026-10-27 07:44:04 +08:00
morluto ecd22c42ac fix(firmware): retain completed command failures 2026-10-27 07:44:04 +08:00
morluto 84a54b6183 fix(asar): preserve header filename identity 2026-10-27 07:44:04 +08:00
morluto 046c4ce353 fix(javascript): preserve native export facts in comparisons 2026-10-27 07:40:55 +08:00
morluto c74059d89f fix(javascript): preserve unknown Next package ownership 2026-10-27 07:40:45 +08:00
morluto 6a9911ea77 fix(session): track retained bundle cleanup dependencies 2026-10-27 07:40:40 +08:00
morluto e80fb638be fix(web): retain source-map declaration provenance 2026-10-27 07:35:13 +08:00
morluto 59a70adefd fix(artifacts): preserve source and coverage facts 2026-10-27 07:35:13 +08:00
morluto 95d9d51f72 fix(process): retain failures through finalization 2026-10-27 07:35:13 +08:00
morluto 381004b664 fix(decompile): preserve provider cleanup through export finalization 2026-10-27 07:34:22 +08:00
morluto af9bc3ab50 fix(ci): validate static evidence through receipt owner 2026-10-27 07:29:57 +08:00
morluto 87e2cc26d8 fix(environment): align comparison collection identity 2026-10-27 07:28:49 +08:00
morluto 29ebd67405 fix(native): retain cleanup failures across capability fallback 2026-10-27 07:25:23 +08:00
morluto 0e578d43c8 refactor(native): retire legacy provider output wrappers 2026-10-27 07:25:08 +08:00
morluto 8706068b57 fix(contracts): validate provider boundary facts consistently
Count only canonical inventory rows in binary overview, reuse the owned Objective-C coverage schemas, and share firmware input issue projection.
2026-10-27 07:19:46 +08:00
morluto 57d0ed45f7 fix(process): retain inherited pipes on output overflow 2026-10-27 07:17:10 +08:00
morluto 0dea08fb07 fix(web): interpret encoded Next route paths 2026-10-27 07:17:10 +08:00
morluto 087acf26e8 fix(evidence): own cached snapshot data 2026-10-27 07:17:10 +08:00
morluto 08f99dbaf2 fix(setup): preserve and validate selected JEB endpoints 2026-10-27 07:17:10 +08:00
morluto dbe2c97af3 refactor(ownership): consolidate snapshot and comparison facts
Retain authenticated immutable snapshot payloads while keeping mutable exports independent. Derive artifact metadata comparisons once and reuse the owning native trace limits.
2026-10-27 07:13:32 +08:00
morluto 3052a067a3 fix(apple): derive NIB hierarchy from observed relationships
Replace overlapping traversals with one bounded relationship owner. Preserve superview direction, treat conflicting or unsupported registry placement as unknown, and retain source facts and coverage.
2026-10-27 07:09:25 +08:00
morluto db0908a732 refactor(artifacts): consolidate projection and PCK interpretation
Keep historical inventory summary variants with their projection owner and share embedded PCK trailer observations while preserving each reader’s admission policy.
2026-10-27 07:09:05 +08:00
morluto 077de92594 fix(setup): preserve selected Hopper loader arguments 2026-10-27 07:03:11 +08:00
morluto 2dd2ffcd45 fix(artifacts): bind operations to admitted sources 2026-10-27 07:03:11 +08:00
morluto ace4dbf36e fix(contracts): align live target classification with admitted facts
Share exact live target variants across open and session responses, preserving historical Evidence and snapshot admission. Consolidate equivalent ELF metadata aliases at their owner.
2026-10-27 06:53:47 +08:00
morluto fc7f042c8c refactor(lifecycle): require cleanup for offline providers
Require resource-owning provider ports to expose cleanup and migrate test adapters to delegate or explicitly close their own resources.
2026-10-27 06:49:07 +08:00
morluto b529bd84d7 refactor(contracts): preserve named output schema types
Keep exact readonly schema maps and constrain contract factories to their owned keys. Verify named catalog lookups retain each schema type.
2026-10-27 06:48:55 +08:00
morluto 248133d784 fix(analysis): interpret provider and ELF inputs once
Pass parsed MIPS program declarations to shared-load analysis and validate IDA string address selectors before enumeration, including empty inventories.
2026-10-27 06:48:48 +08:00
morluto 030fa886f9 fix(setup): verify published configuration content during uninstall
Require publication readback in the owning check so in-place edits are detected and preserved with the original backup.
2026-10-27 06:43:53 +08:00
morluto 82fd223638 refactor(native): consolidate provider-neutral analysis contracts
Move shared function, dossier, inventory, reference and output semantics from the legacy Hopper module to native domain owners. Migrate provider, adapter, test and verifier consumers directly.
2026-10-27 06:43:53 +08:00
morluto a6488a96a9 fix(javascript): preserve renderer path anchors 2026-10-27 06:43:07 +08:00
morluto d0e695b42a fix(hopper): bind relative launcher paths once 2026-10-27 06:43:07 +08:00
morluto 90475ec9e5 fix(artifacts): retain selected keyed archive paths 2026-10-27 06:43:07 +08:00
morluto d60b2cab65 docs(ida): document migration from legacy attached registrations
Describe the native plugin setup, identity and capability differences, verification, and rollback. Link the migration from the packaged skill source.
2026-10-27 06:38:51 +08:00
morluto ed6668b5bc fix(evidence): validate process captures before bundle export
Reuse the read-side capture validation before publication so exported bundles can be imported. Preserve historical and foreign record identity and existing destination files on failure.
2026-10-27 06:36:31 +08:00
morluto 50717c39b6 refactor(contracts): consolidate provider and search ownership
Resolve IDA compatibility facts in one place and migrate capability, session, and verifier consumers. Move native search fields to the domain and reuse residual unknown fields for filters.
2026-10-27 06:36:24 +08:00
morluto 567ea4b1e8 refactor(ownership)!: consolidate legacy aliases
Migrate schema, installer, ASN.1, and tooling consumers to their owning definitions. Remove the redundant CI task entry and the deprecated Cutter command-local JSON flag.

BREAKING CHANGE: Replace Cutter --json=true or --json=false with --json-output=true or --json-output=false. Bare --json still controls CLI result formatting.
2026-10-27 06:31:23 +08:00
morluto 7c8eb63fcc fix(javascript): retain package entrypoint provenance 2026-10-27 06:28:15 +08:00
morluto c27560e434 fix(artifacts): classify selected package names 2026-10-27 06:28:15 +08:00
morluto 3515b26221 refactor(ownership)!: remove redundant aliases
Use canonical owners for literal matches, tool families, MCP schema presentation and IDA protocol selection; remove unused web-bundle reexports. Remove the duplicate CLI command name and keep the canonical compare workflow.

BREAKING CHANGE: rea compare-bundles has been removed. Use rea compare instead. The MCP compare_bundles tool is unchanged.
2026-10-27 06:27:10 +08:00
morluto c4cdb7658b fix(jeb)!: preserve engine refusal diagnostics
Parse JEB failure envelopes at the provider boundary and retain the original response, recovery facts, and selected inputs. Do not infer an unsupported target from undocumented error codes; preserve completed primary observations when metadata requests fail.

BREAKING CHANGE: JEB engine refusals now report execution_failure rather than unsupported_target. Consumers should inspect the retained upstream diagnostics for the engine's reason and recovery facts.
2026-10-27 06:22:10 +08:00
morluto 7f65617064 fix(artifacts): resolve historical retained summaries
Authenticate exact retained parents against current and documented historical summary projections, including caller-selected display paths. Keep source facts, Evidence commitments and unsupported-projection rejection intact.
2026-10-27 06:21:50 +08:00
akushonkamenandgensokyo-dev c6893d1ac6 fix(browser): preserve external defaults during CDP attachment (#7188)
Attach to external browsers without implicit default-context overrides, and own temporary focus only on the selected page. Preserve unrelated page media and exact page inventory through CLI/MCP. Remove bootstrap-page retries; retain late/failed cleanup ownership and verified SDK notices.

Assisted-by: Kogasa (gensokyo-skills:kogasa-surprise-testing)
Co-authored-by: gensokyo-dev <[email protected]>
2026-10-27 06:16:14 +08:00
morluto 779d548c4e fix(artifacts): preserve selected ASAR companion paths 2026-10-27 06:12:41 +08:00
morluto e891926534 fix(artifacts): compare Node SEA member declarations
Compare each retained member's exact runtime key and role without treating archive order as semantic identity. Keep one-sided legacy declaration gaps unknown, preserve proven changes with unavailable content, and retain original Evidence.
2026-10-27 06:06:27 +08:00
morluto 60834e391c fix(artifacts)!: compare ASAR storage declarations
Record packed and unpacked ASAR file declarations at the reader boundary and compare known storage changes without treating historical omissions as packed. Preserve known differences when bytes are unavailable and retain existing integrity precedence.

BREAKING CHANGE: ASAR file occurrences may now contain unpacked: false. Consumers that validate only true must accept an optional boolean; absent historical fields remain unknown.
2026-10-27 05:59:40 +08:00
morluto 83b2d16ee9 fix(javascript): distinguish HTML script loads from data 2026-10-27 05:57:26 +08:00
morluto 8694dc30d9 fix(hopper): discover linked application bundles 2026-10-27 05:57:26 +08:00
morluto 67b9181010 fix(ida): separate function identity from source span
Normalize function address and name without requiring an unused upstream size. Preserve complete upstream observations and the explicit unknown body coverage across IDA protocol profiles.
2026-10-27 05:52:15 +08:00
morluto 81adb8977a fix(ghidra): honor current directory in PATH discovery 2026-10-27 05:49:17 +08:00
morluto e1ec737d9b refactor(artifacts): share extraction admission and reader construction
Reuse completed embedded-format observations only after validating the admitted file identity and state, and centralize reader construction across inventory and extraction. Preserve early refusal, selected-path checks, ZIP ownership, and each operation's traversal policy. A source changed between preflight and inventory now fails integrity validation.
2026-10-27 05:46:25 +08:00
morluto 3a6e4f2cb1 fix(managed): report graph method page coverage 2026-10-27 05:42:50 +08:00
morluto 19f9011511 fix(setup): honor VS Code user data overrides 2026-10-27 05:42:50 +08:00
morluto 52414282b9 fix(xrefs): trust provider address validation
After an invalid address-format result, resolve the caller's exact symbol instead of reclassifying address-like names with a separate heuristic.
2026-10-27 05:37:55 +08:00
morluto 4191974010 fix(environment): share retained response declarations
Preserve status independently of malformed content and use one declaration reader for route grouping, state extraction and manifest eligibility. Verify the partial facts through CLI and MCP views.
2026-10-27 05:37:47 +08:00
morluto 5f91616727 fix(hopper): confirm private display probe cleanup 2026-10-27 05:36:09 +08:00
morluto b6e58c22d4 fix(evidence): retain source and stream ownership 2026-10-27 05:36:09 +08:00
morluto 4803c4f9c2 fix(setup): preserve selected settings and Java paths 2026-10-27 05:36:09 +08:00
morluto 47fa4fbad6 fix(views): bind Mach-O rows to selected facets
Derive each facet's row schema from the Mach-O producer and validate it against the requested page. Preserve the existing wire shape and historical defaults.
2026-10-27 05:33:10 +08:00
morluto 06c8e14b34 fix(native): retain lipo field declarations
Distinguish omitted slice fields from declarations that cannot be interpreted, and preserve their source in native results and artifact limitations. Keep structural fallback and capacity failures explicit.
2026-10-27 05:33:01 +08:00
morluto ca3fd7baf9 fix(javascript): preserve artifact failures through cleanup 2026-10-27 05:23:53 +08:00
morluto 8b7ee48a46 fix(kuna): bind cache exports to their producing inputs 2026-10-27 05:23:53 +08:00
morluto 906f161dc6 fix(environment): share legacy recorded-time interpretation
Use one owner for exact recorded timestamps and documented native-Date fallback in state and probe replay. Retain the source and disclose native precision and host-timezone dependence in state limitations.

Validated historical chronology, timezone and unknown-duration regressions against prior behavior.
2026-10-27 05:19:43 +08:00
morluto 63220edcc3 fix(environment)!: withhold clock writes without an anchor
Keep missing or ambiguous scenario request durations unknown, retain learned clock rules, and withhold plans that cannot generate an observed clock. Refuse such writes in older imported states before changing state or generators.

BREAKING CHANGE: The write-plan reason can now be unknown-clock; strict clients must accept the new output value.

Validated state, CLI and advertised MCP workflows, documentation, and focused clock and cleanup regressions.
2026-10-27 05:19:35 +08:00
morluto 19b2972dfb fix(environment): separate request order from completion
Retain valid request starts independently of duration and require observed completion for interval-based state and write inferences. Bound indexed interference checks across captured writes while preserving ordinal fallback and unmodified Evidence.

Validated state, comparison, CLI and MCP workflows with known, unknown, overlapping and redacted timing controls; check:fast passed.
2026-10-27 05:08:14 +08:00
morluto 3233992733 fix(browser): redact startup cleanup diagnostics 2026-10-27 04:59:27 +08:00
morluto 855eb703af perf(setup): wait directly for prewarm completion 2026-10-27 04:59:27 +08:00
morluto d815367a11 fix(analysis): preserve failure and cleanup outcomes 2026-10-27 04:59:27 +08:00
Chip Liandgensokyo-dev 4767970105 fix(artifacts): explain recovery for existing DMG attachments (#7221)
Preserve producer-supported recovery guidance after an existing-image DMG attachment refusal, without borrowing or detaching existing mounts. Keep diagnostics, root provenance and unknown coverage; centralize guidance ownership and run the real mounted-image regression in macOS CI.

Co-authored-by: gensokyo-dev <[email protected]>
2026-10-27 04:58:43 +08:00
吴佳浩Alben 9e6dd8e372 docs(desktop): name the get_evidence_bundle tool in the MCP section (#7266)
The collection-paging paragraph says complete Evidence stays accessible through
`get_evidence`, but no tool by that name exists. The session family registers
`get_evidence_bundle`, which is the only name the catalog, the contracts and the
CLI equivalents use.

Every other page already uses the full name, so this was the only place a reader
or agent would look for a tool that cannot be called.
2026-10-27 04:56:17 +08:00
Saim Shafiqueandgensokyo-dev 0d1a454e25 fix(ghidra): admit newer 12.x releases as compatible builds (#6924)
Admit same-major Ghidra releases at or above the verified minor through the existing compatible/unverified policy. Preserve the observed version, verified12.1.4 pin, installation-declared Java floor and separate NativeAOT verification gate.

367 targeted tests/static/docs, all10 real Java bridge-source compilation, and actual Linux12.2/JDK25 CLI/MCP queries and cleanup pass. JDK21 remains refused for12.2. Exact-main controls reproduce all3 unrelated CI assertions; canceled shards and an apt-blocked binary-layout lane remain unverified.

Co-authored-by: gensokyo-dev <[email protected]>
2026-10-27 03:49:12 +08:00
morluto dd7a2da5bb fix(environment): keep invalid recorded dates unknown
Distinguish invalid supported timestamp spellings from unrecognized legacy formats at the recorded-date owner. Probe replay retains ordinal order for invalid dates without changing the existing legacy fallback.

Validated date parsing, state ordering and comparison workflows; the prior implementation reorders the invalid-date regression.
2026-10-27 03:29:11 +08:00
morluto 4e86bd0c1c fix(asar): disclose unverified block integrity
Retain a limitation when ASAR block integrity fields are present. Whole-file hash verification does not establish block verification or Electron runtime acceptance.

Validated inventory and extraction workflows, including partial and malformed block declarations.
2026-10-27 03:29:05 +08:00
morluto 4bbd87953f fix(environment): align recorded probe chronology
Use the shared recorded timestamp interpretation for replay ordering, including exact fractions and UTC interpretation of supported zone-less forms. Preserve native-Date fallback for unsupported historical spellings.

Validated environment comparison, state ordering and CLI tests; five new cases reproduce prior drift.
2026-10-27 03:25:06 +08:00
morluto 1008d5e302 fix(asar): retain unsupported integrity declarations
Carry reported algorithms and hashes into listing limitations when the declaration cannot be compared, retaining the existing verification and extraction policies. Bound escaped text before allocation.

Validated ASAR inventory, extraction, cleanup and MCP workflows; prior code omits the declaration details.
2026-10-27 03:25:06 +08:00
morluto 3a65ef0e4c fix(browser): redact scenario cleanup diagnostics 2026-10-27 03:23:50 +08:00
morluto f1535a1753 fix(artifacts): retain archive streams and source receipts 2026-10-27 03:23:50 +08:00
morluto ca75643dbc fix(setup): preserve registration choices and own prewarm 2026-10-27 03:23:50 +08:00
morluto 3ca1cc5466 fix(filesystem): complete plist and limits probes
Read bounded plist format probes and Linux resource-limit reports through positive short reads before classifying their contents. Preserve explicit EOF, report capacity, physical failures and descriptor cleanup ownership.

Validated 66 focused tests and check:fast; nine new fault-injected cases fail against the previous implementation. Linux procfs was not verified on a real Linux host.
2026-10-27 03:18:56 +08:00
morluto 615fbbbdd7 refactor(mcp): consolidate evidence tool execution
Share provider availability, admission, logging and Evidence delivery across browser, Electron and runtime tools. Reuse retained summary delivery for Python and BEAM selected views while preserving preflight and complete parent Evidence.

Validated 47 focused MCP and retained-view acceptance tests and check:fast.
2026-10-27 03:14:24 +08:00
morluto a7004c7df1 fix(filesystem): finish executable and project span reads
Reuse the positional range owner for target metadata, Hopper Mach-O selection and Kuna spans. Preserve physical failures, completed span evidence and cleanup ownership across cancellation and short reads.

Validated 147 focused file and lifecycle tests; ten new cases reproduced failures against the prior implementation.
2026-10-27 03:14:14 +08:00
morluto f68064e46c fix(evidence): exclude sensitive publication identities 2026-10-27 03:12:22 +08:00
morluto 5da50ab3f4 fix(artifacts): retain reader work through cleanup 2026-10-27 03:12:22 +08:00
SII-YDDandgensokyo-dev d4c511ae6e fix(javascript): honor effective symbolic descriptor values (#7222)
Resolve ordered descriptor values at the shared JavaScript fact owner and preserve uncertain intrinsic writes instead of stale exports. Normalize ordinary method values and reuse canonical primitive, prototype, ambient identity and ordering facts across consumers.

384 selected tests, format/static/docs checks, all final CI jobs and reviews pass. Actual Node controls cover 35 bundles; one possibly repeated factory explicitly stays unknown. Shipped Obsidian selected-source complete Evidence matches base/fixed CLI and MCP, with coverage and resource measurements in the PR.

Co-authored-by: gensokyo-dev <[email protected]>
2026-10-27 03:08:00 +08:00
morluto d54f5b07b4 fix(artifacts): complete positional reads across short results
Continue reading Mach-O and Electron ranges after positive short descriptor reads, while preserving explicit EOF, cancellation, file identity and cleanup semantics. Advance fuse scanning and hashing by the bytes actually read.

Validated focused filesystem, Mach-O, dyld, Electron and MCP workflows: 117 tests passed, two Windows-only tests skipped; check:fast passed.
2026-10-27 03:07:24 +08:00
morluto 64f053ab63 chore: remove unused internal exports 2026-10-27 03:02:38 +08:00
morluto 347494f3ab fix(captures): preserve cleanup ownership and sensitive evidence 2026-10-27 03:02:38 +08:00
morluto 7a237aa721 refactor(views): share immutable result readers
Use the common analysis-view reader for Rust and native inventory validation, preserving historical defaults and original Evidence while retaining schema-specific validation.

Validated 123 focused view and service tests and check:fast.
2026-10-27 03:02:09 +08:00
morluto bab49aaf7c refactor(artifacts): unify stable read failure ownership
Centralize completed file observations and cleanup error projection across stable-file providers while preserving each provider’s failure classification and exact cleanup leases. Cover BEAM finalization cancellation and devicetree cleanup retries.

Validated 223 focused lifecycle and MCP tests and check:fast.
2026-10-27 03:02:05 +08:00
morluto 99ff4344c0 fix(browser): centralize CDP network event facts
Parse shared request and response fields at the CDP boundary while preserving capture, session and runtime admission policies. Treat empty request IDs as unknown so unrelated events cannot be joined, and retain redirect occurrences and partial response evidence.

Validate 221 focused tests, fast checks, and real Chrome network and runtime verifiers.
2026-10-27 02:57:58 +08:00
morluto df6db781dd refactor(pyinstaller): centralize CArchive member roles
Derive member roles at the table-of-contents boundary and share declaration and Python-module interpretations across validation, projection, metadata, header reconstruction and nested PYZ traversal. Retain source type codes and generic-file behavior for other printable types.

Validation: 217 focused archive, CLI and MCP tests passed, including mixed-role byte and metadata checks. Typecheck and lint passed.
2026-10-27 02:52:25 +08:00
morluto afc79add7e fix(providers): bind signal and capture configuration per operation 2026-10-27 02:47:25 +08:00
morluto 416f16e2ed fix(managed): unify metadata row and coded-index ownership
Share Field scalar facts and use one coded-index tag registry for widths and decoding. Preserve reserved constructor tags, generic-instantiation restrictions, heap failure order and selected-field signature admission.

Derive native import member kind from the validated MemberForwarded coordinate independently of name lookup, retaining known field or method identity when its display name is unreadable.

Validation: 368 focused source, CLI and MCP tests passed; five lost-kind regressions reproduced on the previous commit. Heap-width, coded-width, reserved-tag and selection boundaries are covered. Typecheck and lint passed.
2026-10-27 02:46:26 +08:00
morluto 741f2de751 test(ci): align source preservation and catalog assertions 2026-10-27 02:35:54 +08:00
morluto b4b5c37ab3 fix(web): retain admitted provenance through incomplete traces 2026-10-27 02:35:54 +08:00
morluto f6478c056f fix(providers): keep each inspection on one environment snapshot 2026-10-27 02:35:54 +08:00
morluto 1f5c5c31c1 refactor(managed): centralize method implementation flag facts
Have the MethodDef row owner derive code type, managed kind and the P/Invoke flag for both body decoding and native-boundary inventory. Remove duplicate masks and unreachable fallbacks while preserving raw flags, zero-RVA precedence and ImplMap-specific declaration policy.

Validation: 280 focused source, CLI and MCP tests passed, including cross-workflow flag combinations and declaration/body policy boundaries. Typecheck and lint passed.
2026-10-27 02:34:59 +08:00
morluto 8ced814b96 fix(browser): unify script and context attribution facts
Centralize CDP script/context declarations and frame-claim interpretation across web capture, Electron, V8, active runtime and storage seeding. Preserve each workflow's admission, provenance and retention policies while removing superseded helpers and schemas.

Malformed or conflicting frame claims now leave passive scripts unattributed. Reused context IDs replace future frame authority without rebinding earlier scripts, and source readers reject empty script IDs.

Validation: 153 focused tests and check:fast passed; 13 regression failures reproduced on the prior commit. Real Chrome script export and runtime CLI/MCP verifiers passed, including correctly reported uncertain cleanup after intentional target closure. Electron regression coverage used simulated CDP.
2026-10-27 02:30:04 +08:00
morluto ff568afa34 refactor(artifacts): retain Node SEA admission facts through inventory
Pass validated blob location and decoded metadata with their admitted descriptor from classification to the reader. Keep root cleanup ownership, freshness checks, format validation order and direct-reader decoding intact, then release borrowed facts after preparation.

Validation: 147 focused source, CLI and MCP tests passed, including real Node blob production; check:fast passed. Optional injected-ELF Docker verification was unavailable because the Docker daemon is not running.
2026-10-27 02:20:00 +08:00
morluto 55f72b5d53 fix(workers): preserve failure receipts and their cleanup owners 2026-10-27 02:16:08 +08:00
morluto 82af5b5f52 fix(web): retain completed file identities across acquisition failures 2026-10-27 02:16:08 +08:00
morluto 620690c923 refactor(firmware): unify extraction problem interpretation
Derive dependency and generic-error facts once per owning task, chunk or multifile stage. Reuse those facts for failure classification and dependency aggregation, preserving unnamed identities, severity precedence and raw diagnostics.

Validation: 70 focused tests passed; 41 platform-guarded tests skipped on macOS. Typecheck and lint passed. Real Unblob/Linux provider behavior was not run.
2026-10-27 02:14:45 +08:00
morluto d06ca092fb refactor(python): consolidate bytecode identity across readers
Share CPython magic framing, version and header facts while keeping standalone marshal coverage and PyInstaller reconstruction eligibility distinct. Preserve legacy headers, opaque unknowns and source bytes.

Validated 272 focused tests across source, CLI and MCP workflows, a 38-test reader recheck, and check:fast.
2026-10-27 02:09:52 +08:00
morluto 669e617eec fix(sqlite): preserve completed member facts on acquisition failure 2026-10-27 02:02:27 +08:00
morluto e813c4c344 fix(pwntools): separate reply admission from debugger enrichment 2026-10-27 02:02:27 +08:00
morluto d77cf61866 fix(evm): retain admitted evidence through reply finalization 2026-10-27 02:02:27 +08:00
morluto 3b4c910fff fix(browser): unify CDP frame and document identity facts
Parse each frame event using its declared protocol fields and retain loader and ancestry only across same-document updates. This removes alias-based attribution, invalidates malformed committed frame facts, and prevents stale WebMCP tools surviving a same-URL document replacement.

Migrate Electron, screenshot, capture, timeline, and execution consumers to the shared facts owner and retire the old helper exports. Validated 428 focused tests, test discovery, typecheck/lint, and real Chrome CLI/MCP network capture; Electron and WebMCP regressions use simulated CDP.
2026-10-27 02:00:58 +08:00
morluto 624ebb2a1e refactor(elf): consolidate bounded string-table interpretation 2026-10-27 01:46:08 +08:00
morluto e405d685bc refactor(macho): centralize dylib command and string facts 2026-10-27 01:38:20 +08:00
gensokyo-devandHarsH 6d5532a204 feat(web): page retained bundle endpoint observations and inferences (#7259)
Page retained web-bundle endpoint observations and inferences through shared CLI/MCP projections, preserving source identities, scoped unknowns, coverage and complete Evidence access.

Based on HarsH’s original implementation in #7213 (6b1d5d42). Real Chromium/CLI/SDK verification and whole-task cost limits are recorded in docs/verification/web-bundle-paging-2026-10-26.md.

Explicit user authorization permits leaving human-readiness declarations unchecked and bypassing reproduced main failures (ADB listing fixture, package catalog expectation, untouched APK verifier formatting). No human-review attestation is made.

Co-authored-by: HarsH <[email protected]>
2026-10-27 01:37:39 +08:00
morluto c09fa01b37 refactor(managed): centralize MemberRef row facts 2026-10-27 01:29:43 +08:00
morluto 939009f6f9 fix(artifacts): preserve completed caller reads through finalization
Retain file identity after Rust, web artifact, shared selected-input and historical capture reads complete before close or cancellation fails. Preserve descriptor-set snapshots in diagnostics alongside sanitized capture observations.

Classify canonical physical capture failures before cleanup composition; source permission errors and bare syscall failures keep their meaning through late cancellation and retry. Keep explicit sensitive-value redaction on retained facts.

Validation: 247 focused cases passed, two Linux-only cases skipped; build, typecheck, lint and formatting passed. Real descriptors with controlled finalization faults establish reader ownership and facts. Worker reply and multi-file database completion semantics remain separate audit work.
2026-10-27 01:27:03 +08:00
morluto 31e8492faf refactor(artifacts): centralize completed snapshot observations
Normalize completed stable-file path, digest and length in one shared observation owner. Migrate thirteen identical provider projections while preserving their selected paths, schema checks and cleanup composition.

Keep specialized metadata and worker reply observations at their existing owners. Validation: shared real-descriptor coverage and provider lifecycle tests passed; build, typecheck, lint and formatting passed.
2026-10-27 01:27:03 +08:00
morluto 1ce9f317be refactor(pyinstaller): centralize PYZ member role ownership 2026-10-27 01:21:11 +08:00
morluto 662ec2e450 fix(verifiers): preserve source Evidence through shared admission 2026-10-27 01:11:51 +08:00
morluto f6805e29c3 refactor(pe): centralize resource tree facts across readers 2026-10-27 01:02:21 +08:00
morluto 9d2c087d83 fix(readers): preserve completed snapshots through cleanup retries
Retain exact completed file path, digest and length in Python pyc and Erlang BEAM failure observations. Translate Python close failures at the provider boundary and bind them to the retained descriptor cleanup leases so later successful retries resolve prior errors.

Exercise actual services and file handles with completed reads, cancellation during finalization and physical read failure followed by cancellation. Assert no fabricated snapshot on early failure and resolution of every prior cleanup report.

Validation: six focused descriptor regressions plus existing filesystem, parser and MCP coverage passed; build and static checks passed. Authored format fixtures establish portable reader behavior, not CPython/Erlang runtime execution.
2026-10-27 01:01:19 +08:00
morluto 74d5cab372 fix(native): account export and version text in UTF-8 bytes
Count retained UTF-8 text rather than UTF-16 code units, including exact-byte base64 spellings. Bound the decoded version-name map independently from output expansion, releasing replaced entries from its live storage charge.

Add real service/provider boundary controls for accepted and over-budget symbol names, version names and unused version metadata. Existing per-string bounds remain the allocation owner.

Validation: native parser, filesystem and CLI/MCP views passed; build, typecheck, lint and formatting passed. No serialized-report size cap, OOM reproduction or performance improvement is claimed.
2026-10-27 01:01:10 +08:00
morluto 3d74c480e0 fix(verifiers): consolidate Ghidra MCP result admission 2026-10-27 00:49:27 +08:00
morluto 60b55be7e7 fix(native): bound PE export names before intermediate retention
Charge PE alias names when storing them, before target interpretation can suppress output rows. Validate unused-slot names as bytes without retaining or decoding strings. Keep row and forwarder accounting separate so names are charged once.

Preserve alias ordering, malformed unused-name issues and ordinal-only suppression. Add production service/provider retention regressions using compact shared-pointer fixtures.

Validation: 43 focused parser, filesystem, CLI and stdio MCP tests; fresh build, typecheck, lint and formatting passed. Three bounded Node 24.22.1 measurement pairs showed median process RSS high-water 154000 to 93168 KiB; timing and sampled family memory have cache/startup/sampling limits. No Windows loader acceptance or OOM reproduction claim.
2026-10-27 00:40:35 +08:00
morluto 0973649cec refactor(captures): consolidate recorded exchange body facts 2026-10-27 00:38:06 +08:00
morluto c2b0c955cd fix(workers): reject malformed UTF-8 in JSON result files 2026-10-27 00:28:42 +08:00
morluto 1d32eb3aa4 fix(providers): keep operation environments consistent through launch
Capture one environment snapshot per admitted WABT, JADX, firmware and Wakaru operation. Use it for tool selection, session identity and child launches while preserving configuration updates between operations.

Regressions reproduce changed JVM options under an old session key, mixed engine/limiter selection and environment drift between subprocesses. Keep selected-wrapper and cleanup coverage in a shared fixture with separate scenarios.

Validation: 115 distinct focused tests passed across the final five-file selection, including seven selected-command cases after refactoring. Fresh build, typecheck, lint and formatting passed. Tests use real subprocess fixtures; no real JADX/WABT/Binwalk/Wakaru engine or native Windows verification is claimed.
2026-10-27 00:25:43 +08:00
morluto 2cdc8cb92e refactor(managed): centralize TypeDef row facts across consumers 2026-10-27 00:21:00 +08:00
morluto 8423c5c6d0 refactor(macho): consolidate prefix interpretation across readers 2026-10-27 00:13:36 +08:00
gensokyo-dev 5508b2affd fix(analysis): retain source evidence through directory exports (#7250)
Retain authenticated provider source records independently of snapshot replay eligibility and return them explicitly to plain application callers, including late cancellation. CLI/MCP commit source records before delivering links; function dossiers share the canonical Evidence constructor. Keep directory replies compact and preserve successful retry verification.

Real Ghidra CLI/MCP investigations verify 652-function ls, historical report provenance, cross-session resume, unchanged source/project content and complete offline Evidence. Final full 14,338-function libcrypto comparison matches all metadata/pseudocode facts and independent fresh-import observations: 14,194 written, 143 external, one recorded deadline; 494.03 s, 445.63 CPU s, 1.584 GiB sampled family peak under 2-CPU/8-GiB guards, with no OOM or cleanup failure.

144 focused tests and static/docs/site checks pass; latest-main integration passes 289 cases plus typecheck/lint and actual CLI snapshot source retrieval. Final CI source/static/build/docs/quality/package checks pass. Four ADB shard failures also occur in main CI 40349559391; cancelled coverage remains unverified and author declarations stay unchecked under the user's explicit bypass authorization. No human-review attestation is made.
2026-10-26 23:56:03 +08:00
morluto 13fba271e6 refactor(plist): centralize binary layout and offset ownership 2026-10-26 23:51:33 +08:00
morluto d27e3f8e89 fix(native): preserve import names across dyld text formats
Parse literal import framing and retain ambiguous boundaries. Consult undefined symbol-table names only when needed to distinguish producer spacing, with import and export captures owned separately.

Validation: 59 native tests passed and real Apple tools on inert arm64 fixtures reproduced the old loss and verified the fix. Fresh build, typecheck and lint passed. Small warm-task median elapsed was unchanged; cold costs and agent tokens were not measured.
2026-10-26 23:50:10 +08:00
morluto ab44487ff8 fix(adb): preserve process names and pin fixture interpreters
Consume Toybox’s single NAME separator without stripping source whitespace. Keep MCP subprocess fixtures independent of ambient PATH and validate calls through the existing named-schema helper.

Validation: focused ADB provider/parser and MCP tests passed; no real Android device was used.
2026-10-26 23:50:10 +08:00
YingNingandgensokyo-dev 4da7e46e9c fix(provider-selection): name whether a rejection came from availability or target support (#7220)
Distinguish local provider readiness failures from target-support refusals in shared CLI/MCP diagnostics, preserving original codes and evidence. Avoid claiming configured providers are unconfigured.

120 focused tests, static/docs checks and real ELF CLI/MCP verification pass. Hosted CI has the same four ADB failures as exact main d9908fd7a; three canceled shards remain unverified. Merge uses the user's explicit authorization to bypass proven main failures. Fresh Codex and Greptile reviews are clear.

Fixes #7214

Co-authored-by: gensokyo-dev <[email protected]>
2026-10-26 23:49:11 +08:00
morluto a5ad164283 fix(browser): unify worker target admission across capture workflows 2026-10-26 23:44:08 +08:00
morluto cadd5b75b3 refactor(elf): consolidate header and section-name interpretation 2026-10-26 23:40:04 +08:00
morluto 81add3d23d refactor(pyinstaller): centralize PYZ header and version facts 2026-10-26 23:33:54 +08:00
morluto d9908fd7a4 refactor(ghidra): consolidate function and inventory admission 2026-10-26 23:27:43 +08:00
morluto b45a83368c fix(ci): preserve lint diagnostics without source locations 2026-10-26 23:21:57 +08:00
morluto 65819e8cc5 fix(pcap): capture provider environment once 2026-10-26 23:21:57 +08:00
morluto a7aad91320 fix(firmware): preserve report metadata before semantic validation 2026-10-26 23:21:57 +08:00
Wenyu Chiou 625e32744e fix(javascript): ignore async exec stdio options (#7087)
Respect Node asynchronous exec/execFile method-default stdio instead of interpreting an ignored option. Remove the superseded callback-shape query while preserving caller input, Evidence, uncertainty, argv and fork controls. Independently validated on current main through actual Node pipe capture, matching real-app CLI/MCP facts, 81 targeted tests and static checking. Closes #7217.
2026-10-26 23:16:28 +08:00
morluto a84e0cfcfe refactor(verifiers): consolidate capture result admission and parity 2026-10-26 23:12:34 +08:00
morluto 99062feaa7 test: bind MCP validation and cleanup faults to selected inputs 2026-10-26 23:06:36 +08:00
morluto fef76a02fb fix(apple): preserve own artifact metadata through schema projection 2026-10-26 23:06:36 +08:00
morluto b7e76a57cd fix(adb): preserve selected environment through provider execution 2026-10-26 23:06:36 +08:00
morluto a30ffd27a6 refactor(dos): centralize MZ admission and load-image evidence 2026-10-26 23:02:53 +08:00
Dery Ferdikaandgensokyo-dev aa6c1d49fd fix(mcp)!: require allowed hosts for non-loopback HTTP binds (#7172)
Require operator-declared hostnames before serving MCP HTTP outside loopback, preserving Host/Origin gates. Parse hostname identities once, share typed startup recovery across CLI/MCP, and announce an admitted client URL. Generated AgentEnv bundles declare their client/service names and validate DNS/HTTP name identity before export.

Validated through real packaged HTTP requests, direct SDK connection, compiled export refusals, 184 focused tests, static and documentation checks on the refreshed main integration. Final CI and review evidence is recorded in the PR body.

BREAKING CHANGE: non-loopback deployments require MCP_ALLOWED_HOSTS or rea mcp --http --allowed-hosts; bundle names must be unchanged HTTP hostnames within the 63-octet DNS label bound.

Co-authored-by: gensokyo-dev <[email protected]>
2026-10-26 22:55:10 +08:00
morluto 0828917c79 refactor(verifiers): centralize named artifact output admission
Route six artifact verifiers through the canonical complete MCP output and error parsers. Remove duplicated JSON/schema interpretation while retaining Evidence authentication, provider provenance, independent CLI parsing and CLI/MCP parity checks. Validate malformed success/error envelopes at the shared boundary.

Validation: 11 boundary tests, Go compiler verifier (30 cases), and synthetic IL2CPP/Flutter verifier workflows passed; typecheck/lint passed. PCAP readiness passed with stub tools, but capture inspection is unsupported on macOS. Linux-only binary-layout and EVM verifier toolchains were unavailable.
2026-10-26 22:48:22 +08:00
morluto f02df45a46 fix(process): preserve selected Darwin helper environment through execution 2026-10-26 22:42:48 +08:00
morluto 921142efbc fix(hopper): own profile reads and retain completed coordinate evidence 2026-10-26 22:42:48 +08:00
morluto 5b983516ab fix(hopper): consolidate release metadata and archive identity ownership
Share release JSON decoding, vendor fields and package integrity checks across macOS setup and Linux publishing/cache reads. Reject malformed UTF-8 instead of repairing macOS metadata, retain established initial BOM tolerance, and preserve complete vendor rows through admission and cache retention. Keep platform channel, signature, installation and cleanup policies unchanged.

Validation: 134 focused tests passed with 21 platform skips; typecheck and lint passed. Linux-only signed-cache cases were updated but skipped on the macOS host. No live Hopper installation performed.
2026-10-26 22:38:53 +08:00
19447516a4 feat(ghidra): reuse existing projects through verified copies (#7236)
Reuse selected Ghidra projects through verified private read-only copies, with shared CLI/MCP configuration, committed project identity, import-SHA verification and explicit historical/unknown analysis semantics. Preserve exact cancellation and platform capability boundaries.

Actual Ghidra 12.1.4/JDK21 Linux CLI/MCP investigations matched fresh import on 652-function ls and 14,338-function libcrypto inventories and pseudocode; authenticated complete Evidence, mismatch/staleness refusal, saved historical reports and shared /tmp projects verified. 958 focused tests passed with four platform skips; build/static/docs/format passed. Real macOS/Windows reuse remains unverified; Windows reuse is unavailable. See PR verification for measured whole-task costs and final CI attribution.

Co-authored-by: akushonkamen <[email protected]>
Co-authored-by: morluto <[email protected]>
2026-10-26 22:31:13 +08:00
morluto 49816235f5 refactor(providers): consolidate IDA and JEB MCP result admission
Give the object-structured provider envelope one owner that retains original dictionary members. Remove duplicate schemas, text extraction and JSON-or-text decoding plus the downstream casts that repaired lossy record clones. Keep provider-specific error, truncation, wrapper and payload-selection policies in each adapter.

Validation: IDA/JEB compatibility tests, pinned SDK transport tests and provider regressions passed; check:fast passed. Real IDA/JEB applications were not exercised.
2026-10-26 22:30:58 +08:00
morluto 2b84efe0da fix(contracts): align CLI omission and native capability coverage 2026-10-26 22:22:27 +08:00
morluto 4a8205456c fix(lifecycle): preserve source failures and native workspace diagnostics 2026-10-26 22:22:27 +08:00
morluto 77468d5043 fix(providers): preserve launch selections and refresh obsolete Kuna exports 2026-10-26 22:22:27 +08:00
morluto eb06e263cb fix(plist): own dictionary decoding across Apple workflows
Preserve XML, OpenStep and binary dictionary keys at their format owners. Share validated binary reference facts with materialization while retaining scalar precision, UID identity and bounded expansion. Migrate archive, Interface Builder, native and app-bundle consumers, then remove obsolete key-omission accounting and schema copies.

Validated source regressions and compiled CLI/MCP workflows, including native plist fidelity and archive identity. Typecheck, lint, docs and test discovery pass. No performance benchmarks were required.
2026-10-26 22:20:00 +08:00
morluto 3b26d7f80b docs(security): add fallback reporting email 2026-10-26 22:18:00 +08:00
morluto cd68181c90 fix(providers): preserve report bytes and dictionary facts 2026-10-26 22:01:51 +08:00
pablodiazjorge 4557791a2e fix(contracts): advertise a v-mode portable Windows absolute-path pattern (#7152)
Use portable hex escapes for Windows absolute-path separators in the shared local path schema. Preserve host path semantics and caller spelling; validate Annex B/u/v compilation and Node win32 agreement. Closes #7151.
2026-10-26 20:56:47 +08:00
Nianze Wu 63417bf3f0 fix(contracts): publish portable browser request patterns (#7185)
Publish portable browser request-path and HTTP-token patterns at their canonical owner, preserving input spelling and existing policy. Independently reproduced and verified through actual full-profile stdio MCP catalog and domain/MCP regressions on latest main. Closes #7168.
2026-10-26 20:52:19 +08:00
Wenyu Chiou 1a91630b88 fix(setup): preserve Linux Hopper HTTP failure status (#7183)
Preserve observed failed-response HTTP status through Linux Hopper setup diagnostics without changing download rejection, retry, or installation policy. Independently validated on latest main through red/green regressions, actual TLS sockets, focused checks and CI. Closes #7182.
2026-10-26 20:45:06 +08:00
김준호 0211a07ec5 fix(session): report a missing Linux ps as provider unavailability in status (#7180)
Keep binary_session available when Linux owned-child probes lack procps ps. Preserve typed ownership-prerequisite failures in direct execution and report per-provider readiness in status. Independently validated on latest main through real stdio MCP plus focused regression, static and documentation checks. Closes #7164.
2026-10-26 20:35:09 +08:00
gensokyo-devandtijo95 e3066c1705 fix(deps): upgrade zod without narrowing timestamp inputs
Upgrade Zod to 4.6.6 and refresh locked license inventories. Preserve REA's established timestamp spellings through one shared UTC/offset grammar, including completion-verifier consumers. Size the stdin resource regression from the child's actual heap capacity.

Fixes #7173
Includes the dependency and inventory changes proposed in #7184.

Co-authored-by: tijo95 <[email protected]>
2026-10-26 20:13:12 +08:00
b675073b73 feat(mcp): scope advertised tools by family (#7228)
Scope MCP discovery, setup, doctor, status and recovery guidance by selected tool families using one contract inventory and shared catalog identity. Preserve default discovery, canonical validation and complete Evidence.

Fixes #3246. Builds on #3026. Real libcrypto workflow verifies all 5,367 exports and identical complete Evidence. Static/source/build/docs/Windows/package/Greptile checks pass; ten assertions reproduced identically on clean main and feature across Node 24/22, with cancelled coverage explicitly unverified. Merge uses the user-authorized bypass for baseline failures and unchecked human-readiness declarations, without attesting human review.

Co-authored-by: 0xLLVEL <[email protected]>
Co-authored-by: Codebuff <[email protected]>
Co-authored-by: Claude Opus 4.6 <[email protected]>
2026-10-26 19:50:12 +08:00
morluto e54bf1006e fix(environment): own JSON fields across capture, state and replay 2026-10-26 18:27:30 +08:00
morluto cc5a96933a test(mcp): validate selected tools without compiling the entire catalog 2026-10-26 18:26:33 +08:00
morluto dec4cf50cb fix(captures): preserve reply failures through descriptor finalization 2026-10-26 18:26:26 +08:00
morluto 68f2d67c56 fix(paths): preserve native selections through analysis and removal 2026-10-26 18:26:17 +08:00
morluto e7f986c994 fix(json): consolidate dictionary admission across analysis and verification 2026-10-26 18:14:50 +08:00
morluto c06dc2d356 fix(native): retain completed debugger evidence through reply cleanup 2026-10-26 18:12:01 +08:00
morluto f8161ad7b3 fix(setup): preserve selected home paths throughout skill installation 2026-10-26 18:11:54 +08:00
morluto 4ca3032e7f fix(json): preserve configuration and capture data at admission 2026-10-26 18:06:00 +08:00
gensokyo-devandAkshad Jogi 9085222ca1 feat(mcp): support compact output schema advertisements (#7218)
Add opt-in bounded output-schema advertisements while preserving the full default, canonical server validation and complete structured content. Verify real stdio discovery under a 1 MiB receive cap, all 5,367 libcrypto export coordinates, complete Evidence parity, whole-task serialization estimates and resource bounds.

Fixes #2512. Integrates #7054 by Akshad Jogi with original authorship retained.

Co-authored-by: Akshad Jogi <[email protected]>
2026-10-26 17:59:51 +08:00
morluto e596a48251 fix(sqlite): retain completed evidence through reply finalization 2026-10-26 17:56:47 +08:00
morluto f2b4468183 fix(paths): preserve native selections across launch and publication 2026-10-26 17:56:47 +08:00
morluto da526fc014 fix(providers): preserve reply bytes and structured JSON ownership
Share strict UTF-8 JSON admission across bounded worker reports. Preserve IDA/JEB structured dictionaries through negotiated SDK validation and adapter decoding, retaining inherited tool validation and protocol handling.

Validated 240 tests, including real SDK/HTTP exchanges and firmware file reads; 83 host-specific cases skipped. Typecheck, lint, and discovery passed. Real-provider execution and modern MCP protocol behavior remain unverified.
2026-10-26 17:52:32 +08:00
morluto 721dd3d1aa fix(javascript): preserve selected inputs and completed source evidence 2026-10-26 17:39:29 +08:00
morluto 3864c7d667 fix(config): preserve captured environments across provider launches 2026-10-26 17:39:23 +08:00
morluto ccdeda7a27 fix(inputs): centralize admission and preserve caller selections
Move shared request parsing into its application owner, migrate all consumers, and remove six duplicated admission blocks while preserving cancellation and validation policy.

Keep operator-selected JavaScript paths in verifier parent Evidence while retaining canonical comparison guards. Validated with 204 passing tests, 3 Linux-x64-only skips, typecheck, lint, and test discovery.
2026-10-26 17:35:18 +08:00
morluto 7d36d172ab fix(evidence): consolidate retained view and verifier ownership 2026-10-26 17:26:55 +08:00
morluto e2d4c0dabb fix(process): preserve environment snapshots and final cancellation 2026-10-26 17:26:16 +08:00
morluto eecadf35b2 fix(verification): unify client schema traversal and reference checks
Share ordered schema positions between Gemini literal checks, DeepSeek regex
checks, and catalog array-shape checks. Reuse the canonical keyword and local
reference owners; keep unreferenced instance literals opaque and unresolved
external or dynamic targets unknown.

Gemini and catalog checks now catch constraints in schemas reached through
references into examples. Preserve source paths, resource scopes, cycles and
regex occurrence counts. Align fixture first-error selection with the native
Gemini check and use consistent bracketed array locations in diagnostics.

Validation: 55 focused tests passed, including full/compact MCP catalogs;
typecheck, lint, discovery, and Node source-loader checks passed. The old
Gemini scanner reproduced the local-reference false negative. Installed
Gemini and DeepSeek clients were not run.
2026-10-26 17:15:58 +08:00
morluto 86ff3237e1 fix(paths): preserve selected command, output, and source identities 2026-10-26 17:14:02 +08:00
Kaoru0822andClaude Opus 5.5 18dc78fa43 revert(mcp): undo input schema fitting that broke main CI (#7135)
This reverts commit 7336a566ac (#7119).

After #7120 and the other merges of 2026-10-25 grew inspect_analysis_view's
complete input schema (71,762 bytes plus 3,003 bytes of examples), the 64 KiB
fitting from #7119 could no longer keep instance level 3 and fell back to
level 2. That summarized the inline Evidence source and the view selector
collections, so main CI failed in toolInputReferences, toolSchemaValidity,
analysisViewMcp, absoluteLocalPathSchema, javascriptFactsViewMcp and
godotPckMcp. #7119's own CI ran against bc143125e, before that growth.

Reverting restores the complete full-profile advertisement while the size
limit is redesigned against these contracts (#4081 stays open).

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-26 02:06:57 -07:00
morluto 6c35d74a7d refactor: centralize input validation and schema interpretation
Migrate twelve application service files to the existing provider input owner,
preserving schema causes, cancellation precedence, caller selection, and
retained Evidence. Keep flows with distinct error policies separate.

Replace the DeepSeek verifier's recursive object scan with schema-position
traversal using the canonical keyword and local-reference owners. Literal
pattern fields no longer cause false regex failures; patternProperties keys
are checked alongside actual pattern constraints in native regex modes.

Validation: 271 focused workflow/service tests and six verifier regressions
passed; static checks, discovery, and actual Node source-loader smoke passed.
The baseline scanner reproduced the new regressions. Installed DeepSeek client
execution was not run.
2026-10-26 17:05:05 +08:00
morluto 8a65787507 refactor: consolidate parsed image ownership across consumers
Retain validated DOS relocation records in their domain owner and consume
those facts in Ghidra load-image preparation. Preserve ordered duplicate
aliases, overlapping writes, and original/final byte evidence.

Share FAT header facts across generic, native, and Hopper readers without
merging their distinct admission policies. Replace managed method bodies'
two identical mapping lookups with one owned offset-and-extent result.

Validation: 660 focused tests passed across 19 suites; typecheck, lint, and
test discovery passed. Hopper/Ghidra checks used fixtures and simulated
provider observations; no real external provider execution was performed.
2026-10-26 16:56:50 +08:00
morluto a7f50e3386 refactor: consolidate ELF and CLR record interpretation
Share ELF note headers across note inspection, Flutter build IDs, Node SEA,
and recorded-crash source binding. Decode REL/RELA records once for MIPS
shared-load analysis and IL2CPP linking, removing superseded scalar helpers.

Share the CLR header prefix between desktop detection and managed PE parsing
while preserving their distinct read bounds and admission policies. Cover
those differences and ignored R_NONE fields with production-boundary tests.

Validation: 493 focused tests passed, two existing skips; static checks and
test discovery passed. Public managed/desktop CLI and ELF notes CLI/MCP
workflows exercised; no real external analysis providers were run.
2026-10-26 16:50:08 +08:00
morluto eb51eb6915 fix(process): preserve selected launch paths and completed command output 2026-10-26 16:46:53 +08:00
morluto 86e7cd9b48 fix(verification): keep runtime packaging and boundary fixtures complete 2026-10-26 16:46:53 +08:00
morluto 09acf217d9 fix(evidence): preserve source and authenticated observation identity 2026-10-26 16:46:45 +08:00
morluto 6336fe5bb5 refactor: consolidate binary analysis admission and format ownership
Reuse the existing provider input admission helper across twelve services,
preserving cancellation, schema errors, path policies, and Evidence handling.
Share ELF dynamic records across Dart, MIPS, and IL2CPP readers, and reuse
Mach-O segment and section records for Node SEA location.

Require canonical DOS ownership before GPU PE section discovery so relocation
bytes cannot masquerade as a PE pointer. Cover valid short DOS headers and
the malformed case through both inventory and the public CLI.
2026-10-26 16:44:21 +08:00
morluto 7adfcc4467 fix: unify executable format ownership across artifact readers
Centralize ELF section and Godot trailer declarations while preserving caller admission policies. Route six PE entrypoints through canonical DOS ownership so relocation data cannot establish a PE header, and update NativeAOT interpretation identity and verifier dependency coverage.
2026-10-26 16:32:49 +08:00
morluto 9341b116ef refactor: consolidate binary artifact interpretation and fixture ownership
Share exact ELF program-header facts across nine consumers while preserving their admission and precision policies. Centralize conformance path and digest binding, retain verifier-specific checks, and route fixture-owner changes to CI qualification.
2026-10-26 16:21:11 +08:00
morluto 23bc51d5c4 test(package): migrate skill metadata fixture to frontmatter 2026-10-26 16:15:08 +08:00
morluto 28222089f4 refactor(zip): share end-of-directory declarations 2026-10-26 16:14:58 +08:00
morluto 91bacc5c14 refactor(config): share bounded decimal setting validation 2026-10-26 16:14:49 +08:00
morluto 2411d60b5a fix(verification): unify complete MCP catalog checks 2026-10-26 16:11:09 +08:00
morluto 74f9870b7a refactor(macho): share FAT evidence interpretation 2026-10-26 16:10:57 +08:00
morluto 90199ff0ec refactor(elf): centralize symbol record decoding 2026-10-26 16:10:46 +08:00
morluto 3d565fa42a refactor(windows): share native artifact validation 2026-10-26 16:04:44 +08:00
morluto 729684af9a refactor(png): share IHDR interpretation 2026-10-26 16:04:33 +08:00
morluto 92bdb4443f refactor(pe): share CodeView range interpretation 2026-10-26 16:04:18 +08:00
morluto a7d71f47ee refactor(elf): centralize raw table declarations 2026-10-26 16:04:09 +08:00
morluto c994870f6b fix(inspector): route file locations through URL semantics 2026-10-26 16:02:14 +08:00
morluto 1a1121c61f fix(android): retain selected Java across workspace launches 2026-10-26 16:02:14 +08:00
morluto ce5373e42f test(providers): verify malformed HTTP acquisition recovery 2026-10-26 16:02:14 +08:00
morluto 38ed5a3142 fix(browser): invalidate ASAR headers on file state changes 2026-10-26 16:02:14 +08:00
morluto 258a1dd3b7 fix(skills): parse metadata only from YAML frontmatter 2026-10-26 15:49:58 +08:00
morluto 0f64f1f855 refactor(macho): share bounded header declarations 2026-10-26 15:49:52 +08:00
morluto f8c5a6cc10 fix(providers): reject malformed UTF-8 before MCP decoding 2026-10-26 15:41:03 +08:00
morluto c849a97d2a fix(setup): preserve explicit Codex home traversal 2026-10-26 15:41:03 +08:00
morluto 628087a2db fix(process): preserve platform-aware environment precedence 2026-10-26 15:41:03 +08:00
morluto 554816becc refactor(verification): centralize native client result discovery 2026-10-26 15:40:34 +08:00
morluto 981d806924 refactor(macho): centralize segment and section decoding 2026-10-26 15:40:10 +08:00
morluto f4cc6c85ef refactor(verification): share native client fixture assertions 2026-10-26 15:34:11 +08:00
morluto 0929751798 refactor(macho): reuse canonical header declarations 2026-10-26 15:34:04 +08:00
morluto 36515a5c9b refactor(cli): derive desktop limits from the input contract 2026-10-26 15:33:59 +08:00
morluto deed39f7c4 refactor(pe): share optional-header facts across readers 2026-10-26 15:27:35 +08:00
morluto 971b48f702 refactor(mcp): reuse desktop retained-summary handler 2026-10-26 15:27:29 +08:00
morluto b9ee8fe057 refactor(pe): centralize fixed COFF header decoding 2026-10-26 15:20:02 +08:00
morluto 99d4ea9c05 fix(captures): preserve relative redirect URL evidence 2026-10-26 15:15:00 +08:00
morluto 2012efe5fd fix(artifacts): retain source identity failures after relocation 2026-10-26 15:15:00 +08:00
morluto 0c147a4a70 fix(snapshot): preserve authenticated replay provenance 2026-10-26 15:15:00 +08:00
morluto f4987281fa fix(setup): forward supported tool and runtime selections 2026-10-26 15:15:00 +08:00
morluto 53779b59bf refactor(pe): reuse canonical section header facts 2026-10-26 15:14:10 +08:00
morluto 943ae604df refactor(reference): derive import lookup from reader inventory 2026-10-26 15:14:05 +08:00
morluto 44b716f2f2 fix(runtime): preserve exact selected target identities 2026-10-26 15:09:04 +08:00
morluto d9e12c957b refactor(firmware): centralize extraction depth ceiling 2026-10-26 15:08:56 +08:00
morluto 90b793e0f4 docs: scope performance measurements to concrete needs 2026-10-26 15:04:08 +08:00
morluto b3818bbba2 refactor(il2cpp): share generic container byte decoding 2026-10-26 15:00:41 +08:00
morluto 9e4702b7a7 fix(sqlite): preserve schema-owned input issue paths 2026-10-26 15:00:30 +08:00
morluto 23d56e1abd refactor(config): use canonical resource limit constants 2026-10-26 15:00:23 +08:00
morluto 85075fa979 refactor(asar): let listing own parsed member facts 2026-10-26 14:53:42 +08:00
morluto 7cd65278ea refactor(config): remove legacy provider timeout re-exports 2026-10-26 14:53:20 +08:00
morluto 7404847f64 fix(update): bound and own metadata probe lifetimes 2026-10-26 14:49:10 +08:00
morluto bf876efbaa fix(kuna): bind cached export reads to verified file state 2026-10-26 14:49:10 +08:00
morluto b654ac8b13 fix(setup): retain auxiliary tool selections in registrations 2026-10-26 14:49:10 +08:00
morluto a116d32cdc chore: sync main before publishing cleanup migrations 2026-10-26 14:48:30 +08:00
morluto 8fe3b17ea7 refactor(tooling): move completion ledger generation out of domain 2026-10-26 14:46:35 +08:00
morluto 42f3ed7119 refactor(tooling): move verifier host helpers out of runtime 2026-10-26 14:46:23 +08:00
morluto d3946a3f50 fix(apk): reuse canonical subject identity in retained views 2026-10-26 14:46:14 +08:00
morluto 975900f565 docs(setup): state the FreeBuff minimum release (#7066) 2026-10-26 14:45:43 +08:00
morluto 2be39ef47a refactor(model): let format parsers own tensor range validation 2026-10-26 14:33:35 +08:00
morluto f4768eb5d3 fix(wasm): reuse canonical subject identity admission 2026-10-26 14:33:27 +08:00
morluto 06655af306 refactor(evidence): centralize publication source collection 2026-10-26 14:25:50 +08:00
morluto 8982f40d00 refactor(godot): share PCK header facts across inspections 2026-10-26 14:25:44 +08:00
morluto d7d9457661 refactor(gpu): reuse canonical ELF note layout 2026-10-26 14:25:34 +08:00
morluto ed7b3a2a10 refactor(macho): share header declarations across native readers 2026-10-26 14:20:01 +08:00
morluto c11fb48c98 fix(electron): share wrapped member identity parsing 2026-10-26 14:19:55 +08:00
morluto 83a6cdb859 fix(paths): preserve native traversal and unset PATH semantics 2026-10-26 14:17:27 +08:00
morluto 1fe1f1f8d6 refactor(managed): retire redundant verification facade 2026-10-26 14:14:26 +08:00
morluto 5a332d856a refactor(browser): resolve Link metadata identity once 2026-10-26 14:14:22 +08:00
morluto 38ebe11a52 refactor(pe): centralize section header interpretation 2026-10-26 14:14:18 +08:00
morluto ba7ac7908d refactor(elf): carry parsed header facts into native consumers 2026-10-26 14:08:35 +08:00
morluto 9fa5a87c73 refactor(javascript): remove obsolete Electron classifier facade 2026-10-26 14:08:34 +08:00
morluto 6362ac6b72 refactor(web): reuse shared provider input admission 2026-10-26 14:08:30 +08:00
morluto fab78501c7 refactor(services): centralize direct provider input admission 2026-10-26 14:04:57 +08:00
morluto 5c4a6c467c refactor(pe): share optional header image and alignment facts 2026-10-26 14:04:45 +08:00
morluto 72fa15a509 refactor(snapshots): derive workflow entries from canonical schema 2026-10-26 14:04:39 +08:00
morluto 52976ac5cd fix(doctor): bound and cancel diagnostic subprocesses 2026-10-26 14:02:55 +08:00
morluto fa6112b29f fix(providers): preserve native traversal in installation paths 2026-10-26 14:02:55 +08:00
morluto 6819cc5b8d fix(setup): preserve explicit configuration independently of readiness 2026-10-26 14:02:55 +08:00
morluto f6d9f1b05c refactor(browser): reuse worker URL exclusion classification 2026-10-26 13:59:22 +08:00
morluto 5e581fd3b1 refactor(workflows): share source caveat grouping identity 2026-10-26 13:59:11 +08:00
morluto 7439d0d5d8 refactor(artifacts): reuse Mach-O framing for Node SEA discovery 2026-10-26 13:59:04 +08:00
morluto 071a637a9f refactor(cli): share managed comparison finalization 2026-10-26 13:53:43 +08:00
morluto 634e566c09 refactor(capabilities): remove inert client requirement facade 2026-10-26 13:53:35 +08:00
morluto 941838fdd9 refactor(elf): centralize fixed header type and flags decoding 2026-10-26 13:53:25 +08:00
morluto 991a240606 fix(cli): preserve script export evidence through shared finalization 2026-10-26 13:47:15 +08:00
morluto 7ccecbcb00 refactor(cdp): centralize execution context identity parsing 2026-10-26 13:47:04 +08:00
morluto 60daca427d refactor(reconstruction): share canonical original cases across evaluation 2026-10-26 13:46:57 +08:00
morluto 1173c0a9dc test(setup): align launch fixtures with runtime and host requirements 2026-10-26 03:20:13 +08:00
morluto db4d3abcda fix(setup): retain bounded startup diagnostics without losing failures 2026-10-26 02:33:31 +08:00
morluto 37a7c2d652 fix(doctor): retain host uncertainty and resolved Java selection 2026-10-26 02:33:31 +08:00
morluto 82fe820060 fix(setup): retain explicitly selected ILSpy paths 2026-10-26 02:33:31 +08:00
morluto 14dd18a7fd fix(setup): preserve native configuration path traversal 2026-10-26 02:33:31 +08:00
morluto d69a54d592 fix(setup): register explicit MCP runtimes and preserve launcher identity 2026-10-26 02:33:31 +08:00
b8ce3c74cd feat(native): make function matching allowance configurable (#7159)
Allow native matching representation budgets to be selected at runtime or launch while retaining the 96 MiB default and main shared-provenance accounting. Preserve actionable configuration recovery through the setting owner and verify complete CLI/MCP matches for 20k/50k synthetic inventories and real stripped LLVM 14/15 inventories.

Fixes #6406.

Based on #7112 by Kaoru0822; original authorship and co-author credit retained.

Co-authored-by: Kaoru0822 <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-26 02:22:32 +08:00
morluto a174892e3e fix(website): improve layout and clarify homepage copy 2026-10-26 01:33:31 +08:00
morluto ff6d96bce8 fix(website): surface RL environments and adjust homepage navigation 2026-10-26 00:51:30 +08:00
morluto a0ddb83e45 docs(environments): lead with agent prompts 2026-10-26 00:40:56 +08:00
morluto 14631f698d docs(website): showcase RL environment workflows 2026-10-26 00:33:55 +08:00
gensokyo-dev 4c8647a706 feat(native): page retained observations and source context (#7146)
* feat(native): page retained observations and source context

* docs(native): record complete observation workflow measurements

* fix(native): preserve context coverage and overflow recovery

* fix(benchmarks): finalize partially initialized observation probes

* docs(native): publish reviewed workflow verification
2026-10-26 00:32:01 +08:00
morluto 447907f280 docs(environments): explain building RL environments with REA 2026-10-26 00:16:52 +08:00
Kaoru0822andClaude Opus 5.5 5d42839099 revert(mcp): undo input schema fitting that broke main CI
This reverts commit 7336a566ac (#7119).

After #7120 and the other merges of 2026-10-25 grew inspect_analysis_view's
complete input schema (71,762 bytes plus 3,003 bytes of examples), the 64 KiB
fitting from #7119 could no longer keep instance level 3 and fell back to
level 2. That summarized the inline Evidence source and the view selector
collections, so main CI failed in toolInputReferences, toolSchemaValidity,
analysisViewMcp, absoluteLocalPathSchema, javascriptFactsViewMcp and
godotPckMcp. #7119's own CI ran against bc143125e, before that growth.

Reverting restores the complete full-profile advertisement while the size
limit is redesigned against these contracts (#4081 stays open).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Refs: #7135
2026-10-25 23:50:18 +08:00
gensokyo-dev 6381aa0b1d fix(ghidra): scope rejected-input diagnostics to their request (#7140)
Scope expected Ghidra input failures to the active request after the authenticated startup handshake. Preserve operation output, identity and log coordinates while retaining cumulative output with its session owner; lifecycle and unexpected failures keep full diagnostics.

Share remote input-code classification with domain error projection. Verify startup rejection, healthy continuation, cleanup, unknown errors, OOM diagnostics and real CLI/MCP workflows. The measured unknown-symbol reply falls from 13,170 to 2,209 bytes with unchanged successful facts and Evidence IDs.
2026-10-25 22:37:32 +08:00
gensokyo-dev f69c52a1cf feat(native): page retained load-image observations (#7134)
Add optional native load-image summary delivery and retained selected-row pages through shared CLI/MCP analysis views. Preserve source verification, provenance, unavailable collections, exact row identity and opt-in byte/check payloads.

Preserve direct caller paths during shared CLI summary publication and cover raw/DOL projection contracts through the actual SDK. Real Ghidra ELF CLI/MCP captures, retained and file-backed pages, focused regressions and static checks passed; the PR records existing main schema failures and the JavaScript resource-recovery CI uncertainty.
2026-10-25 21:50:42 +08:00
Kaoru0822andClaude Opus 5.5 25f0f9934d fix(javascript): close member-path cycles through self stores (#7131)
A write below a slot that stores a value read from the same slot, such
as `s.c = s.c.p; s.c.i = 1` on a parameter, revisits the binding with
one more segment after the stored key on every turn. The cycle closure
only recognized segments added at the front of the path (`node =
node[key]`), so the work-list never terminated and exhausted the owned
worker heap (CodeMirror 5 `mode/stylus/stylus.js`).

Recognize a contiguous segment inserted after a stable base, check that
repeating it after that base can never be pruned, and escape through it
once, as the self-alias rule already does. Front insertions keep their
existing order and result.

Fixes #7130

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-25 05:32:29 -07:00
Kaoru0822andClaude Opus 5.5 70b96b65d9 fix(javascript): stop recording replaced slot values as store receivers (#7128)
Reference-store identity queries resolved the written target as a value,
so once a step consumed the written key the walk continued into the value
read from that slot and recorded it, and everything reachable from it, as
an object the store mutates. Every value stored into one local object at
unknown keys then received an entry for every such store, and each new
store re-queued every earlier one. Ambient-write indexing grew cubically
in the number of such stores.

Receiver queries now drop items whose projection no longer ends at the
written key. Stored-value selection has one owner, which the resolver and
the store fixpoint share. The fixpoint re-queues a dependent only when a
changed entry can reach it with a selection that its receiver query keeps.

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-25 05:29:23 -07:00
Kaoru0822andClaude Opus 5.5 5a22ae6bc8 fix(mcp)!: return a compact binary_session status by default (#7127)
* fix(mcp)!: return a compact binary_session status by default

binary_session {} with no target open returned about 66 KB, most of it
provider rejection diagnostics and candidate capability sets that the
skill's recovery flow re-reads on every retry. 6.8.0 grew it by 21%
because the shared regex capability limitation began repeating setting
bounds and step-by-step recovery for every search operation of every
provider (#6099, #6267, #6301, #7049) and IDA gained that limitation for
its two search operations (#4862).

The regex limitation now states the effective deadline and heap, their
sources and where to change them; the deadline and heap errors keep the
full recovery. binary_session gains detail: summary (default) or
complete. The summary keeps open state, target identity, the binding,
route capabilities, run, activity and health status, each candidate's
availability and target-support status, code and reason, and every
tool's availability, reason, remediation and modes, and lists the
fields it leaves out in omitted_fields. Complete detail returns the
previous shape. The CLI capabilities and providers commands keep
complete provider facts through the same application projection.

BREAKING CHANGE: binary_session {} no longer returns
analysis_provider_candidates[].availability.diagnostics,
target_support.diagnostics or capabilities, or tool_availability group
surface, effects and annotations. Pass {"detail": "complete"} to get
them.

Fixes #7109

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(mcp): key binary_session output fields by detail

The output schema accepted detail: "complete" with summary candidates or
tool groups, and a summary without omitted_fields, so detail did not
tell callers which fields existed. Each session kind is now crossed
with a detail-specific shape: a summary requires omitted_fields and
summary-only candidates and tool groups; complete requires diagnostics,
candidate capabilities and static tool flags and forbids omitted_fields.
Both the canonical schema and the SDK-validated advertised schema reject
mismatched shapes.

The verifier helper accepts summary candidates only for a validated MCP
summary; CLI provider output still requires candidate capabilities.
Regex limitation tests check values, sources and setting names instead
of whole sentences.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-25 05:29:03 -07:00
Kaoru0822andClaude Opus 5.5 e06be06530 fix(cli): report JSON Lines selection refusals as caller errors (#7124)
* fix(cli): report JSON Lines selection refusals as caller errors

A --format jsonl refusal because the selected value is not a record
collection is the caller's output selection, not unreadable analysis
output. Report it as invalid_request/invalid_input that names the
selection and its observed shape, keep the --format json /
--filter-output remediation, and drop the rea doctor advice.

AnalysisInputError gains an optional producer remediation, mirroring
the other refusal classes. The JSON Lines owner examines its documented
boundaries once for both admission and refusal facts.

docs/cli.md now states that columnar results such as
inspect-sqlite-database --table rows are not record collections.

Fixes #7110

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(cli): carry JSON Lines refusal facts as structured observations

JSON Lines refusals now put the location and shape of each refused value
in details.issues[].observed, and the message is built from those facts.
Tests assert the classification, issue path, observed facts, and the
shared remediation constant instead of fixed sentences.

The filtered analysis-view export check now uses a non-empty request
and compares the emitted rows with the unfiltered export.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-25 05:26:13 -07:00
Kaoru0822andClaude Opus 5.5 81afb043f7 fix(javascript): evaluate each short-circuit left operand once (#7126)
Logical branch selection evaluated the left operand to decide which
branches stay reachable, and value evaluation then evaluated the selected
left branch again. In a left-nested `a || b || c ...` chain every level
repeated that, so semantic analysis doubled per operand and a 24-operand
chain exhausted the per-source deadline.

The selection now carries the left operand's value with the branch, and
value evaluation reuses it. Provenance still walks the branch nodes.
Results are unchanged.

Fixes #7115

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-25 05:25:54 -07:00
Kaoru0822andClaude Opus 5.5 7336a566ac fix(mcp): fit every advertised input schema within the 64 KiB client limit (#7119)
* fix(mcp): fit every advertised input schema within the 64 KiB client limit

Claude Desktop drops a local connector tool whose compact-JSON input
schema exceeds 64 KiB. The full input profile advertised
compare_web_captures at 171,867 bytes and inspect_analysis_view at
71,632 bytes, because the self-contained projection inlines the
complete producer capture schemas once per side.

The full advertisement now fits the limit at its producing boundary.
A property whose schema repeats an earlier sibling's points at it, and
schemas below the deepest instance level that fits are summarized as
their types, size constraints and descriptions. Every rewrite widens the
advertisement through the existing relaxation rules for oneOf, not,
if/then/else, contains and unevaluated keywords, so canonical inputs
stay accepted and the canonical contract still validates every call.
Schemas within the limit are unchanged, and the compact profile is
still presented from the complete projection.

Numeric const and enum literals are also relaxed in the advertisement,
like boolean literals, since Gemini accepts only string enum members.

Fixes #4081

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(mcp): keep fitted input summaries widening and at the deepest fitting level

Array summaries took item types only from `items`, so a tuple such as
`{prefixItems: [{type: string}], items: {type: number}}` was advertised
as a number array that rejected `["a", 1]`. Item types now include tuple
positions from `prefixItems`, a draft-07 `items` array, and
`additionalItems`, and stay unconstrained when later elements are open.

A summary can also be longer than the schema it replaces (a nullable
array becomes an anyOf), so candidate size is not monotone in the
retained depth and stopping at the first overflow could skip a deeper
candidate that fits. A schema is now kept whole when its summary is not
smaller, and fitting searches from the deepest level down.

Property tests check that every summary accepts what its schema accepts
over every catalog input subschema and synthetic tuples, and that the
fitted schema is the deepest fitting candidate. The browser comparison
boundary test now expects a malformed `after` capture to be rejected
canonically, since `after` is advertised as the same schema as `before`;
the same malformation on `before` stays advertised-invalid.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-25 05:25:07 -07:00
Kaoru0822andClaude Opus 5.5 aa79f59478 fix(javascript): bind defaulted and destructured Webpack factory roles (#7117)
* fix(javascript): bind defaulted and destructured Webpack factory roles

A Webpack factory whose module parameter was defaulted (`e = {}`) or
destructured (`{exports: x}`) reported an exact empty export list with
complete coverage, because roles were taken only from plain identifiers.

Bind factory roles once, by position, in javascriptBundlerFactoryRoles.
Defaulted module/exports parameters keep their role (the convention always
supplies the argument), `{exports: alias}` binds the alias to
module.exports, and a repeated sloppy parameter name binds its last
argument. Other role patterns (rest, array, nested or computed keys, a
non-identifier loader) receive no role and count as an unknown finding,
so the file's analysis is partial instead of claiming no exports. The
export collector, the factory require name and the standalone bootstrap
externals all use these parsed roles.

Fixes #7113

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(javascript): keep factory receivers through unordered reads

A defaulted role parameter kept the unused default in the binding facts
passed to write ordering, so a receiver read inside a loop, nested block
or closure was unresolved and its export silently dropped while the
module stayed complete.

The role owner now removes defaults the calling convention never runs
before ordering. Separately, an export-shaped write through a role
binding whose read stays unordered now counts as an unknown finding
instead of being dropped, so an incomplete list is never reported as
complete.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(javascript): treat never-run factory parameter defaults as dead code

Removing unused defaults from binding facts let writes inside a default
expression reach the export collector: `(e = {}, t = (() => {
e.exports.ghost = 1; })())` reported `ghost` although Webpack supplies
both arguments. Loader calls in such defaults also produced require
references and async chunk keys.

The role owner now records the default of every role parameter, any
binding pattern included, as never run. The export collector, the
async-chunk walk and the static source traversal skip those subtrees,
so they yield no exports, references or chunk keys. Defaults of later,
non-role parameters still run and are still reported.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-25 05:23:23 -07:00
Kaoru0822andClaude Opus 5.5 d32d9f2902 fix(lldb): keep the bridge's no-target proof through interruption (#7111)
When cancellation or the deadline interrupted LLDB after it had published
its bridge result, the tracer never read that result, so an explicit
`target_process_created: false` (or `target_process_exit_observed: true`)
was lost. The run kept a `native-target:unknown` marker that no cleanup
retry could release, wedging the tracer for every later trace.

Read the published result once LLDB's process group is stopped, even for
an interrupted run, and apply the same target-gone proof in cleanup retry.
Cancellation or timeout stays the primary outcome; a created target, a
truncated result, or a missing PID alone stays unresolved.

Fixes #5385

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-25 05:21:26 -07:00
Kaoru0822andClaude Opus 5.5 cc2656be8d perf(native): charge function matching for the copies it makes (#7108)
* perf(native): charge matching for what it copies, not borrowed retained inventories

Native function matching deep-walked every retained list_procedures record
at about 2.8 KB per Ghidra-shaped row, although the session ledger lends
those immutable records without a copy, and charged each inventory's one
Evidence ID once per function. The report and Evidence copies of every
emitted member were not charged at all.

Charge each inventory row before parsing for the member it becomes,
including its report and Evidence copies, charge the shared inventory ID
once, and skip the walk only for borrowed immutable inventories and closure
records that matching never parses. Inline Evidence, bundle records and
selected observations keep their existing charges. Retained references now
fit about 22,000 rows per side instead of 10,000, and an oversized side is
refused before its rows are parsed.

Refs #6406

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(native): charge the envelope of borrowed matching inputs

Borrowed inventories skipped admission entirely, but matching copies their
limitations into the report and Evidence and digests their profile, so a
retained inventory with few rows and large limitations escaped the
allowance. Charge every envelope field and leave only the result payloads,
which row charging covers or matching never parses, uncharged.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-25 05:21:09 -07:00
Kaoru0822andClaude Opus 5.5 58a5275aa2 fix(mcp): advertise reduced array elements with Gemini-compatible item types (#7107)
Reduced compact input schemas gave every array property an any-value item
schema, a six-member type list beside nested `items`. Gemini function
declarations accept `items` only beside the single `array` type, so every
request through a Gemini-backed client failed with
`properties[functions].items.items: field predicate failed: $type == Type.ARRAY`.

Reduced array properties now advertise the element types their canonical item
schemas accept. Unknown, tuple, and mixed array elements use the empty item
schema, and properties that may be arrays or another type list typed anyOf
branches. An inline `not: {}` keeps the empty schema. The compact catalog test
now rejects `items` beside a non-array type as well as untyped item unions.

Fixes #5201

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-25 05:20:36 -07:00
Kaoru0822andClaude Opus 5.5 0eb3919d69 fix(artifacts): admit raw UDRW disk images (#7106)
* fix(artifacts): admit raw UDRW disk images

Raw (UDRW, UDTO) disk images have no UDIF koly trailer, so target
admission and root inventory both refused them as unsupported binaries.
Recognize disk images in one domain owner (koly trailer, GPT, Apple
partition map, MBR, or an unpartitioned HFS+/HFSX volume header) that
both boundaries consume.

hdiutil reports "has no checksum" for these images; the native reader
now treats that status line as an explicit absent checksum, records the
nonzero verify command, attaches read-only, and reports that integrity
was not verified. Checksum mismatches and unrecognized images are still
refused.

Positional range reads used by these probes now fill until end of file
so short host reads cannot hide a signature.

Fixes #5687

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(artifacts): read disk image signature windows at the recognizer

Target admission passed its first unfilled 4 KiB read to the raw-image
checks, so a short host read could hide a GPT, APM, MBR or HFS+
signature that inventory (which fills its prefix) still observed. The
recognizer now reads its own leading window through the caller's
filling positional read, and target admission fills its format-probe
prefix as well. The controlled short-read regression now covers target
admission alongside inventory.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-25 05:20:24 -07:00
Kaoru0822andClaude Opus 5.5 a8e5eaf1aa fix(macho): derive the runtime name of each Mach-O symbol (#7105)
inspect_macho reported only the symbol-table spelling (`_main`), while
LLDB, dlsym and observe_native_calls use the runtime spelling (`main`).
Passing a static name as a breakpoint silently selected a different
function, so results from the two tools could not be joined by name.

Keep `name` as the exact symbol-table fact and add a derived
`runtime_name` to every export and import row, produced by one shared
Mach-O symbol-name owner. Managed P/Invoke verification now uses the
same owner instead of its own underscore stripping.

Fixes #5298

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-25 05:19:54 -07:00
fc43e285ea feat(browser): retain compact scenario summaries and selected views (#7120)
Retain complete browser scenario Evidence while delivering compact summaries and selected step, event, transaction and text views through shared CLI/MCP workflows. Preserve initial CLI failures across cleanup retries and validate with real Chromium/TodoMVC.

Co-authored-by: Kaoru0822 <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-25 19:03:06 +08:00
morluto bc143125ed fix(release): reject missing manifest release boundaries 2026-10-25 17:25:18 +08:00
github-actions[bot]andmorluto 160d821ac9 chore(main): release rea-agents 6.8.0 (#5004)
* chore(main): release rea-agents 6.8.0

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

* docs(release): pin the JSON Lines migration guide

* docs(release): cover the remaining reviewed user-facing fixes

* docs(release): refresh the reviewed contribution snapshot

* docs(release): correct migration references and gateway scope

Preserve the concurrent 6.8.0 editorial draft and verified credits. Cite the actual keyed-archive UID migration, move CLI version and superclass-classification references to their matching entries, retain unknown-date plist behavior, and avoid implying live gateway acceptance. Existing author readiness declarations remain unchanged.

* docs(release): include nine newly merged changes in 6.8.0

* docs(release): include recorded write views in 6.8.0

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: morluto <[email protected]>
2026-10-25 16:35:15 +08:00
113679c9a6 feat(environment): serve creates whose answers show less than reads, and whole-object updates (#7080)
* feat(environment): take created entities' read-backs and re-derived update members

- A create's observation takes the fields the first read of its entity
  shows that the answer lacks, and an item read's clock spellings, when the
  read agrees with the answer otherwise. The plan records the read
  (observation.read with its shape); an item read then observes rather than
  checks, a list read only adds fields and still checks. Serving merges the
  same recorded objects.
- An update member sent back as the clock the entity held and stored as a
  fresh clock near the call is a clock rule; one stored as the slug of
  another member stored as sent is a slug rule. Both are accepted with any
  value, as whole-object writes send what they read.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(environment): record the write-views verification

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): reconcile write views without hiding contradictions

* fix(environment): reject incompatible slug-only saves

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: morluto <[email protected]>
2026-10-25 15:06:27 +08:00
Adolanium 80673b22a4 fix(javascript): report fork's silent stdio as piped (#7083)
* fix(javascript): report fork's silent stdio as piped

Node's child_process.fork pipes stdin, stdout and stderr when options.silent is truthy and options.stdio is neither a string nor an array, and inherits them otherwise. REA never read silent, so fork("worker.js", { silent: true }) was reported with stdio_mode default (inherited). The unset-stdio case of fork now reads silent: truthy gives pipe, falsy or missing gives default, and an unresolved value gives dynamic.

* fix(javascript): apply fork's silent rule to other known stdio values
2026-10-25 14:23:46 +08:00
Adolanium c74d302cc4 fix(ghidra): report a DOL language override conflict as a configuration error (#7081)
* fix(ghidra): report a DOL language override conflict as a configuration error

A REA_GHIDRA_LANGUAGE_ID outside PowerPC:BE:32:* made an otherwise admitted GameCube DOL an unsupported target. docs/ghidra-provider.md and the other fixed-import lanes treat such an override as a configuration error naming the conflicting setting. The DOL lane now uses the same languageOverrideConflict owner and names only REA_GHIDRA_LANGUAGE_ID, since DOL accepts a compiler-spec override.

* test(ghidra): keep a valid compiler spec out of the DOL language conflict
2026-10-25 14:19:14 +08:00
2c38becc4e feat(binary-layout): admit ELF32 i386, ARM and MIPS layout profiles (#7052)
* feat(binary-layout): admit ELF32 i386, ARM and MIPS layout profiles

inspect_binary_layout refused every ELF32 object, so router and IoT
firmware (ARM32, MIPS32) got no layout facts at all.

The pwntools adapter now admits EXEC/DYN/REL objects in these profiles:
ELF64 x86-64, ELF32 i386, ELF32 little-endian ARM and ELF32 little- or
big-endian MIPS. Header fields are read in the declared byte order, the
ELF32 header is bounds-checked at 52 bytes, and pwntools' own reported
architecture, class and byte order must match the admitted profile.
Other machines, classes and byte orders keep an unsupported_target
refusal that names the admitted profiles.

The output schema admits exactly those architecture combinations, and
the Evidence subject now names the reported machine instead of always
claiming x86_64.

verify:binary:layout cross-compiles i386, ARM, MIPS and MIPSel
executables and relocatable objects (never executed) and checks every
section name, address, offset and size against GNU readelf through CLI
and MCP. The real-binary-layout workflow installs those toolchains.

Refs #6934

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* ci(binary-layout): install target libc headers for ELF32 fixtures

--no-install-recommends skips the libc6-dev-*-cross packages the cross
compilers only recommend, so the i686 fixture fell back to the host's
/usr/include and failed to find bits/libc-header-start.h. Install the
four target libc development packages explicitly.

Refs #6934

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(binary-layout): size the minimum symbol entry by ELF class

The relocation reference check required a 24-byte Elf64_Sym entry, so
every positive symbol reference in an ELF32 report was refused as
unreadable output. Elf32_Sym is 16 bytes; take the minimum from the
reported class. A 64-bit table with 16-byte entries is still refused.

Refs #6934

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* test(binary-layout): compare ELF32 symbols and relocations with readelf

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: morluto <[email protected]>
2026-10-25 14:18:44 +08:00
rodricksz4h5 0a2b7af905 fix(apple): keep the documented terminating process, parent pid and metadata members of IPS reports (#7079)
Apple's crash report JSON documents termination.byPid, byProc and flags, the body's parentPid, and
the metadata line's build_version and platform. The parser shapes did not list them, so they were
reported only by name in unlisted_keys / unlisted_body_keys and their values were dropped: a
watchdog kill no longer said which process ended it, and process kept parentProc without its pid.
#4297 asks for termination members beyond namespace/code/indicator to be retained as recorded, and
docs/apple-ips.md says documented fields are reproduced.

They are now kept: termination.by_proc, termination.by_pid and termination.flags, process.parent_pid
and metadata.build_version / metadata.platform. The integers are recorded integers, so they count
toward max_integer_text_bytes and a type mismatch is recorded like the other fields.
2026-10-25 13:56:51 +08:00
RRFZ cc146042d1 fix(cli): preserve literal Markdown table facts (#7073)
* fix(cli): preserve literal Markdown table facts

* test(cli): retain capacity diagnostics through Markdown escaping

* fix(cli): keep intraword underscores readable in Markdown tables

Underscore runs between letters or digits cannot open or close CommonMark emphasis, so escaping them only adds noise to identifiers such as source_index and invalid_request in raw Markdown. Escape underscore runs only at word boundaries and restore the original diagnostic assertions.
2026-10-25 13:55:18 +08:00
Wenyu Chiou fead4fdaa7 fix(artifacts): preserve integrity contradictions for one-sided members (#7076) 2026-10-25 13:47:41 +08:00
Adolanium 4973ce7202 fix(capture): report an out-of-range record ordinal as a selection fault (#7067)
inspect_web_network_capture reported a record_ordinals entry past the capture's records as an invalid_format issue on record_ordinals, with no bounds. It is now an out_of_range issue on that entry (record_ordinals[i]) with minimum 0 and maximum total_records - 1, or invalid_value when the capture has no records.
2026-10-25 13:46:26 +08:00
Adolanium 06a4c338fc fix(protobuf): exclude trailing whitespace from gRPC-web trailer values (#7069)
RFC 9110 section 5.5 excludes leading and trailing whitespace from a field value. The gRPC-web trailer parser stripped only leading SP/HTAB, so 'grpc-status: 0 ' was reported as '0 '. Trailing SP/HTAB is now excluded too; internal whitespace and the examined byte count are unchanged.
2026-10-25 13:45:22 +08:00
Adolanium 60ab148afa fix(ghidra): accept stock language IDs with LEBE endianness or a slash (#7070)
REA_GHIDRA_LANGUAGE_ID only allowed LE or BE as the endian field and no '/' in the processor, so it rejected four language IDs that ship with Ghidra: ARM:LEBE:32:v7LEInstruction and v8LEInstruction (ARM BE8) and M16C/60:LE:16:default and M16C/80:LE:16:default. The syntax check now allows LEBE and a slash between processor name parts; the installed .ldefs catalog check is unchanged.
2026-10-25 13:45:11 +08:00
morluto 8e6a957886 fix(release): exclude Copilot from human contribution credits
Preserve GitHub User metadata for Copilot while excluding it from generated human thanks and accepted prose credits. Extend the real inventory/render/check regression and document the known agent identity.
2026-10-25 12:28:21 +08:00
Hahaa909andmorluto 9750486e78 fix(mcp): normalize compact schemas for strict gateways (#3806)
* fix(mcp): make compact schemas gateway compatible

* test(mcp): bind input precision assertions to the advertised profile

* test(mcp): own schema assertions and validator lifetimes

---------

Co-authored-by: morluto <[email protected]>
2026-10-25 12:11:42 +08:00
Ikko Eltociear Ashimineandgreptile-apps[bot] 4232399079 docs(i18n): add Japanese translation (#7061)
* docs(i18n): add Japanese translation

* Update docs/readme/README_ja.md

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-25 11:44:05 +08:00
Wenyu Chiou 26e155987f fix(javascript): invalidate Electron reads after direct eval (#7053) 2026-10-25 11:29:45 +08:00
69a2faa120 feat: add FreeBuff agent support for REA setup (#2736)
* feat: add FreeBuff agent support for REA setup

Register FreeBuff as a supported setup client using the shared user
agents directory (~/.agents/mcp.json with mcpServers, skills in
~/.agents/skills), which matches what FreeBuff Desktop loads. Adds the
client definition, boundary tests, supported-agent docs, and the skill
reference client list.

* fix(freebuff): detect FreeBuff from its profile directory, not shared .agents

The ~/.agents marker made FreeBuff appear installed for any home where
REA had installed shared skills (the default skill destination, also
Gemini CLI's), producing false doctor failures. Detect FreeBuff from
its own fixed profile directory ~/.config/freebuff-desktop instead,
and add a regression test covering a home with only the shared agents
skill directory.

* fix(setup): require product evidence for FreeBuff discovery

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

* fix(uninstall): retain unverified shared client configuration

* fix(clients): preserve scoped removal evidence

* fix(doctor): omit undefined selectedClientIds under exactOptionalPropertyTypes

Passing selectedClientIds explicitly as undefined fails typecheck on this
branch (TS2379), which blocks the focused test workflow before any test
can run. Spread the option only when defined.

* fix(update): separate ownership evidence from product applicability

rea update's maintenance scope inspected only product applicability
evidence, so when a FreeBuff profile was absent but REA already owned a
version-pinned row in the shared ~/.agents/mcp.json, the plan omitted
freebuff and left the integration stale. FreeBuff now declares its
registration storage as shared, which proves ownership of an owned row
but never product applicability, and maintenance requests ownership
evidence through that declaration instead of reinterpreting the shared
paths. Doctor and setup keep readiness evidence, foreign rows stay out
of maintenance, and the Doctor exactOptionalPropertyTypes fix makes the
focused test workflow runnable.

* test(dotnet): split oversized operand extent diagnostics describe

The describe callback reached 162 lines (max 150), failing
max-lines-per-function and blocking the pre-push check for this branch.
Move the single-case extent scenarios into their own describe; no test
content or order changes.

* fix(update): preserve ZCode's disabled shared registration during maintenance

The shared user agents file doubles as ZCode's fallback, where
`enable: false` disables the registration. Ownership-based maintenance
read that row as a FreeBuff registration, whose format does not
recognize the flag, so the row was selected and the emitted setup
command rewrote it, dropping the flag and silently re-enabling a
registration the user had disabled.

registrationActivation now honors `enable: false` for any client whose
registration storage is shared, because the activation fact binds every
reader of that storage. Maintenance leaves such rows out; doctor
reports them as stale, matching ZCode's own treatment.

* fix(doctor): let explicit client selection authorize the config read

When an ACL or an inaccessible shared parent made every access probe
fail, a client selected by name was omitted before the configuration
read could return its unreadable-config detail, so doctor reported it
as not detected with installation remediation: an unresolved selected
path collapsed to absence.

An explicit selection now authorizes the read outright and the probes
serve automatic discovery only. A selected client with an unreadable
config reports invalid with the read failure; one with no config
reports missing; automatic discovery still probes before inspecting.

* fix(uninstall): share shared-storage checks between ZCode and FreeBuff

clients() queued ~/.agents/mcp.json twice: once as zcode_fallback and once
as freebuff. ZCode has no discoveryPaths, so its strict read still blocked
uninstall on malformed shared text, despite FreeBuff's new retain-and-continue
behavior. For a valid owned entry, ZCode removed it first, so FreeBuff reported
skipped rather than removed.

Make the shared file's checks and removal one shared decision:
- Add zcodeDiscoveryPaths to ZCode so it has its own applicability evidence.
- Gate zcode_fallback push on applicable, not just zcodeFallbackPath presence.
- Report shared storage ownership as one decision for both clients:
  FreeBuff and ZCode now use clientOwnershipEvidencePaths together, so a
  recognizable REA-owned row proves ownership once for the shared storage, and
  both clients report the same result consistently.

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* fix(uninstall): merge duplicated host and prove shared ownership from owned rows

Uninstall.ts declared systemUninstallHost twice, preventing module load,
with the exported copy returning only clients and the second copy missing
clientWriteBlocker. Merge into one exported host carrying both blockers.

hasOwnershipEvidence used access() existence, so an unrelated malformed
~/.agents/mcp.json established ownership and blocked every removal.
Ownership now requires a readable, recognizable REA-owned row via the
injected filesystem, so unrelated shared data stays retained.

Also cover ZCode discoveryPaths in its path test.

* fix(uninstall): keep denied product-local configurations fail-closed

Unproven product applicability marked every configuration path of that client unverified, so a denied ZCode profile turned an unreadable product-local config into a retained item: uninstall continued and could report complete with REA still registered. The retain-and-continue exception now applies only to storage REA does not own, classified once by clientSharedRegistrationPaths (shared configuration plus another product's same-scope fallback).

Covers the Greptile P1 on Uninstall.ts. Validation: the new test fails on the pre-fix Uninstall and passes after it; freebuff/zcode/updateMaintenance/bom/dsh suites green; clientLifecycle, clientRegistrationStatus and claudeDesktop keep the same 8 host failures as the pre-merge baseline (symlink EPERM, stale-vs-aligned on Windows).

* fix(update): retain FreeBuff when ZCode reads the shared fallback

Maintenance deduplicated clients on the path each inspection read, so ZCode's fallback view of ~/.agents/mcp.json marked that file "covered" and dropped FreeBuff from the scope. ZCode setup writes ~/.zcode/cli/config.json and deliberately leaves the fallback untouched (docs/client-configuration.md), so the emitted command refreshed only ZCode while the shared row both products load stayed pinned to the old package version. Each setup action rewrites only its own configuration path and no other action rewrites the shared row, so every selected client keeps its own maintenance action; the read-path covered/deduped filter is removed.

Covers the Codex P2 on UpdateMaintenance.ts:104. Validation: the new regression fails on e2fdec384 with {clients:["zcode"]} and passes with {clients:["freebuff","zcode"]}; updateMaintenance 31/31, setupClientConfigCleanupOwnership, cliUpdateAlias, updateOutput, cliStartupImports green; tsc --noEmit, oxlint (0 errors) and oxfmt --check clean; clientRegistrationStatus keeps the same 4 symlink/stale-vs-aligned failures this Windows host has on base e2fdec384 (verified by stashing the change).

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <[email protected]>

* fix(uninstall): preserve completed reads as shared ownership evidence

hasOwnershipEvidence caught every read failure and kept scanning, so a handle-close failure after a successful read — a RegularFileCleanupFailure whose completed outcome still contains the exact shared-file text — was discarded as "no proof". FreeBuff's applicability-unverified storage therefore never got its ownership fact, and once the retained handle blocked later reads, readClientConfiguration downgraded them to retained: inspection passed, removals soft-failed, and runUninstall(true) could purge REA data while the active shared registration remained. The catch now parses the completed outcome for the ownership text while missing or unreadable storage and malformed data keep their proves-nothing semantics, so ownership holds and every later read failure stays blocking.

Covers the Codex P2 on Uninstall.ts:252. Validation: the new regression fails on 4dbfdf1a9 (freebuff reports no blocking failure while the poisoned-scope error lands on claude_code) and passes after; setupClientConfigCleanupOwnership 25/25; the other 21 Uninstall-consumer suites are byte-identical to base 4dbfdf1a9 (61 pre-existing Windows-host failures: symlink EPERM, stale-vs-aligned); tsc --noEmit, oxlint (0 errors, same 6 warnings) and oxfmt --check clean.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <[email protected]>

* fix(uninstall): keep owned-row proof through removal refusals

hasOwnershipEvidence treated any clientRegistrationRemoval refusal as no ownership: an effective REA-owned rea member shadowed by an earlier foreign duplicate (shadowed_unowned_member), or an owned native row beside a foreign V1 fallback (foreign_legacy_fallback), discarded the already-parsed positive row fact. With FreeBuff unverified and close failures recurring on the preflight/removal rereads, those failures were then downgraded to retained, so runUninstall(true) removed the skill and purged REA data while the active owned row remained. The refusal now carries the owned rows that motivated it — the refusal only refuses the edit — and hasOwnershipEvidence evaluates that fact on both paths; preflight and removal keep refusing the unsafe edit exactly as before.

Covers the Codex P2 on Uninstall.ts:269. Validation: the new regression fails on 225cb214a (freebuff reports no blocking failure while the poisoned-scope error lands on claude_code) and passes after; setupClientConfigCleanupOwnership 26/26; the 21 Uninstall-consumer suites are byte-identical to the recorded 225cb214a baseline (61 pre-existing Windows-host failures: symlink EPERM, stale-vs-aligned); tsc --noEmit, oxlint (0 errors, same 6 warnings) and oxfmt --check clean.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <[email protected]>

* fix(uninstall): keep unresolved discovery probes from softening shared failures

The discovery probe swallowed every stat failure, so a FreeBuff profile denied with EACCES/EIO was indistinguishable from an absent profile: shared storage was marked applicability-unverified and, when ~/.agents/mcp.json also remained unreadable, the ownership and removal reads softened to retained. runUninstall(true) then removed the skill and purged REA data while an installed FreeBuff could still hold an active registration — the regression even reports complete. Absence (ENOENT) is now the only state that leaves shared paths unverified; any other probe failure leaves applicability unresolved, and shared-config failures stay blocking.

Covers the Codex P2 on Uninstall.ts:318. Validation: the new regression fails on 7572a7082 with status complete and purged data and passes after; freebuffClientConfiguration 13/13; the Uninstall-consumer batches are byte-identical to the recorded baselines (61 pre-existing Windows-host failures: symlink EPERM, stale-vs-aligned); tsc --noEmit, oxlint (0 errors, same warning rules) and oxfmt --check clean.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <[email protected]>

* fix(uninstall): retain completed configuration reads as removal evidence

A close failure after a completed read discarded the text that read
produced, so an owned registration row the text proves softened to
retained. The completed text now drives removal, keeping shared-config
failures blocking when ownership evidence exists.

Co-Authored-By: Anthropic <[email protected]>

* fix(uninstall): stop removal when a cleanup retry leaves a handle unreleased

A completed reread whose handle the cleanup retry cannot release now fails
blockingly instead of serving the text: with no owned row, the soft path
let removeAll purge managed data before close() reported the incomplete
cleanup. The completed text is served only after the retry releases the
handle; an unreleased handle is REA's own failure, never applicability
uncertainty, so it bypasses the retained softening.

Co-Authored-By: Anthropic <[email protected]>

* fix(uninstall): keep the ZCode fallback when its profile probe is unresolved

A denied or failed profile probe leaves ZCode's applicability unresolved,
never absent, but the fallback branch dropped the shared row on
applicability alone: with the native config and FreeBuff's profile both
absent, an installed ZCode kept its uninspected registration while
uninstall purged skill and data. Unresolved probes now keep the fallback
in scope with the same fail-closed treatment as other unresolved shared
paths; definitive absence still keeps ZCode out of another product's
primary storage.

Co-Authored-By: Anthropic <[email protected]>

* fix(setup): preserve a shared row's enable:false during FreeBuff repair

The shared agents file doubles as ZCode's fallback, so its `enable: false`
binds every reader: doctor now reports the disabled row as stale for
FreeBuff, but following that remediation rebuilt the generic JSON entry
without the flag and silently re-enabled a registration ZCode disabled.
Repairs of shared storage now carry the activation fact forward, while a
client's own native storage keeps its established single-reader repair
contract; the denied-profile uninstall regression also pins the
fallback's new fail-closed inspection beside the denied read.

Co-Authored-By: Anthropic <[email protected]>

* test(uninstall): retarget the completed-reread regression to a released retry

The concurrent-writer regression held the reread handle open forever,
which the fail-closed path now serves: it duplicated the unreleased-handle
regression while documenting the opposite contract. The fixture's close
spy gains a failure limit so the cleanup retry releases the handle, and
the test again proves the completed text drives removal of the
concurrently added owned row, with purge verified against the cache
layout purge actually removes.

Co-Authored-By: Anthropic <[email protected]>

* fix(doctor): report a preserved shared disable as a manual conflict

Preserving a shared row's enable:false left doctor recommending a setup
run that registrationCurrent correctly treats as already current, so the
scoped doctor returned needs_human forever. The shared-enable
interpretation now has one owner, sharedEnableFlagBinds, used by both
the activation rule and the remediation: a row disabled through the
shared flag reports the manual conflict and states that rea setup will
not change it, instead of an endlessly ineffective repair.

Co-Authored-By: Anthropic <[email protected]>

* fix(doctor): keep a native ZCode disable on the setup repair path

The manual-conflict remediation matched a native zcode row through
sharedEnableFlagBinds, but setup drops enable:false on single-reader
storage, so a native disabled registration was told setup would not
change it. The remediation now reads setup's own writer rule,
exported as setupPreservesDisabledActivation: shared storage and the
zcode fallback view keep the manual-conflict message, while a native
row keeps the working rea setup instruction. Regressions pin both
remediations.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <[email protected]>

* revert: drop the unrelated managed-CIL describe split

The split does not belong to FreeBuff client support and only widens this
PR's diff.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <[email protected]>

* fix(uninstall): remove the shared agent registration once for both clients

ZCode's ~/.agents/mcp.json fallback and FreeBuff's own configuration queued the
same physical file twice, so ZCode removed the row and FreeBuff then reported
it absent, losing the outcome for the client that owns the file.

Resolve every configuration path to one owner -- the client whose
configuration it is -- and project that single inspection and removal to each
client that loads it. A fallback reader still contributes its discovery probe,
so an installed or unresolved reader keeps the shared file fail-closed.

Discovery also accepted a dangling ~/.config/freebuff-desktop link, which setup
and doctor reject because they follow the target, so an unrelated malformed
shared file blocked every other removal. Resolve the link before treating the
profile as installation evidence, keeping lstat for removal safety.

Also date the FreeBuff release wording against the published 6.7.0 package.

* fix(uninstall): keep a row per client when two clients own one file

OPENCODE_CONFIG can point OpenCode at FreeBuff's configuration file, where
OpenCode writes `mcp.rea` and FreeBuff writes `mcpServers.rea`. Grouping by
filename gave that path a single owner, so uninstall removed only OpenCode's
row, reported that removal for FreeBuff, and could purge managed data with
FreeBuff's registration still active.

Share the decision only through a fallback view of another client's row: every
client that declares the path as its own configuration keeps its own
inspection and removal, and the fallback reader reports each of them.

* fix(clients): preserve discovery and fallback registration identity

Follow product discovery link targets without losing configuration entry
presence or explicitly selected inspections. Associate fallback readers with
the format owner's registration table and project outcomes by owner and path,
so OpenCode removal cannot be reported as a ZCode fallback removal.

All three new boundary regressions fail against the previous source and pass
with the fixes. Validation: check:fast; 217 focused tests passed; the two
changed test files were rerun with 50 passing tests. No installed FreeBuff
client was launched.

---------

Co-authored-by: morluto <[email protected]>
Co-authored-by: Claude Opus 4.6 <[email protected]>
Co-authored-by: Kaoru0822 <[email protected]>
Co-authored-by: Codebuff <[email protected]>
2026-10-25 11:29:17 +08:00
Adolanium bffe8a52a1 fix(apple): keep the recorded image name and frame symbol offset of IPS reports (#7059)
* fix(apple): keep the recorded image name and frame symbol offset of IPS reports

Apple's IPS format documents usedImages[].name and frames[].symbolLocation, and docs/apple-ips.md says documented body fields are reproduced, but the parser shapes did not list either member. They were syntax-checked and discarded without an unlisted_keys entry, so a symbolicated frame lost its +offset and every image its binary name. Both are now kept: images[].name and frames[].symbol_location as a recorded integer, with type mismatches recorded like other fields.

* fix(apple): count frame symbol offsets in the integer text check
2026-10-25 11:18:52 +08:00
Vinícius Andrade 4ed33efb0c fix(cli): format arrays of empty objects without keys as JSON (#7058)
When formatting arrays of objects that have no own enumerable keys as
Markdown, formatObjectTable derives empty headers and outputs a malformed
table with an invalid delimiter row. Require at least one item with keys in
isObjectArray and fall back to JSON serialization when headers are empty.

Fixes #7057
2026-10-25 10:46:15 +08:00
morluto 75e0d620bf fix(tests): accept retained module aliases in node views
Content-identical assets share a node whose observations are ordered by identity. Accept either retained display path when selecting that node, while retaining exact per-path reference and observation assertions.
2026-10-25 10:30:08 +08:00
morluto 4c680d099c fix(lint): preserve portable advertised patterns through autofix 2026-10-25 10:08:11 +08:00
Adolaniumandmorluto 046ec56d16 fix(browser): report an out-of-range script_index as a selection fault (#6979)
* fix(browser): report an out-of-range script_index as a selection fault

A valid export_web_scripts manifest without the selected script_index was reported as an invalid_format input issue, as if the manifest file were malformed. It is now an out_of_range issue on script_index with the valid bounds, or invalid_value when the manifest has no scripts.

* test(browser): keep the out-of-range index case in its own block

---------

Co-authored-by: morluto <[email protected]>
2026-10-25 10:06:34 +08:00
morluto e9b21aadc5 docs(settings): explain runtime design and provider recovery 2026-10-25 09:56:11 +08:00
morluto f62a2baad8 refactor(javascript): own coverage facts and reuse block prefixes
Separate syntax, semantic uncertainty, examination limits and observed Electron applicability at the static producer, then preserve them through worker, graph and retained/inline summary views. Derive the established parse_status from the same facts and leave historical measurements unknown. Give semantic resource-limit classifications one schema owner.

Replace repeated block-prefix searches and copies with collector-scoped syntax indexes. Keep invocation environments separate, charge indexing/replay to the existing work budget and yield during prefix work. Remove the superseded synchronous collector entrypoint.

Validated producer, worker, CLI/MCP, historical and partial-coverage regressions, plus actual cancellation during indexing. Bounded authored 128/256-call controls preserved all prior facts while replacing repeated search/copy work with one index pass; this is not a throughput or agent-token benchmark.
2026-10-25 09:49:26 +08:00
morluto c8269ca497 fix(ci): own mock isolation and normalize Git patch directories
Convert native directory separators only at Git patch argument production. Exercise fresh, applied and invalid dependency patches in private copies, preserving protectNTFS, and select the regression for Windows CI.

Replace the spelling-based mock inventory with one binding-aware Oxlint rule. Canonical test projects own isolation for both Vitest and lint; external temporary CLI probes avoid concurrent test discovery.

Validated actual local Git and Oxlint execution, resolved-policy parity, CI selection and report tests. Native Windows verification remains with CI. Refs: #7050
2026-10-25 09:49:26 +08:00
morlutoandAdolanium cb26e8ca63 fix(contracts): share ADB limits and preserve Rosetta register facts
Use the domain-owned activity extra count in CLI parsing and advertisement. Preserve nested Rosetta registers at the selecting parser and qualified register projection, sharing the existing aggregate budget.

Port the Rosetta work from Adolanium commits 1889ac153 and c9e93bd88 in #6976, including morluto follow-up 07fa932ee. Add malformed-container and mixed-thread budget controls. CLI/MCP coverage uses a recorded ReportCrash excerpt in an authored report, without generating a native crash or contacting an Android device.

Refs: #6291, #6976, #6977

Co-authored-by: Adolanium <[email protected]>
2026-10-25 09:49:26 +08:00
morluto d23dc5d188 fix(browser): admit JSON shape expansion against shared retention
Require explicit shape output admission before allocating expanded paths. Share capture retention across console entries, WebSocket frames and selected request/response shapes, with exact shape replacement and removal receipts.

Preserve malformed-input versus capacity outcomes and peer facts. Validated deep, wide, Unicode, repeated-row, replacement, redirect, reset and mixed-retention regressions.
2026-10-25 09:49:26 +08:00
morluto e97f47b0b7 fix(lifecycle): retain IDA cleanup owners and drain JEB fixture work
Preserve primary copy failures, exact descriptor retry ownership and complete-session cleanup state. Make the authored JEB HTTP fixture join admitted callbacks after transport closure.

Validated with controlled real-file faults, cancellation/finalization and retry cases, plus local HTTP and existing CLI/MCP lifecycle regressions. No real IDA or JEB engine was exercised.
2026-10-25 09:49:26 +08:00
morluto f3ed1657f8 feat(mcp): support runtime analysis limit recovery (#7049)
* feat(mcp): support runtime analysis limit recovery

* refactor(config): remove unused limit re-exports

* fix(android): keep fixture release metadata dependency-free

* fix(settings): preserve provider budgets and recovery facts

* fix(tests): resolve lint plugins from their repository owner

* refactor(mcp): verify guidance delivery without frozen wording

* fix(android): align availability with runtime timeout

* test(recovery): assert limit facts instead of frozen wording

* fix(mcp): share runtime settings with the default JADX provider

* test(mcp): bound acknowledgements and release held work on failure

* fix(catalog): declare runtime settings tools as MCP-only
2026-10-25 09:45:26 +08:00
morluto ab720b4216 test(evaluation): preserve partial coverage in the Notes oracle 2026-10-25 09:07:57 +08:00
morluto bb17e90f27 chore: integrate concurrent lint and async fixes 2026-10-25 08:36:10 +08:00
morluto 627c085cee chore(lint): enforce reviewed promise and fallback contracts 2026-10-25 08:29:20 +08:00
morluto 5938581b37 fix(async): own completion across test and provider boundaries 2026-10-25 08:29:20 +08:00
morluto fdf5a33fe2 refactor(tests): keep fixture adapters outside runtime 2026-10-25 08:29:20 +08:00
morluto be698d4761 test: verify assertion contracts and the production evaluation gate 2026-10-25 08:27:16 +08:00
morluto 760d9f6019 fix(source-maps): retain primary failures through cleanup retries 2026-10-25 08:25:17 +08:00
morluto 42c16ba631 fix(tests): resolve lint plugins in boundary regression 2026-10-25 08:18:09 +08:00
morluto 76afdd35ae chore(lint): guard validation ownership and asynchronous assertions 2026-10-25 07:57:07 +08:00
morluto ed86f9c3bb fix(verifiers): propagate session assertion failures synchronously
Remove the redundant async declaration from the Ghidra session assertion helper so its existing callers receive assertion failures within their synchronous error boundary. Keep call order unchanged.

Validation: a public-helper Node subprocess accepts authored valid handshake facts and rejects incomplete analysis and a target-digest mismatch. The regression failed for the original async declaration with Missing expected exception, then passed with the fix. No installed Ghidra execution was performed.
2026-10-25 07:56:26 +08:00
morluto 475f09d5ca chore(lint): enforce buffer and cancellation regression guards 2026-10-25 07:31:42 +08:00
morluto f2fa951c00 fix(cancellation): retain caller reasons across digest and startup 2026-10-25 07:31:31 +08:00
morluto 9140deae63 docs: clarify ownership and cleanup review guidance
Clarify structural parsing, admitted facts and freshness, useful facade and compatibility responsibilities, immutable JSON proof, and partial Evidence retention. Document fixture cleanup ownership, semantic fault injection and the limits of static checks; extend the existing PR owner hint. Correct the session-record architecture label.

Validation: docs:check (including nine README consistency tests), rendered docs:build with link validation, touched Markdown formatting and git diff --check passed. Source and installed Vitest cleanup behavior were reviewed. No runtime behavior changed.
2026-10-25 06:51:31 +08:00
morluto efab65c2f9 docs: codify boundary regression checks
Turn recurring parser and projection failures into a selectable regression
guide covering format assumptions, raw identity, missing values, partial work,
independent limits, representation expansion, snapshots, and public delivery.
Link it from the agent instructions and PR template without adding checkboxes.

Clarify completed-observation ownership, digest representations, and exclusive
success/failure budget accounting. Preserve capacity fixtures and use byte
comparisons for large buffers to avoid diagnostic allocation blowups.
2026-10-25 06:49:10 +08:00
Wenyu Chiou 07eb044f77 fix(snapshot): decline ambiguous exact evidence replay (#7037) 2026-10-25 06:35:27 +08:00
Wenyu Chiou 238914974d fix(analysis-view): preserve current observation paths in summaries (#7039) 2026-10-25 06:35:12 +08:00
Dery Ferdika 1ff275fbf7 fix(browser): admit passive capture events against the shared retained budget (#7043)
Passive page capture retained console entries and websocket frames with no
aggregate ceiling. Every sibling retention surface already enforces
WEB_RUNTIME_LIMITS.retainedEventBytes (8 MiB): the observation session's
pre-arm buffer and document-request identities, the browser runtime sources,
requests, and metadata, the V8 inspector's retained metadata, and this same
capture's script metadata, which has its own count-and-bytes budget. A page
that logs a few hundred megabytes of console text, or sends text frames whose
JSON shapes carry hundreds of thousands of keys, grew the capture without
limit until the process ran out of memory; the MCP response budget runs after
the handler, so the growth happened before any typed refusal could exist.

Admit each console entry and websocket frame against the same shared budget
before retaining it. A refused entry is counted through the completeness
tracker's dropped-events vocabulary, which already carries console_events and
websocket_frames kinds and was so far used only for late scripts, so the
inspection reports a truncated inventory with the dropped count instead of
silently overflowing. Console entry bytes measure the retained representation:
the entry's type, url, argument-type strings, and the text capture's retained
bytes, which the handler already computed per entry. A websocket frame retains
its shape summary rather than the payload, and the shape grows with the
payload's own keys, so the frame is charged the serialized size of the shape
it retains. beginAuthorizedFrame resets the counter together with the rest of
the per-frame state.

Out of scope: response metadata and agent hints, whose rows are fixed-shape
facts already bounded per response, and any new setting. The shared 8 MiB
constant is reused as-is.

Validation: RED then green in src/browser/CdpCaptureEvents.eventBudget.test.ts,
where the console case previously retained every oversized entry and the
websocket case retained the over-budget frame; 26 tests across the neighboring
console, websocket, coverage, and completeness suites pass. On this Windows
host the wider browser project has 26 pre-existing failures in three
Electron/WebBundle worker-spawn files, reproduced identically with this change
stashed (26 failed, 644 passed on the pristine base; 26 failed, 647 passed
with it on the same file set). tsc --noEmit over the changed set exits 0,
oxlint reports 0 errors, oxfmt --check is clean, and verify:module-boundaries
passes. No real browser session was driven on this host; the browser CI lane
owns that coverage.
Each retained entry now carries a conservative fixed overhead, and each captured console text value a wrapper cost, so a stream of minimal entries cannot admit millions of objects into the budget when shapes are disabled; a mixed-kind test locks that console entries and websocket frames spend one shared allowance.
2026-10-25 06:34:26 +08:00
Syed Rishad 6bcd243ca4 fix(verifiers): expect the session-continuation regex overflow reason (#7045)
overflow into the remediation and ended the reason with the session
continuation sentence, but the real-Ghidra boundary check still required
the old reason text, so every lane that runs verifyGhidraBoundaries failed
on real Ghidra.
2026-10-25 06:34:11 +08:00
aba487eec0 feat(environment): learn creates from every call, with sorted arrays and referenced entities (#7034)
* feat(environment): explain values an application derives from other fields

Prompt-built environments withheld creates whose responses carry values the
application derives deterministically, which the write model kept as
constants or could not match across calls:

- A composed string is literal text plus parts read from the fields other
  rules write: a field's value, its slug, or a clock's calendar digits. A
  create composes a string only when another entity of the type spells it
  the same way and none spells it otherwise, and never from two sources
  serving could tell apart; an update composes an unsent change from members
  stored as sent and unchanged fields that also spell its value before the
  call. A composed field no longer pins the members it reads.
- A trimmed clock format drops trailing fraction zeros, as Go spells
  nanoseconds. It is learned from the type's values, a later create or a
  later update, and spreads to the plan's other clocks unless a value shows
  a fixed width.
- A large integer no entity of the type holds, among distinct ones of the
  same length and kind, is a generated unique value, served as a seeded
  integer.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(environment): record the derived-values verification

Gates, tests, write plans over 69 recorded captures and served write tasks on the baseline and on this branch.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* feat(environment): learn a create plan from every call

A later create the plan does not take is explained on its own and merged when both calls show the same fields, every field both rule they rule alike, and a field neither rules holds one value: members either call sent are accepted, only those every call sent are required, and an echoed member a call left out takes the value that call stored, kept in the body's defaults.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* feat(environment): sorted arrays, referenced entities and nested view keys

- An echo is `sorted` when the application stored the sent strings in
  code-point order; serving stores them sorted, and a plain echo and a
  sorted one agree in update and create plans.
- A `reference` is an object field holding fields of the entity a sent
  member names, as the state holds it. The member is accepted with any
  value; replay and serving look the entity up, and serving refuses a call
  naming one the environment does not hold.
- A view names in `nested_fields` the keys it always shows of objects it
  shows with fewer keys than an entity stores; a stored object it never
  recorded renders with them. The view spelling learner moves to its own
  module.
- Every later create, not only one the plan refuses, is merged into its
  plan when it agrees, keeping the plan's rule order.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): keep clock spellings, linked ids and word boundaries honest

- A later update's clocks take the plan's spelling, sibling-trimmed
  clocks included, wherever the call's values fit it, so an update whose
  later call trims only one of two clocks is served.
- A held value that ends its fraction in a zero keeps a create's clock
  at a fixed width, whatever order the held spellings come in.
- A large integer that another field of the created entity also holds
  is linked, as a repeated token is: it is never unique, and the create
  is withheld as unexplained-change.
- Composed parts are delimited by whole characters through the shared
  joinsBefore/joinsAfter, so an astral letter joins a match to its word.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* test(environment): keep a same-width fixed clock out of trimming

A held .123456780 beside a held .12345678 keeps the create's clock at
nine digits in either order; the cases fail on 8ce1127b5.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(environment): record the create-calls verification

Gates, tests, write plans over 69 recorded captures and served write tasks on the derived-values baseline and on this branch.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(environment): note the create-calls census on current main

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): check composed parts and clock fits before they shape a plan

- A composed string whose part has nothing to spell refuses the create
  or update through the apply Result instead of keeping a stale value.
- A composition learned on a later call is rebuilt from each earlier
  call's fields, so an earlier call that left the value otherwise
  contradicts it.
- An update adopts its fitted clock spellings only after the
  unknown-entity and contradiction checks, so a call on an entity an
  unmodelled write left unknown no longer trims a served plan's clock.
- Sibling trimming on a later create also checks that call's fields,
  so a sibling clock it shows with a trailing zero stays fixed-width.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* test(environment): split the derived-values view tests by rule

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): keep fixed-width, zone and generated-integer evidence through a plan

- A plan keeps the first value that showed each clock at a fixed width,
  from held entities and every accepted create or update. No later fit
  or sibling trim widens that field, and a later call that shows it
  shorter is withheld.
- Two stamped clocks are one composed source only when they share a
  zone offset. A held entity that holds the target field as anything
  the parts do not spell, a non-string included, vetoes the
  composition. The token index is built once, and only when a string is
  left to compose.
- A generated integer that another field repeats as a number at any
  depth is linked. Compare treats two integers of one JSON kind and
  length under a unique rule as generated values.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): keep optional references across create calls and in answers

- A reference a later create added stays while further creates leave its
  member out, and agrees with a later call that shows it again.
- A create's answer shows optional reference and echo fields only when the
  call wrote them, whichever recorded call the answer came from.
- An update that re-sends the id of the entity a field already repeats keeps
  the plan's reference instead of pinning the member.
- An id naming entities of one type in several collections teaches no
  reference, and serving or replay refuses a call that names one.
- An echo is not promoted to sorted once a call stored its strings out of
  code-point order as sent.
- Nested view keys compare as sets and keep the first recording's order.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* test(setup): share registration prewarm host preparation

* fix(environment): retain write evidence and match trimmed response clocks

* fix(environment): withhold creates whose generated integer another string repeats

A unique integer whose digits show in another top-level string that no
template or composed rule rebuilds from it (an echoed serialText, or a
token like folder-<id>) withholds the create as unexplained-change,
since serving would seed the integer and keep the string. A templated
copy is still served.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): respect earlier clock widths and left-out members in later creates

- The create merge widens a clock to trimmed only against the value that
  showed its width: a held entity or accepted call at a fixed width keeps it
  untrimmed.
- A later create that left out a member the plan fills, with a default or a
  reference's absence, while its observation shows that field otherwise is
  unexplained-change, so the plan cannot serve the stale value.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): hold later creates and compare to the composed and integer forms

- A later create whose response spells a composed field otherwise than
  the plan rebuilds it is withheld as unexplained-change.
- A unique field learned from a generated integer accepts only an
  integer of the same length and kind from a later create, as serving
  seeds one.
- Compare counts a composed field as equal when each side's response
  spells it from its own fields, which compare under their own rules.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): preserve composed parts and unique generator families

* fix(environment): reject contradictory create references

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: morluto <[email protected]>
2026-10-25 05:31:46 +08:00
morluto ae54b96c6f refactor(tests): use fixture-owned temporary cleanup
Remove duplicate root lists and teardown deletion for directories already owned by createTestTempDirectory. Retain independent IDA workspaces, runtime closure, environment resets, permission recovery and explicit deletion assertions.

Validation: all nine affected suites passed (126 tests); installed Vitest cleanup ordering reviewed. Provider protocols use authored fixtures, not installed IDA or Ghidra.
2026-10-25 05:28:31 +08:00
morluto 599099f656 refactor(pe): share optional-header structural facts
Decode optional-header declarations once and pass detached directory words, locations and SizeOfHeaders to native consumers. Preserve executable machine admission, raw GPU/resource machine reporting, RVA mapping and certificate file-offset semantics.

Validation: fresh build/catalog, full static checks, 466 core/provider fixture tests, 122 workflow tests and 14 selected file-backed PE admission tests passed. Public CLI and actual stdio MCP used authored artifacts; installed external providers were not qualified. Added 17 structural cases and two GPU machine-policy controls. Eager directory decoding costs remain unmeasured.
2026-10-25 05:28:31 +08:00
morluto 9548953b47 refactor(clients): retain inspected command ownership
Carry the command identity already parsed during registration inspection into maintenance selection. Keep activation, validity and public status output separate.

Validation: fresh build/catalog and full static checks; registration status, update maintenance and setup cleanup ownership suites passed (90 tests).
2026-10-25 05:28:31 +08:00
morluto d24ad4f55d docs: make pull request draft status optional 2026-10-25 05:19:22 +08:00
Ahmedandmorluto 4b045e8f2f feat(cli): accept stdin for structured JSON request documents (#5486) (#7040)
* feat(cli): accept stdin for structured JSON request documents (#5486)

* fix(cli): preserve cancellation and admission for JSON stdin

---------

Co-authored-by: morluto <[email protected]>
2026-10-25 05:10:29 +08:00
morluto 10b83a9b62 fix(browser): preserve completed source-map observations
Return typed fetch outcomes with completed map coverage when acquisition is
cancelled or a response resource fails. Compose those facts with the captured
inspection as partial bundle Evidence, preserving exclusions, exact abort causes,
and cleanup priority without claiming unperformed script analysis.

Keep complete response-body SHA-256 and byte counts when map interpretation
fails, while preserving compact failure rows and capacity for later valid maps.
Reuse the read owner digest for successful text artifacts and preflight their
JSON representation before parsing. Historical failed rows remain importable.

Exercise controlled HTTP/stream boundaries, CLI SIGINT, and SDK cancellation
with later MCP Evidence retrieval. Share the public cancellation fixture and
use one Result API across consumers. Real Chromium and Windows CLI cancellation
were not verified.
2026-10-25 05:09:26 +08:00
38719a6fa9 feat(environment): explain values an application derives from other fields (#7025)
* feat(environment): explain values an application derives from other fields

Prompt-built environments withheld creates whose responses carry values the
application derives deterministically, which the write model kept as
constants or could not match across calls:

- A composed string is literal text plus parts read from the fields other
  rules write: a field's value, its slug, or a clock's calendar digits. A
  create composes a string only when another entity of the type spells it
  the same way and none spells it otherwise, and never from two sources
  serving could tell apart; an update composes an unsent change from members
  stored as sent and unchanged fields that also spell its value before the
  call. A composed field no longer pins the members it reads.
- A trimmed clock format drops trailing fraction zeros, as Go spells
  nanoseconds. It is learned from the type's values, a later create or a
  later update, and spreads to the plan's other clocks unless a value shows
  a fixed width.
- A large integer no entity of the type holds, among distinct ones of the
  same length and kind, is a generated unique value, served as a seeded
  integer.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(environment): record the derived-values verification

Gates, tests, write plans over 69 recorded captures and served write tasks on the baseline and on this branch.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): keep clock spellings, linked ids and word boundaries honest

- A later update's clocks take the plan's spelling, sibling-trimmed
  clocks included, wherever the call's values fit it, so an update whose
  later call trims only one of two clocks is served.
- A held value that ends its fraction in a zero keeps a create's clock
  at a fixed width, whatever order the held spellings come in.
- A large integer that another field of the created entity also holds
  is linked, as a repeated token is: it is never unique, and the create
  is withheld as unexplained-change.
- Composed parts are delimited by whole characters through the shared
  joinsBefore/joinsAfter, so an astral letter joins a match to its word.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* test(environment): keep a same-width fixed clock out of trimming

A held .123456780 beside a held .12345678 keeps the create's clock at
nine digits in either order; the cases fail on 8ce1127b5.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): check composed parts and clock fits before they shape a plan

- A composed string whose part has nothing to spell refuses the create
  or update through the apply Result instead of keeping a stale value.
- A composition learned on a later call is rebuilt from each earlier
  call's fields, so an earlier call that left the value otherwise
  contradicts it.
- An update adopts its fitted clock spellings only after the
  unknown-entity and contradiction checks, so a call on an entity an
  unmodelled write left unknown no longer trims a served plan's clock.
- Sibling trimming on a later create also checks that call's fields,
  so a sibling clock it shows with a trailing zero stays fixed-width.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* test(environment): split the derived-values view tests by rule

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): keep fixed-width, zone and generated-integer evidence through a plan

- A plan keeps the first value that showed each clock at a fixed width,
  from held entities and every accepted create or update. No later fit
  or sibling trim widens that field, and a later call that shows it
  shorter is withheld.
- Two stamped clocks are one composed source only when they share a
  zone offset. A held entity that holds the target field as anything
  the parts do not spell, a non-string included, vetoes the
  composition. The token index is built once, and only when a string is
  left to compose.
- A generated integer that another field repeats as a number at any
  depth is linked. Compare treats two integers of one JSON kind and
  length under a unique rule as generated values.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* test(setup): share registration prewarm host preparation

* fix(environment): retain write evidence and match trimmed response clocks

* fix(environment): withhold creates whose generated integer another string repeats

A unique integer whose digits show in another top-level string that no
template or composed rule rebuilds from it (an echoed serialText, or a
token like folder-<id>) withholds the create as unexplained-change,
since serving would seed the integer and keep the string. A templated
copy is still served.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): hold later creates and compare to the composed and integer forms

- A later create whose response spells a composed field otherwise than
  the plan rebuilds it is withheld as unexplained-change.
- A unique field learned from a generated integer accepts only an
  integer of the same length and kind from a later create, as serving
  seeds one.
- Compare counts a composed field as equal when each side's response
  spells it from its own fields, which compare under their own rules.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): preserve composed parts and unique generator families

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: morluto <[email protected]>
2026-10-25 05:06:21 +08:00
morluto 58fb824105 refactor(evidence): centralize JSON payload admission
Give ordinary Evidence creation an explicit unknown-payload input contract
while retaining the strong JSON contract for producer helpers and cooperative
immutable creation. Keep schema admission, input snapshotting and identity
hashing with the Evidence owner.

Remove 32 generic JSON preparses that only fed ordinary Evidence construction.
Preserve semantic format validation, selected-source bindings, standalone
JSON boundaries, typed executions and scenario digest contracts. Invalid JSON
now reports its Evidence parameters/normalized_result/raw_result prefix.

Validation: fresh runtime/catalog build, 560 focused and README tests passed,
4 platform-specific cases skipped, full static/docs checks, test discovery,
duplication and diff checks. New regressions cover omitted semantic optionals,
raw-result defaults and nested undefined refusal without input mutation.
Installed providers, real browsers/Frida/JADX and Linux-only recovery remain
unverified; boundary providers are authored fixtures.
2026-10-25 05:01:36 +08:00
morluto 835904e4ee refactor(evidence): carry producer-owned source records
Carry the final artifact inspection's exact nested source Evidence through
AnalysisExecution. Derive links in the shared Evidence builder instead of
reparsing the result in CLI and MCP adapters. Retain validation for unknown
and historical Evidence inputs.

Move the shared source recorder into application ownership and migrate its
consumers. Record sources before parents; keep source recording failures
from publishing summaries. Decline snapshot entries whose source closure the
current entry format cannot represent, while retaining investigation Evidence.

Validation: actual ASAR regression failed before the change with missing
links and passes afterward; exact nested identity, source recorder refusal,
final-close cancellation and cleanup failure, historical authentication,
snapshot controls and public CLI/MCP workflows. Fresh runtime/catalog build,
full static/docs, discovery and duplication checks pass. Installed native
providers and platform-specific skipped cases remain unverified.
2026-10-25 04:37:11 +08:00
morluto 5ea2c280b8 refactor(doctor): retain parsed configuration diagnostics
Return native admission issues and an explicit Hopper timeout result from
the configuration owner. Doctor consumes those parsed facts instead of raw
timeout text plus a separate issue list. Reuse a successful aggregate parse;
validate the individual timeout independently when other settings reject it.

Preserve aggregate error precedence, JEB isolation, omission, constraints,
causes and provider scope. Keep partial diagnostics when unrelated NUL,
loader arguments, language selection or timeout settings are invalid.

Validation: configuration and Doctor workflow regressions, mixed-invalid
settings, full static/docs checks. No installed provider was exercised.
2026-10-25 04:37:11 +08:00
morluto aced9fa9b4 fix(wasm): retain acquired inputs across failed inspection
Keep completed module and candidate identities, glue observations, and the
admitted tool profile when WABT validation or later inspection fails. Recover
stable reads from their existing owner when descriptor finalization fails;
mark unparsed glue explicitly and preserve validation uncertainty. Forward
partial Evidence through output/cleanup errors and preserve pre-abort reasons.

Exercise producer failures, partial acquisition, decoding, finalization and
cleanup retry with simulated owned WABT commands. Public CLI and stdio MCP
coverage follows the retained Evidence reference through get_evidence_bundle.
Real WABT 1.0.42 and the Windows shell-fixture lane were not verified.
2026-10-25 04:24:03 +08:00
JANG BYUNGKUNandmorluto 149b43ee61 fix(il2cpp): link arm64 builds whose registrations were inlined (#6989)
* fix(il2cpp): accept arm64 module arrays loaded through adrp/add pairs

A Unity 2022.3 macOS Master (LTO) arm64 build keeps no static CodeRegistration, so the module-array fallback that only decoded x86-64 RIP-relative references reported code-registration-not-found.
The registration owner now also decodes aligned adrp + add pairs in executable ranges and keeps the same exact-module-set and single-reference rules.
A minimal Mach-O fixture failed on the previous reader (47 passed / 1 failed) and passes here with four fail-closed variants (48/48 binary tests).

* fix(il2cpp): name the matched instruction form in inferred module-array limits

The inferred-array limitation always described RIP-relative LEA/MOV, which is wrong for arm64 results.
The provider now names the adrp/add pair on arm64, and the tool contract and docs cover both forms and state that a located array alone does not finish linking a Master build.
The new arm64 provider case failed before the wording change (1 passed / 1 failed) and both cases pass now.

* fix(il2cpp): accept type tables loaded without a static MetadataRegistration

A Unity 2022.3.62f3 macOS Master build inlines MetadataRegistration as well as CodeRegistration, so linking stopped at metadata-registration-not-found after the module array was found. Registration now infers the only field linking reads, the Il2CppType table, from the one run of type-shaped pointers that covers every referenced type index and whose start is loaded by the existing RIP-relative or adrp/add forms; types_field is null and the Evidence is inferred. The new arm64 and x86-64 fixtures failed on the previous reader (52 passed / 2 failed). Four run-selection variants (interior reference, run extended before the loaded address, two loaded runs, run shorter than the referenced indices) keep metadata-registration-not-found; instruction decoding and record-shape checks are not covered by these fixtures.

* test(il2cpp): pin adrp page deltas, register pairing and type-record shape

Every adrp fixture targeted its own 4 KiB page, so dropping the page delta or its sign extension, the add register check, the type-kind bound or the type-definition bound left all binary tests passing. Fixtures now reach a later and an earlier page, pair an add with a different destination or source register, break the type run with an out-of-range kind or type-definition index, and bound type-table candidates when the metadata references few type indices. Each of those five source mutations now fails 1 to 3 of 85 binary and provider tests.

* refactor(il2cpp): share Il2CppType kind decoding and document type-table bounds

The type-table fallback decoded the Il2CppType kind and class/value-type kinds separately from the type namer; both now use the Types owner. Zero and unaligned words are rejected before mapping lookup (Master sample metadata-registration search 92 to 68 ms). Docs and the limitation state that types_count is an upper bound, that a run spanning mappings or a small referenced type range can fail, and credit Cpp2IL StrippedCodeRegSupport and the data-side MetadataRegistration searches of Il2CppDumper and Cpp2IL. Master sample output is unchanged (11,031 code addresses, 37,025 type rows).

* fix(il2cpp): refuse generic instances when the type table was inferred

An inferred type table has no static MetadataRegistration, so the
genericMethodTable it would anchor cannot be located. Report
generic-methods-unsupported instead of reading from a null field.

* fix(il2cpp): fail closed when an inferred type table is not unique

Adversarial review found that the type-table fallback could pick another
type-shaped pointer table when the real one was unloaded or excluded, could
replace a static MetadataRegistration whose counts matched, and accepted a
run cut short by a mapping end. Require exactly one long-enough run in the
image, ending inside its mapping and loaded by code; skip the fallback when a
record's two counts matched; accept only assigned Il2CppTypeEnum kinds; and
look records up through the indexed file mapping so many mappings cannot
multiply the scan. Docs and limitations now describe the run length as at
least every referenced type index rather than an upper bound.

* fix(il2cpp): keep the type-table fallback cheap and its refusals exact

Review follow-ups: check that an address can lie in a mapping before the
throwing indexed lookup, so words pointing into gaps or zero-fill no longer
build an exception each (64 MiB: 74 s to under 2 s); treat a run starting at
its mapping start like one reaching its end; stop at the second long-enough
run instead of a 64-candidate capacity error; ignore ADRP/ADD pairs on
register 31; and say whether a counted MetadataRegistration was unusable or
no code-loaded run was found. Docs no longer generalize from one build or
state tool history they do not source.

* fix(il2cpp): judge type-table slots by their loaded value

The run scan read stored words, so a dyld-bound slot's placeholder could
decide whether a run qualified. Confirm each slot through readSlot and give
a run bounded by a bound or unresolved slot an unknown extent, so the
placeholder no longer changes the choice.

* docs(il2cpp): keep the failure-reason table layout

Shorten the two changed meanings so the table keeps its column widths and the
diff shows only those rows.

* docs(il2cpp): keep the reason table at its formatted width

* fix(il2cpp): read type-table record fields through the loader too

The previous change confirmed table slots through readSlot but still read
each record's kind and definition index from stored bytes. Read them with
readUInt32 and readSlot, and treat a record field the loader binds or leaves
unresolved like an unresolved slot: the run it bounds has an unknown extent.

* perf(il2cpp): share image mappings across loader-aware reads

---------

Co-authored-by: morluto <[email protected]>
2026-10-25 04:21:18 +08:00
morluto 22b88300d0 refactor(environment): reuse admitted capture facts in builds
Separate typed manifest/capture composition from unknown-input loading.
Builds retain the admitted replay facts locally for credential warnings and
record counts instead of parsing the same capture again. Keep full captures
out of the compact replay source and preserve manifest-first authentication,
parent identity checks, failure precedence and cancellation points.

Validation: environment source/build/report/scenario/input-snapshot/state
workflows and public build CLI; fresh runtime/catalog build, full static/docs,
test discovery and duplication checks. No external clients or targets ran.
2026-10-25 04:12:53 +08:00
morluto 162bd56c9e refactor(mcp): share pcap summary delivery handling
Use the existing named-contract retained-summary handler for pcap capture
inspection. Remove its duplicate validation, detail selection, preflight and
Evidence projection policy while preserving client inspection separately.

Validation: named pcap MCP schema/delivery, parent issue attribution and
workspace admission tests; fresh runtime/catalog build and full static/docs
checks. Native packet dissection and Linux-only retained-view tests were not
verified on this macOS host.
2026-10-25 04:12:51 +08:00
morluto a891bb80ae refactor(filesystem): preserve admitted file facts
Return the validated descriptor and its original metadata from regular-file
admission. Propagate that contract through owned reads, hashes, snapshots,
artifact inventory and provider file workflows instead of replacing initial
facts with another stat. Keep arbitrary output-handle operations separate.

Preserve final descriptor/path identity proofs, borrowed-source verification,
BigInt precision, cancellation provenance and failed-close ownership. Remove
the obsolete source and owned-ZIP baseline stages and update their consumers
and regressions. Tie read completion faults to actual reads instead of the
retired duplicate stat; instrument real handles without fake handle wrappers.

Validation: fresh runtime/catalog build; focused source, lifecycle, filesystem,
CLI/MCP and authored provider-fixture tests; full static checks; docs checks;
test discovery; duplication check. Platform-specific cases remain skipped on
macOS. No installed provider or device behavior was verified.
2026-10-25 04:09:11 +08:00
Dery Ferdikaandmorluto f196ebf806 fix(apple): refuse unpaired surrogates in IPS JSON strings (#6990)
The IPS JSON reader decodes \uXXXX escapes with String.fromCharCode and
returns raw slices verbatim, so an escaped unpaired surrogate reaches
Evidence as-is: malformed text reported under complete coverage, with no
refusal and no uncertainty. A crash report's procName or exception message
comes from the crashed process, so the escape is reachable input.

Refuse the string the way the parser already refuses other malformed
content. The check runs on the decoded value, so it covers escaped and
unescaped text alike, and a high surrogate followed by its low surrogate
escape stays valid text.

Validation: RED then green in src/native/apple/losslessJson.test.ts, where
the new case previously accepted the escaped surrogate; 311 tests across
the apple parser suites pass; tsc --noEmit over the changed set exits 0;
oxlint reports 0 errors; oxfmt --check is clean; verify:module-boundaries
passes.

Co-authored-by: morluto <[email protected]>
2026-10-25 03:59:10 +08:00
Wenyu Chiou c7ae27807f fix(electron): retain scoped IPC registration provenance (#7031)
* fix(electron): retain scoped IPC registration provenance

* fix(electron): defer mutation analysis until scoped receiver admission
2026-10-25 03:57:02 +08:00
morluto 2acdd54130 test(setup): simplify prewarm fixture ownership
Flatten the prewarm cases so suite grouping does not exceed the function-size gate. Let createTestTempDirectory own cleanup instead of deleting the same roots from a second afterEach owner. Preserve all registration and warning assertions.

Validation: all four focused filesystem cases passed using temporary client configurations and injected prewarm results; no package runner or provider was launched.
2026-10-25 03:42:49 +08:00
morluto f4f7c75c66 refactor(artifacts): consolidate selected source lifetimes
Use the shared source owner for Mach-O and PyInstaller admission, descriptor and selected-path identity, hashing and cleanup. Retain lipo joining and reader-owned PyInstaller admission, caller cancellation and private PYZ leases. Centralize admission failure projection and remove the superseded PyInstaller wrapper.

Preserve physical identity verification before late hash-completion cancellation. Close an owned source before returning failed initial metadata acquisition and retain the exact handle when close fails. A focused regression failed twice before that fix and now passes.

Validation: fresh runtime build, 259 focused fixture tests, nine documentation checks and all local static/discovery/duplication gates passed. An earlier check also ran four system-lipo suite cases (three read-only native inspections) before the final source fix; those were excluded from subsequent fixture qualification.
2026-10-25 03:42:49 +08:00
Adolaniumandmorluto a5bfd04401 fix(hopper): keep installed apt dependencies at their versions during Linux setup (#6974)
* fix(hopper): keep installed apt dependencies at their versions during Linux setup

apt-get install upgrades every listed package that is already installed, so Linux setup could upgrade the system python3 and X11 libraries while installing Hopper. Runtime dependencies are now installed with --no-upgrade in a separate transaction before the Hopper archive, which may still upgrade. The setup approval plan lists both commands.

* fix(hopper): narrow the apt upgrade claim and cover split transaction failures

* fix(evidence): preserve JSON ownership through metadata updates

---------

Co-authored-by: morluto <[email protected]>
2026-10-25 03:37:20 +08:00
ShuaixinHuang ce4e48ccae fix(cli): exclude inherited values from Markdown rows (#6984)
* fix(cli): exclude inherited values from Markdown rows

* test(verifiers): follow shared command output and identity
2026-10-25 03:33:25 +08:00
Xinjian Wang 6a96f4fa4a fix(setup): materialize the pinned package-runner cache before the first client start (#7032)
* fix(setup): materialize the pinned package-runner cache before the first client start

Package runners key their package cache by the requested spec, so the
entry installed for the @latest invocation that ran setup cannot serve
the pinned rea-agents@<version> registration. The first client start
installed that entry inside the client's MCP startup deadline and timed
out (three consecutive 30 s timeouts observed in one client log; a
controlled cold install with every tarball already cached took 22 s).

Setup now materializes the pinned entry once after applying
registrations: best-effort, bounded by a timeout, launched from a
neutral working directory, and non-fatal on failure. The setup result
carries the prewarm summary, interactive output prints a warning when
the step fails, and direct launcher registrations keep skipping it.

Fixes #7027

* fix(setup): gate prewarm to applied registrations and supervise the runner tree

Address review findings on the prewarm step:

- Gate the prewarm on at least one applied client registration
  (configured_<client>), so an empty plan or a skill-only run never
  installs into the package-runner cache; the step now only follows
  registrations the user approved in this transaction.
- Replace execFileOutput with a supervised spawn: on timeout the whole
  runner tree stops through cleanupWindowsProcessTree on Windows and a
  process-group kill on POSIX, because killing only the direct child
  leaves npm holding the inherited output pipes past the deadline.
- Emit started and completed progress events around the prewarm so an
  interactive cold install no longer looks like a frozen setup.

* fix(setup): forward terminal interrupts to the detached prewarm runner

A detached POSIX package-runner group no longer receives the terminal's
Ctrl-C, so setup could exit while the runner kept installing in the
background. The supervised launch now registers SIGINT and SIGTERM
forwarding for the duration of the prewarm: the runner tree is stopped
through the same family cleanup as the timeout, and the default
termination is restored afterwards. An interrupted launch reports a
failure reason instead of a hang.
2026-10-25 03:32:58 +08:00
morluto 0354759370 fix(artifacts): retain admitted ZIP ownership through extraction
Transfer the inventoried ZIP descriptor after inventory reader cleanup, then
borrow it for materialization instead of reopening the pathname. Release the
exact owner before publishing output; preserve failed owners for cleanup retry
and completed inventory facts through cancellation or later tagged failures.
This uses the existing admitted-descriptor reader API without a third carrier
hash pass. Remove the superseded materialization wrapper.

Cover identical-member carrier replacement, directory/missing replacements,
late cancellation at both handoff and final close, mixed cleanup outcomes and
retry. Root-owner test seams preserve real output-handle cleanup. Compiled
CLI/MCP ZIP, archive and report regressions also run; no benchmark is claimed.
2026-10-25 03:30:39 +08:00
morluto 8dfc6832a4 fix(apple): preserve empty IPS text and source keys
Keep observed empty JSON strings separate from missing fields in IPS process,
image, frame, register and OS-version facts. Preserve empty unlisted keys,
retain malformed UUID source text, and keep unknown architecture interpretation
separate from raw spelling. Classification excludes empty supporting fields.

Validate producer cases plus compiled CLI and stdio MCP delivery against the
same output contract. This is saved-report inspection, without target execution.
2026-10-25 03:30:38 +08:00
morluto 7fe5e5ff5e fix(artifacts): retain raw archive keys across entry projections
Publish Godot res:// declarations and PyInstaller CArchive/PYZ keys through
shared occurrence source_path while preserving safe logical extraction paths.
Consolidate PyInstaller file, directory and namespace projection through one
owner, and account for the additional retained source spelling before allocation.

Cover producer inventory, Windows separator conversion, PYZ module/data keys,
CLI extraction bytes and retained MCP inventory views. Format fixtures are
synthetic; real Godot/PyInstaller producer lanes were not run.
2026-10-25 03:30:22 +08:00
gensokyo-dev 03ed0c63d9 fix(evidence): preserve JSON ownership across metadata rebinding (#7036) 2026-10-24 19:11:38 +00:00
morluto a834ed1cab refactor(artifacts): share persistent file source ownership
Compose one source owner for Godot PCK and Node SEA admission, descriptor identity, carrier hash binding and retained handle cleanup. Preserve format-specific parsing and member streams, drain their reader operations before source close, and leave borrowed descriptor release with its caller.

Validation: runtime build and 136 focused tests passed, including 17 real-descriptor integration cases for delayed admission, cached hash mismatch, close failure and retry, borrowed handles, admission cleanup cancellation and mixed peer outcomes. Tests use local authored carriers and controlled failure seams.
2026-10-25 03:04:21 +08:00
morluto 425776174c fix(artifacts): preserve opaque Node SEA asset keys
Separate raw SEA names from safe logical extraction paths. Accept empty asset
keys, retain absolute main paths and conflicting source spellings, and assign
deterministic paths through the shared artifact path registry. Keep duplicate
raw keys malformed and include sourcePath in owned entry equality.

Validate 102 focused regressions including compiled CLI and stdio MCP inventory
and extraction, the canonical SDK schema case, 9 documentation consistency
cases, cached build, typecheck/lint and formatting. A pinned Node-generated
preparation blob is verified in a synthetic ELF carrier; real injected native
executables and the optional Docker lane were not run.
2026-10-25 02:54:17 +08:00
morluto 31fc9762a3 refactor(go): parse module records through one byte contract
Parse the original unframed module bytes for every build-info result. Remove the duplicate text facade and string-source branch while retaining observed text, framed bytes, malformed lines, quoting and replacement semantics.

Validation: 163 Go parser, image, provider and public CLI/MCP tests passed using local binary fixtures; build, static and documentation checks passed.
2026-10-25 02:44:32 +08:00
morluto faf219e90b refactor(application): use current Evidence and CLI contracts
Require the immutable Evidence read APIs already implemented by production owners. Remove the detached-only compatibility fallbacks and forwarding helpers, and bind consumers directly to their owner. Preserve editable detached exports and existing snapshot semantics.

Use the shared canonical CLI error classifier for process commands instead of reinterpreting their projection. Validation: 172 focused application and process tests, build, static checks and documentation checks passed using local fixtures.
2026-10-25 02:44:21 +08:00
morluto 3f168af2e8 refactor(native): consolidate section and PE subject ownership
Specialize the buffer-backed directory driver for its actual ELF consumers and remove the unused PE branch. Share PE subject architecture interpretation at the existing machine owner, preserving unknowns for conflicting formats and unproven managed overrides.

Validation: runtime build and 108 focused service, native parser, GPU, and CLI/MCP tests passed using local fixtures. Live descriptor-backed PE readers remain supported.
2026-10-25 02:28:09 +08:00
morluto 92f32981da refactor(mcp): remove the duplicate result delivery class
Construct the canonical ToolResultDelivery through the native continuation policy factory. Move type and lifecycle spy consumers to the core owner while preserving explicit response budgets and the deferred runtime composition.

Validation: runtime build and 335 focused tests passed; 41 Linux-only firmware cases were skipped on macOS. Tests use authored local fixtures, not real external providers.
2026-10-25 02:28:09 +08:00
morluto 9b3a633298 fix(evidence): retain caller paths across snapshot replay
Preserve the verbatim direct CLI selection in caller_input_path, separate from canonical subject identity and provider/query parameters. Fresh publication uses the existing immutable metadata import path so provider auto-recording cannot discard request provenance. Replay returns the current selection without rewriting stored Evidence or partitioning the cache by alias. Historical records without the optional field still authenticate.

Use the domain semantic projection for ledger agreement, remove the duplicate projection and subject-only replacement names, and preserve request metadata at same-invocation summary delivery. Later retained-view questions do not inherit that context. Existing one-shot parameters.input_path remains for compatibility.

Validation: 149 focused tests, 28 public CLI/MCP cases, all canonical advertised schemas through the pinned SDK, and nine documentation tests passed across the affected checks. Build, typecheck/lint, and formatting passed. Two Windows-only replay cases were skipped on macOS. Native macOS and managed fixtures were exercised; no Hopper/Ghidra/IDA runtime was started.
2026-10-25 02:20:13 +08:00
Abel YagubyanandAbelo9996 955c7c80ec fix(android): report DEX static values of static final fields (#6981)
* fix(android): report DEX static values of static final fields

Fixes #6397

* fix(android): request DEX static values only for class inspection

* fix(android): range-check wide DEX static values

* fix(android): move the bridge to version 7 after main took 6

---------

Co-authored-by: Abelo9996 <[email protected]>
2026-10-25 02:17:13 +08:00
ShuaixinHuang fcebd79ed8 feat(elf): decode Zstandard section-name tables (#6987)
* feat(elf): decode Zstandard section-name tables

* test(verifiers): follow shared command output and identity
2026-10-25 02:16:33 +08:00
ShuaixinHuang 85bc8c8977 fix(browser): preserve media type parameter spelling (#6985)
* fix(browser): preserve media type parameter spelling

* test(verifiers): follow shared command output and identity
2026-10-25 02:16:18 +08:00
Wenyu Chiou 1ca7d38764 fix(evidence): link composed workflows to consumed observations (#7028) 2026-10-25 02:11:32 +08:00
ed79bf9361 feat(artifacts): inventory and extract Node.js single executable application blobs (#7000)
* feat(artifacts): inventory and extract Node.js single executable application blobs

Adds a node-sea artifact reader that locates the NODE_SEA_BLOB payload in
Mach-O sections, ELF notes and PE RCDATA resources, decodes the blob
header, main script, optional snapshot and code cache, and named assets,
and extracts their stored bytes through the existing safe output tree.
The executable is never run. Resource-tree errors in a PE without a
NODE_SEA_BLOB name stay with native classification; truncated, unknown
or over-limit SEA declarations fail closed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_016y53PdpyMAp9RSW65fatHe

* fix(artifacts): reuse PE resource facts and bound SEA asset tables

Move the bounded NODE_SEA_BLOB name probe into the shared PE resource
layout module so SEA admission uses the same header and section mapping
as the full parser; the layout reader now accepts a header prefix with
the file size. Bound SEA asset count and decoded name bytes before
records are created and report resource bounds as limit failures. Offer
the JavaScript next probe only for main scripts with a source suffix.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_016y53PdpyMAp9RSW65fatHe

* fix(artifacts): make SEA metadata construction cancellable

---------

Co-authored-by: Matt Van Horn <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: morluto <[email protected]>
2026-10-25 02:10:41 +08:00
morluto 08b8e62914 refactor(mcp): consolidate retained-summary inspection workflows 2026-10-25 02:03:28 +08:00
morluto 9a13c4f420 refactor(config): centralize parsed registration environment keys 2026-10-25 02:03:28 +08:00
gensokyo-dev bc9efc9279 test(mcp): verify recovery before reading retained views (#7029) 2026-10-24 17:54:27 +00:00
Dhruvilkumar Ashokbhai Chodvadiyaanddhruvac29 ab30df08f7 fix(targets): keep original DOS header padding out of e_lfanew (#7013)
* fix(targets): keep original DOS padding out of e_lfanew

A complete DOS header with a nonempty relocation table that ended before
e_lfanew treated the padding dword at 0x3c as a Windows declaration. Without
a recognized Windows signature, fall back to the validated DOS structure so
original DOS images are not refused as malformed Windows targets. Preserve
unresolved-candidate handling when no relocation table delimits the padding.

* test(ghidra): cover DOS padding and unresolved header candidates

* fix(targets): read Windows signatures before dismissing DOS padding

* docs(targets): reconcile DOS padding admission contract

Document that relocation-table padding inside a complete DOS header is DOS data, while an unread Windows signature beyond the current probe is requested before that rule applies.

Aligns docs/installation.md with docs/ghidra-dos.md after the MZ admission fix.

---------

Co-authored-by: dhruvac29 <[email protected]>
2026-10-25 01:54:20 +08:00
Stoian Andrei-ȘerbanandStoian Andrei-Șerban 403fef0d40 docs(i18n): add complete Romanian README (#7026)
Co-authored-by: Stoian Andrei-Șerban <[email protected]>
2026-10-25 01:54:08 +08:00
niko3xandmorluto de37dfbcc9 fix(website): correct Turkish translation errors (#7015)
* fix(website): correct Turkish translation errors

Correct misleading technical senses in the Turkish catalogue: port,
caller, assembly, instruction, checks, script and oracle, plus
capitalized suffixes, hâlâ, and Turkish number formatting.

Leaves msgid, msgctxt, #: references, obsolete entries and placeholders
untouched across 308 msgstr values.

* fix(website): preserve Turkish words and quotation context

---------

Co-authored-by: morluto <[email protected]>
2026-10-25 01:51:24 +08:00
morluto 535926ca2b fix(analysis): retain one-shot target selection provenance
Retain the exact selected CLI path in one-shot provider Evidence while preserving canonical subject identity and provider inputs. The shared request-provenance boundary replaces the artifact-only special case across native, managed, and bundle-resource routes. Remove the redundant parameter fallback and make the private managed helper receive explicit Evidence parameters.

Validation: real macOS codesign fixtures through the compiled CLI retained app aliases for signature, plist, and absent asset inspections, with direct-target controls. Archive selection, managed direct analysis, and snapshot replay regressions passed; Windows-specific replay cases were skipped on macOS. Build, static, format, and docs checks passed. Snapshot-backed direct request provenance still follows its existing authenticated query contract.

Refs: #5479
2026-10-25 01:45:23 +08:00
morluto 84c30609e1 fix(browser): include IndexedDB versions in storage fingerprints
Hash the observed database version with object-store definitions at the CDP boundary. Missing, invalid, or imprecise versions preserve store evidence with incomplete coverage and an explanation. Mark the revised projection so historical store-only hashes cannot create false changes or prove unchanged versions. Preserve comparable metadata and independent storage changes.

Remove the redundant storage comparability wrapper and use each fingerprint's parsed scope instead of interpreting its identity string.

Validation: producer-to-comparison regressions and a compiled CLI to stdio MCP comparison passed, including version-only changes, invalid versions, legacy projections, and complete database additions/removals. Static and docs checks passed. CDP responses were simulated; no real browser runtime was exercised.
2026-10-25 01:45:23 +08:00
morluto 8b57dc630e fix(native): preserve metadata uncertainty in coverage
Explain unreadable ELF note section names without discarding complete note coverage. Keep export and PE import rows when only version or DLL-name metadata is unresolved, and stop claiming those retained rows were unreported.

Use native byte equality for the 4 MiB ELF extended-numbering source-preservation checks; generic deep equality enumerated every buffer byte and exhausted the test worker heap. The full fixture and byte coverage remain.

Validation: focused ELF note, native export, and PE import regressions plus complete CLI/MCP adapter files passed under the existing 512 MiB heap limit. Build, typecheck/lint, formatting, and documentation checks passed.
2026-10-25 01:45:23 +08:00
morluto 12f6e0974c fix(cli): retain Cutter discovery through shared finalization 2026-10-25 01:34:37 +08:00
morluto 900f21b982 fix(mcp): centralize validated partial Evidence delivery 2026-10-25 01:34:37 +08:00
Dhruvilkumar Ashokbhai Chodvadiyaanddhruvac29 97194c4b48 fix(ghidra): admit recordless MIPS-I legacy targets (#7014)
* fix(ghidra): admit recordless MIPS-I legacy targets

MIPS-I headers carry a zero-value ISA field (EF_MIPS_ARCH_1) and predate the ABI flags record. The recordless legacy admission only accepted EF_MIPS_ARCH_32 and EF_MIPS_ARCH_32R2, so every MIPS-I target was refused before provider selection even when its ELF32/ET_EXEC shape matched the existing inferred-o32 lane.

Admit MIPS-I through the same recordless legacy path, pin the endian-matched 32-bit MIPS language, and use a distinct support-lane identity so profile-bound snapshots invalidate. Declared-ABI MIPS-I headers remain refused because that lane is verified for MIPS32r2 only. The session reports that the generic 32-bit language also decodes later MIPS32 instructions and that MIPS-I subset conformance is not established from the header.

Fixes #6933.

* fix(ghidra): bind MIPS profiles to their target

Refuse a committed MIPS lane or ELF metadata that was resolved for a different target, including the same-endian MIPS-I and MIPS32 legacy lanes that share a language pin. Report the committed caller-selected MIPS-I language in the session limitation instead of claiming the provider pin.

---------

Co-authored-by: dhruvac29 <[email protected]>
2026-10-25 01:16:51 +08:00
Wenyu Chiou a1967fbbdb fix(lldb): retain completed trace facts through cleanup failures (#7023) 2026-10-25 01:16:26 +08:00
gensokyo-dev 422ca5a8f8 test(cli): follow prepared projection ownership during cancellation (#7022) 2026-10-24 16:46:34 +00:00
gensokyo-dev 397aa62f2e test(web): align manifest path errors with artifact ownership (#7021) 2026-10-24 16:18:25 +00:00
41cf869949 fix(environment): bound create learning and invalidate stale write proofs (#6929)
* feat(environment): report what an environment serves with an environment-coverage view

A new inspect_analysis_view kind over capture Evidence. It projects the
state an environment derives with the given template rules and reports
each tool as answered from state, replayed with its view reason, a served
write, or a withheld write with its reason, the records behind it and a
hint about what to record next. The hints cover every write reason, so a
new reason without one fails to compile.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* feat(environment): build an environment bundle in one command

rea environment-build <capture> [directory] inspects a HAR (or reads
capture Evidence), projects the environment manifest from the in-memory
Evidence, authenticates the pair, prepares the state and writes the bundle
environment-export writes. Every bundle file is byte-identical to the
three-step path for the same capture and options.

The report adds the capture and manifest identities, the coverage summary,
warnings (a loopback capture named after its file, credential-named body
members by pointer) and the next command. --dry-run writes nothing. The
default template rules are the four the published environments use.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* test(environment): cover environment-build and the coverage view

Domain tests check the hint table against the state view's reasons, the
summary, and the view against the environment-state view it summarizes.
Service tests build the fixture capture and compare every file with the
three-step path, a dry run, loopback naming and the refusals. A CLI test
compares a HAR built in one step with the three CLI steps byte for byte,
and an MCP test projects the view from every source kind.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* test(environment): build the real-agentenv lane's bundle with environment-build

The lane now runs the command users run: it builds the Grafana-shaped HAR
with environment-build and the packed checkout, checks the report, then
runs the bundle with agent-env as before.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(environment): document environment-build and the coverage view

The agent environments guide opens with the one-step build: the report,
the hint for each withheld write reason, and what a bundle ships. The CLI
guide, the capture guide and the skill reference name the command and the
environment-coverage view.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): harden environment-build after review

- The credential scan walks with its own stack, so a deeply nested body
  cannot crash the build, and it also reports credential-named header
  names, query names, form body members and string arrays.
- The build checks SIGINT between stages: cancelled before the bundle
  stage it writes nothing, during the write it names the written bundle.
- A dry run opens the package tarball, so it refuses what a build refuses.
- Repeated template rules are refused, a HAR saved as .json gets a hint,
  file-derived names follow the bundle name rules, `localhost.` counts as
  a loopback origin, and the next command is shell-quoted.
- --capture-timeout-ms passes through to the HAR inspection, and a
  missing directory is refused before inspection.
- The no-observation hint covers updates and deletes of entities the state
  never held; the coverage view states that it does not apply the
  serve-time admission checks; the inspect_analysis_view description
  names the view.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(environment): state the build's conditions and record its verification

The guides now state when the one-step bundle equals the three-step one,
which headers inspection excludes, what the credential warning scans, how
cancellation behaves, and that the coverage view does not apply the
serve-time admission checks. The spec folder records the census, the end
to end runs, the gates and the review.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): dispatch a pending interrupt before each build stage check

The manifest, state and coverage stages run synchronously, so a SIGINT
that arrived during them was only dispatched once the bundle write had
begun. Each check now yields first, so an interrupt before the bundle
stage cancels the build with nothing written.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): scan scenario credentials, own the dry-run tarball and serialize Evidence once

A browser scenario's retained header values and the query parameters of its recorded URLs are scanned for credential
names as a HAR's are, through a review projection of its records. A dry run admits and releases the package tarball
through the export's regular-file scope, so a close it cannot confirm keeps its owner, and checks for cancellation
after that last await. Evidence is written into one exactly sized buffer instead of chunks joined by a copy.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(environment): record the review round's checks

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* style(environment): format the build spec

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): scan every form occurrence and MIME-less scenario bodies

The build's credential scan collapsed form bodies with Object.fromEntries, so a
repeated field kept only its last occurrence: `api_key=secret&api_key=` warned
about nothing although the Evidence ships the value, and repeated values were
undercounted. Form fields are now scanned as ordered name/value pairs, as
headers and query strings already are.

The review projection of a capture_browser_scenario dropped a request body
recorded without a media type, so the scan never saw it. Review keeps such a
body's text with an empty media type; the manifest projection still reads only
bodies it can classify.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): scan structured HAR form params and the request side of unattributed scenario transactions

A HAR request recorded with postData.params and no text is scanned by those parameters, each occurrence counting, and
the review projection keeps the request side of a scenario transaction whose response cannot be attributed, so the
credential warning covers what evidence.json ships in both cases.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): validate build paths and read dry-run tarballs

* fix(environment): report bodies too large for the credential scan

Text bodies over the scan's 4 MiB limit, including a response decoded only as a prefix, are reported as a
credential-scan-incomplete warning by record and side, never by content, so an absent credential-named-members
warning no longer reads as a complete check. The spec and agent-environments guide also state the path validation and
dry-run tarball read that e52ae1cd added.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(verification): release cold source transform services

* fix(javascript): retain capture context along covered mutation paths

* fix(environment): admit serialized exports and scan HAR form fallbacks

* fix(environment): admit in-memory build inputs against heap headroom and scan SaveHar null-text forms

The inspected HAR's Evidence and the projected manifest are checked against live heap headroom before they are
serialized, with the same check the file route uses, so a capture too large to parse and seal is refused as
resource_constraint before anything is written. A request whose postData text is SaveHar's null is scanned by its
structured params.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): measure the credential scan limit in UTF-8 bytes

A body counts against the 4 MiB limit by its UTF-8 length, not its UTF-16 code units, so multibyte text over the
limit is reported as unscanned instead of parsed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(javascript): erase inline type imports before binding values

* fix(environment): parse credential form bodies by their media type

* fix(environment): review untyped retained form fields

* fix(environment): bound request comparisons and preserve write proofs

* fix(environment): keep write proofs and bundle review complete

* fix(environment): finalize entity overrides with simulated views

* test(environment): restore 0895c89b's untyped form tests and hint

Integrating main took main's credential scan and coverage view, which
dropped 0895c89b's tests and its body-unusable hint. Main's scan passes
the tests, now reading the scenario by event. The hint names the
response too, since extraction and serving also withhold a write as
body-unusable when its recorded response cannot be read.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* test(environment): scan a scenario content event recorded twice

Main's event scan reads both content events of a transaction whose
content the capture recorded twice; the test fails on 0895c89b, whose
review projection dropped that transaction's response. The spec names
event pointers among the first 20.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(environment): record the review round's checks

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): retain contradictions and trim retired state

* fix(environment): distinguish mixed array order from changed values

* fix(environment): keep dependent create state pinned

* fix(environment): keep checking retired view observations

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: Kaoru0822 <[email protected]>
Co-authored-by: morluto <[email protected]>
Co-authored-by: gensokyo-dev <[email protected]>
2026-10-24 23:57:30 +08:00
gensokyo-dev c94bd27e85 fix(runner): scope internal Node notices to the supervisor (#7020) 2026-10-24 15:57:23 +00:00
morluto d9189aebc4 fix(artifacts): retain completed acquisition facts through finalization
Forward the file owner's completed stable-read hash and byte count through descriptor-close failure or cancellation in PE resources, certificates and imports, ELF notes and kernel modules, native exports, Apple IPS, and APK signature inspection. Preserve the primary failure, exact cancellation reason, and failed-close cleanup lease.

Validation: 32 acquisition-finalization regressions across eight providers and 30 existing APK provider, lifecycle, and identity tests passed. Existing native filesystem regressions and typecheck/lint also passed. No external analysis engines or Android device were used.
2026-10-24 23:51:20 +08:00
morluto 72e75e9a48 fix(metadata): preserve declared phandles and relocation fields
Keep malformed canonical devicetree phandles unresolved instead of falling back to legacy identities. Report the PE dynamic relocation offset and section at their exact source widths with Size-gated absence and zero preservation. Clarify classic kernel-module version coverage and byte-preserving string encoding.

Validation: 158 focused parser, service, filesystem, and finalization regressions; 14 CLI/MCP tests; build, typecheck/lint, formatting, and docs checks passed. Fixtures establish parser behavior; no external analysis engines were exercised.
2026-10-24 23:51:20 +08:00
morluto e9abf39848 refactor(javascript): remove superseded full-IR analysis pipeline 2026-10-24 23:49:25 +08:00
gensokyo-dev cc7cc8155a test(javascript): bound route scan work independently of timing (#7016) 2026-10-24 15:37:10 +00:00
morluto 8c6cddcffb refactor(javascript): retire synchronous artifact graph runner 2026-10-24 23:33:09 +08:00
morluto 798450638e test(verifiers): retire superseded source-loading regression
The shared source-loading suite covers the former generic command and pre-exit lineage regression, plus package ownership, error-class identity, native module behavior, and preservation of a caller-owned compiler context. Remove the overlapping older fixture after validating those controls.
2026-10-24 23:14:20 +08:00
morluto 731109b7d7 refactor(analysis-view): consolidate result and budget ownership
Reuse the shared immutable-result reader for Interface Builder while checking its current parent binding on every read. Accept truthful optional schema outputs and validate normalized JSON before freezing or caching it, preserving explicit undefined facts without caching them.

Move accumulator construction and inline admission policy into the domain. One prepared projection retains the same budget across admission and projection while preserving standalone defaults, custom budgets, and authentication order.
2026-10-24 23:11:02 +08:00
morluto 399e2226ca refactor(resources): remove redundant helper exports and lease aliases
Keep ASAR header details and the Godot header parser module-local. Use the canonical lease types in Hopper consumers, including release-cache publication, while preserving acquisition and cleanup behavior.
2026-10-24 23:11:02 +08:00
gensokyo-dev 1184819cb9 fix(verifiers): load shared source without compiler processes (#7012) 2026-10-24 15:02:22 +00:00
morluto bff03bfc97 test(setup): retain Hermes policies across registration refresh 2026-10-24 22:54:05 +08:00
morluto 4f212c6a30 fix(analysis): preserve artifact and selection error ownership 2026-10-24 22:54:05 +08:00
69f99c5198 feat(environment): time browser scenario records so scenario environments learn a write clock (#6967)
* feat(browser): record when scenario requests were observed and how long they took

Request events carry observed_at, the wall-clock time the provider observed
the request start, and finished or failed events carry the browser's
elapsed_ms when it reports one. Both are optional, so earlier captures still
parse, and scenario comparisons leave them out as a committed built-in rule.
The real-Chrome network lane checks every request and finished response is
timed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* feat(environment): time browser scenario records so their state learns a write clock

Scenario records take startedDateTime from when each request was observed and
time from the browser's duration, as HAR entries are timed. A timed scenario
of the book session learns the same clock and serves the same writes as its
HAR; an untimed one keeps record order and learns no clock, as before.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(environment): document scenario request times and timed scenario records

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): start scenario records at the browser's own request start

The provider observes a request up to tens of milliseconds after the browser
starts it while it reads other responses, so a record starting then and
ending after the browser's duration mixed two clocks. Finished and failed
events now carry the browser's started_at, records prefer it and fall back to
observed_at, and comparisons drop it too. Tests cover the fallback for a
request that never ended or ended twice, a partly untimed capture keeping
record order, comparing an untimed capture with a timed one, and the
real-Chrome lane checks every finished request carries the browser's start
and duration.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(environment): document the browser start time, its fallback lag and older readers

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(environment): record the scenario timing verification

Gates, the real-Chrome lane, prompt-recorded and re-recorded published
scenario captures built with and without their times, the stale published
environments left unrepublished, and the review.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* test(browser): compare timed completion events in the request-time comparison test

The comparison test's captures had no finished or failed events, so dropping
started_at and elapsed_ms from them went untested. The captures now end one
request and fail the other, the test asserts both carry times, compares them
shifted by seven seconds and against an untimed capture, and a control shows
a changed failure on a timed completion is still reported.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: morluto <[email protected]>
2026-10-24 22:48:50 +08:00
b4ba96a9a5 feat(firmware): inspect flattened devicetree blobs (#7009)
* feat(firmware): inspect flattened devicetree blobs

Adds inspect-devicetree and inspect_devicetree for FDT v16/v17 blobs
without target execution or external dependencies. Results include
validated headers, reservations, a node/property tree with bounded raw
previews, and derived standard values and selected phandle references.
Parsing enforces file, tree, and decoded representation budgets and
rejects malformed input without partial success. Summary delivery
supports retained Evidence pagination, with tests covering parsing,
service validation, CLI delivery, and MCP access after the input is
removed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_016y53PdpyMAp9RSW65fatHe

* fix(firmware): reject conflicting devicetree phandles and bound page summaries

A node whose phandle and linux,phandle disagree no longer resolves under
either value; the conflict is reported as a limitation. Finalization
failures after a completed read retain the file-snapshot-hash partial
observation, matching the Go provider. Devicetree view pages carry
fixed-size context and counts instead of repeating summary strings, so a
large bootargs no longer pushes every page over the MCP response budget.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_016y53PdpyMAp9RSW65fatHe

---------

Co-authored-by: Matt Van Horn <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: morluto <[email protected]>
2026-10-24 22:45:22 +08:00
Ahmed 7e57b8c212 fix(process): preserve caller-selected working directory in scenario validation errors (#7003)
* fix(process): preserve caller-selected working directory in scenario validation errors

* test(process): cover caller-selected working-directory alias preservation
2026-10-24 22:41:34 +08:00
dba8a31978 feat(elf): inspect Linux kernel module metadata (#7010)
* feat(elf): inspect Linux kernel module metadata

Adds inspect-kernel-module and inspect_kernel_module for ELF32/ELF64
ET_REL Linux kernel modules, read statically without executing them.
Reports ordered .modinfo entries with duplicates preserved, __versions
symbol CRCs, the presence of .gnu.linkonce.this_module and __ksymtab,
.init.text and .exit.text sizes, and an appended module signature
trailer with its PKCS#7 byte range and SHA-256. gzip, xz and zstd
containers are reported unsupported with external decompression as the
next probe. Malformed headers and exhausted entry budgets return no
partial success.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_016y53PdpyMAp9RSW65fatHe

* fix(elf): bound kernel module ksymtab listing and tighten contracts

Charge listed __ksymtab* sections against max_entries, omit section names
longer than 256 bytes with ksymtab_name_exceeds_limit, and aggregate the
non-file-backed limitation so repeated huge names cannot inflate Evidence.
Use fixedTupleSchema for the signature pad bytes with projection parity
tests, and assert the late-cancellation reason and retained observation.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_016y53PdpyMAp9RSW65fatHe

* fix: resolve merge integration errors

---------

Co-authored-by: Matt Van Horn <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: morluto <[email protected]>
2026-10-24 22:35:20 +08:00
morluto de088fcec6 fix(verifiers): join model fixture request finalization
Keep each admitted HTTP handler owned through Evidence writes and error rendering, including after its client disconnects. Close joins graceful transport shutdown and handler work before publishing completion, and propagates exact late failures while preserving ordinary request diagnostics.

Share lifecycle ownership across OpenAI, Anthropic, and Gemini fixtures while retaining protocol-specific parsing and responses. Exercise real disconnects, graceful concurrent and repeated close, ordinary 400s, and opaque late failures.
2026-10-24 22:28:39 +08:00
morluto 48ff37f6ea fix(verifiers): consolidate shared helper ownership
Use JSON.parse directly in package verification and retain one parsed Cursor recovery snapshot. Route native command source loading through the shared transform-service owner so completed command cleanup leaves no transform child.

Preserve JSONC validation and caller inputs. Cover the actual public source-loading lifecycle with an authored command and verified process lineage.
2026-10-24 22:28:39 +08:00
791524c2fa fix(importer): reach package-main selection for trailing-slash directory references (#4219)
* fix(importer): reach package-main selection for trailing-slash directory references

posix.normalize preserves a trailing slash in the specifier, so normalized was sub/ while dirname(match) was sub; the resolvedThroughIndex comparison failed and selectPackageMain never ran for require("./sub/"). Compare against the slash-stripped directory so trailing-slash directory references take the same package-main path as the plain spelling. Boundary rows pair each trailing-slash request with the existing manifest fixtures and are validated against Node's own resolver in the same table.

Fixes #4189

* fix(importer): derive trailing-slash directory identity with path semantics

The lookbehind strip kept a trailing slash when the final component ended in a line terminator (require("./pkg\n/")), so dirname(match) still never matched and the trailing-slash path skipped package-main selection for those directories. Join the normalized path with "." instead: path semantics collapse any trailing slash run regardless of the preceding character, and behave identically to the strip on every other spelling. Boundary fixture gains a newline directory with a package.json, an index and a declared main so the index path (not the no-match branch) exercises the comparison.

Addresses Codex P1 on #4219

* fix(importer): normalize package-main targets with path semantics

---------

Co-authored-by: Hemang Varshney <[email protected]>
Co-authored-by: Kaoru0822 <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-24 23:14:19 +09:00
c6a2576aba fix(contracts): reject NUL bytes in objdump and rizin local path inputs (#3600)
Co-authored-by: qorvr <[email protected]>
Co-authored-by: Kaoru0822 <[email protected]>
2026-10-24 23:13:34 +09:00
3d84215bb2 feat(pe): inspect TLS callbacks and load configuration (#7008)
* feat(pe): inspect TLS callbacks and load configuration

Adds inspect-pe-load-config and inspect_pe_load_config for PE32 and
PE32+ images, with artifact hashes and file ranges in the returned
evidence. Reports callback addresses and mappings, plus size-covered
security-cookie, SafeSEH, CFG, EH-continuation and XFG fields;
flag-derived mitigation claims are explicitly labeled inferred. Missing
directories remain null, and malformed storage or exhausted read budgets
return errors without partial success. Adds documentation and tests for
service validation, CLI/MCP access, malformed inputs and stable-file
handling.

x86 images that declare the legacy 64-byte load-config directory are
read to the structure's own Size when those bytes are file-backed, as
the Windows loader does.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_016y53PdpyMAp9RSW65fatHe

* fix(pe): retain snapshot facts and bound load-config section lookup

Keep the completed stable-read path, SHA-256 and byte count as a
file-snapshot-hash partial observation when descriptor close fails or is
cancelled. Report the checked Size span as structure_location and add it
to Evidence locations beside the declared directory range. Resolve
callback sections through a sorted segment index built once per image,
keeping the ambiguity failure. Pin every load-config field to offsets
transcribed from the PE/COFF spec and llvm COFF.h at its exact Size
boundary for PE32 and PE32+.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_016y53PdpyMAp9RSW65fatHe

---------

Co-authored-by: Matt Van Horn <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-24 22:06:41 +08:00
02e049744f feat(environment): build agent environments from a prompt (#6946)
* feat(environment): report what an environment serves with an environment-coverage view

A new inspect_analysis_view kind over capture Evidence. It projects the
state an environment derives with the given template rules and reports
each tool as answered from state, replayed with its view reason, a served
write, or a withheld write with its reason, the records behind it and a
hint about what to record next. The hints cover every write reason, so a
new reason without one fails to compile.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* feat(environment): build an environment bundle in one command

rea environment-build <capture> [directory] inspects a HAR (or reads
capture Evidence), projects the environment manifest from the in-memory
Evidence, authenticates the pair, prepares the state and writes the bundle
environment-export writes. Every bundle file is byte-identical to the
three-step path for the same capture and options.

The report adds the capture and manifest identities, the coverage summary,
warnings (a loopback capture named after its file, credential-named body
members by pointer) and the next command. --dry-run writes nothing. The
default template rules are the four the published environments use.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* test(environment): cover environment-build and the coverage view

Domain tests check the hint table against the state view's reasons, the
summary, and the view against the environment-state view it summarizes.
Service tests build the fixture capture and compare every file with the
three-step path, a dry run, loopback naming and the refusals. A CLI test
compares a HAR built in one step with the three CLI steps byte for byte,
and an MCP test projects the view from every source kind.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* test(environment): build the real-agentenv lane's bundle with environment-build

The lane now runs the command users run: it builds the Grafana-shaped HAR
with environment-build and the packed checkout, checks the report, then
runs the bundle with agent-env as before.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(environment): document environment-build and the coverage view

The agent environments guide opens with the one-step build: the report,
the hint for each withheld write reason, and what a bundle ships. The CLI
guide, the capture guide and the skill reference name the command and the
environment-coverage view.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): harden environment-build after review

- The credential scan walks with its own stack, so a deeply nested body
  cannot crash the build, and it also reports credential-named header
  names, query names, form body members and string arrays.
- The build checks SIGINT between stages: cancelled before the bundle
  stage it writes nothing, during the write it names the written bundle.
- A dry run opens the package tarball, so it refuses what a build refuses.
- Repeated template rules are refused, a HAR saved as .json gets a hint,
  file-derived names follow the bundle name rules, `localhost.` counts as
  a loopback origin, and the next command is shell-quoted.
- --capture-timeout-ms passes through to the HAR inspection, and a
  missing directory is refused before inspection.
- The no-observation hint covers updates and deletes of entities the state
  never held; the coverage view states that it does not apply the
  serve-time admission checks; the inspect_analysis_view description
  names the view.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(environment): state the build's conditions and record its verification

The guides now state when the one-step bundle equals the three-step one,
which headers inspection excludes, what the credential warning scans, how
cancellation behaves, and that the coverage view does not apply the
serve-time admission checks. The spec folder records the census, the end
to end runs, the gates and the review.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): dispatch a pending interrupt before each build stage check

The manifest, state and coverage stages run synchronously, so a SIGINT
that arrived during them was only dispatched once the bundle write had
begun. Each check now yields first, so an interrupt before the bundle
stage cancels the build with nothing written.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* feat(browser): send declared requests from a scenario page with a request step

A capture_browser_scenario step `request` sends one declared HTTP request
through the page's own fetch, with its cookies and session, the way the
application's frontend would, and reads the whole response so the capture
records the transaction like any other. The path must resolve to the start
URL's origin, and the page must be on that origin when the step runs.
Query values and header values may be literals or declared secrets, and a
header may read its value from a cookie or a local storage entry, which is
then redacted from later records like a declared secret. `expected_status`
fails the step on any other status. Scenarios without a request step are
unchanged.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* test(browser): verify request steps in a real browser through to a built environment

verify:browser:request records a write session against a fixture item API
that refuses writes without its CSRF cookie header and stored bearer
token. It checks that every request lands in its own step, that the page
values authenticated each write and appear in no record, header or body,
that an unexpected status fails the step and cancels the rest, and that
environment-build serves the recorded create, update and delete.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* feat(environment): export environment bundles over MCP

export_environment_bundle writes the bundle environment-build writes, from
capture Evidence retained in the session or one capture Evidence file,
into an absolute output directory that must not exist and whose parent
must exist, as extract_firmware and export_web_scripts require; it never
creates the parent or reuses a directory. It returns the same report,
whose schema now lives in the domain so the CLI and the tool share it, and
dry_run reports without writing. A loopback capture without a file needs a
name. The build's refusal messages no longer name CLI flags.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): scan scenario credentials, own the dry-run tarball and serialize Evidence once

A browser scenario's retained header values and the query parameters of its recorded URLs are scanned for credential
names as a HAR's are, through a review projection of its records. A dry run admits and releases the package tarball
through the export's regular-file scope, so a close it cannot confirm keeps its owner, and checks for cancellation
after that last await. Evidence is written into one exactly sized buffer instead of chunks joined by a copy.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(environment): record the review round's checks

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(skill): build agent environments from a prompt with request steps and the bundle tool

Add the environments reference to the skill and install it with setup: record
against a disposable instance, sign in through the UI, write with request
steps and read each write back, check coverage, re-record by withheld reason
and export the bundle. Route it from SKILL.md, name agent environments in the
skill description, and document the request step, the export tool and
recording writes.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* refactor(browser): split the request lane and header resolution into smaller functions

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* style(environment): format the build spec

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): say when an untimed capture learns no write clock

Coverage of a capture whose records are not all timed, which includes every
browser scenario capture, now states that the state learns no clock, so an
update stamped with a server-set modification time stays unexplained-change
however often it is recorded.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(skill): give agents a whole scenario shape with step timeouts

Agents guessed the scenario schema field by field and two hung on steps
without timeout_ms. The reference now starts from a complete scenario with a
timeout on every step, and says a scenario capture cannot serve updates the
application stamps with a modification time.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(browser): keep request steps on their origin and their page values redacted

- Follow no redirects: a redirect fails the step and nothing reaches the
  location it names (checked in real Chrome), and check the page's origin
  again in the page as the request is sent.
- Send nothing once the step's time has run out or it was cancelled while
  header values were read; the in-page deadline is what remains of the step's.
- Keep every page value redacted under a repeated or colliding label, keep a
  value's first marker when it is read again, and leave values shorter than
  8 characters unredacted rather than rewriting ids, ports and hosts.
- Refuse a header name given twice, User-Agent (Chrome drops it) and line
  breaks in literal values and prefixes; fail before sending when a secret or
  page value holds them.
- Append query entries after the path's literal query without re-encoding it.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(environment): document request step limits and word bundle refusals for both surfaces

Document redirects, timeouts, page value redaction, HttpOnly cookie sources
and header refusals for request steps. Refusals shared by environment-build
and export_environment_bundle no longer name CLI flags, and the retained
loopback refusal no longer prints an empty file name. The skill reference
asks for read-backs after each write, shows the coverage view's source, names
package_tarball for unreleased builds and the browser path a scenario ships.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): scan every form occurrence and MIME-less scenario bodies

The build's credential scan collapsed form bodies with Object.fromEntries, so a
repeated field kept only its last occurrence: `api_key=secret&api_key=` warned
about nothing although the Evidence ships the value, and repeated values were
undercounted. Form fields are now scanned as ordered name/value pairs, as
headers and query strings already are.

The review projection of a capture_browser_scenario dropped a request body
recorded without a media type, so the scan never saw it. Review keeps such a
body's text with an empty media type; the manifest projection still reads only
bodies it can classify.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(browser): close request step edge cases from the second review

- Append query entries correctly after a path ending in "?", "&" or "#".
- Refuse 3xx expected statuses, which an unfollowed redirect can never
  report, and say a redirected write may already have been applied.
- Redact a page value also as fetch sends it, without surrounding spaces
  and tabs.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(environment): document page value body costs, redirects and MCP dry runs

Document that reading a page value shrinks the bodies a capture keeps under
the default allocation limit, that redirected writes may have landed, that
the MCP dry_run still checks its output directory and which refusals name
the tool, and keep the .har hint in the HAR-as-JSON refusal for both
surfaces.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(environment): record the prompt-to-environment verification

Gates, the real-browser lanes, five environments built from prompts by an
agent on disposable VMs and evaluated with two models, and the two reviews.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* test(skill): count rollback failures from the managed skill files

The environments reference made the skill eight files, so the recovery tests now derive the counts they expect from
MANAGED_SKILL_FILES instead of fixing them at six and seven.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(adb): restore the portable Java float pattern a merge rewrote

A lint autofix during a merge unescaped the hyphens in javaFloatValuePattern, which is invalid in a v-mode character
class, so the advertised start_adb_activity schema failed to compile in every JS regex mode. The file now matches main.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(browser): show the request step inside a whole scenario

Every JSON example in the browser scenario guide is a capture_browser_scenario input the catalog test parses, so the
request step example now sits in a launch scenario instead of standing alone.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): scan structured HAR form params and the request side of unattributed scenario transactions

A HAR request recorded with postData.params and no text is scanned by those parameters, each occurrence counting, and
the review projection keeps the request side of a scenario transaction whose response cannot be attributed, so the
credential warning covers what evidence.json ships in both cases.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): validate build paths and read dry-run tarballs

* fix(environment): report bodies too large for the credential scan

Text bodies over the scan's 4 MiB limit, including a response decoded only as a prefix, are reported as a
credential-scan-incomplete warning by record and side, never by content, so an absent credential-named-members
warning no longer reads as a complete check. The spec and agent-environments guide also state the path validation and
dry-run tarball read that e52ae1cd added.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(verification): release cold source transform services

* fix(javascript): retain capture context along covered mutation paths

* fix(environment): guard request dispatch and report export limits

* fix(environment): admit serialized exports and scan HAR form fallbacks

* fix(environment): admit in-memory build inputs against heap headroom and scan SaveHar null-text forms

The inspected HAR's Evidence and the projected manifest are checked against live heap headroom before they are
serialized, with the same check the file route uses, so a capture too large to parse and seal is refused as
resource_constraint before anything is written. A request whose postData text is SaveHar's null is scanned by its
structured params.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): measure the credential scan limit in UTF-8 bytes

A body counts against the 4 MiB limit by its UTF-8 length, not its UTF-16 code units, so multibyte text over the
limit is reported as unscanned instead of parsed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(browser): send request steps only before their deadline and keep page reads on the start origin

The page receives the step's absolute deadline and a one-second start window, so a busy page that runs the request
late, or after the step is cancelled, sends nothing; local storage is read only on the start origin; the response is
drained without keeping its body; an unparseable path is a field issue; retained Evidence is refused for heap headroom
before it is serialized; and the skill says how to reach server-assigned ids.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* style: format integrated export admission

* fix(javascript): erase inline type imports before binding values

* fix(browser): run the request lane under the shared verifier command owner

The browser request lane runs each CLI call through createPackageVerifierCommands on a private runtime root, so a
timeout, output overflow or failure stops the CLI and the browser it launched before the root is removed, and a failed
stop is reported with the root retained. The verification record states the lane's current counts.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): scan scenario content by event and read bodies by their media type

A browser scenario's credential scan reads every retained network-content event, including a response attached
mid-flight and a transaction no record represents, with event pointers, and each transaction URL's query once; the
review record projection it replaces is removed. A body is parsed as a form or as JSON by its recorded media type,
falling back to its shape, so valid forms such as `password=x&` are scanned and sentences are not read as forms.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: Kaoru0822 <[email protected]>
Co-authored-by: morluto <[email protected]>
2026-10-24 21:51:41 +08:00
d361c53c50 feat(environment): serve writes by what their recordings show (#6897)
* fix(environment): keep read-only POSTs out of the write list

A POST that repeats the latest read of its path, tunnels only read
operations, names a lookup in its path's last meaningful word, or renews
a session no longer counts as a state write: it is replayed, does not cut
initial windows, and does not end observation searches. A tool stays a
write when a related read line changes across one of its calls.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): take an entity an unplanned write made when a read first shows it

A successful item read of an entity the gate's copy never held, after a
write no built plan models, puts the entity into the copy instead of
failing the read, so later writes to it are checked as usual. An entity a
modelled write touched is never taken back.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): plan a create into the type its returned id is read under

A POST whose path names no state type becomes a create into the type
whose item read first names an id its response returned, when the id is
new to the state; serving creates in that read's collection. A successful
POST that returned an id a type's reads only show after it also ends that
type's initial window, so entities a capture created stay out of the
initial state.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): end a type's initial reads at the write its reads disagree across

When a type's reads disagree across a write under another root, the
type's initial window now ends at the earliest write within that change
and the state is extracted again, instead of leaving every view of the
type replayed. The cut composes with the creation cut; the earlier wins.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): replay request lines that show entities in another form

A read line that shows the same entities in another form, by an extra
query parameter that adds or drops members, or conflicts with a base
line on a shared entity, is a variant: it leaves the view's checks and
the entity observations, the view names it with the query keys that pick
it out, and a call matching those keys replays its recording. A filter
role whose name matches no field needs two agreeing values.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(environment): specify and record the state-target changes

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): keep a read-only POST a write when its evidence is incomplete

Body-level container operations are skipped like query ones, a lookup is named only
by the path's last literal segment and never alongside a write word, a tool with a
redirected or unanswered call is not reclassified, and the mutation veto is resolved
over all candidates at once so another read-only call cannot explain a read change.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): answer a field selection only when the state shows it exactly

Serving tests a field selection before any variant key: a projection value a replayed
read gave, or one no checked line gave other than `*`, replays. A list sets aside a
line asking for some fields only before the identity proof, and a dropped variant's
membership change no longer rewrites the type.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): hold adopted entities and linked creates to what the recording shows

An adopted entity is charged against the view's budget like a created one. A linked
create holds only for its exemplar's path arguments: serving refuses others and the
gate counts such a call as unmodelled. A response's identity-named id links at any
length, but a short one only to a type read before the call.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(environment): record the review fixes' measurements

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): release a discarded planning pass before planning again

A rewrite cut clears the first pass, so the budget rewinds to a checkpoint taken after
the write cutoffs and created ids before the state is extracted again. Rewrite spans are
charged and appended without spreading them into an argument list.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): link no create whose id an earlier read showed, and read only whole batches

A linked create is rejected when an earlier list of its type or read of its collection
showed the returned id. Body ids are walked without spreading children into an argument
list. An RPC call reads only when every operations entry names an operation of its own.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): let the gate skip exactly the reads serving replays

Serving's replay decision moves to the domain as replayReason, and the write gate uses it:
a read serving replays can neither approve nor veto a write, and a read of every field
beside a replayed selection is checked. A selection read missing a required field is
still skipped, since part of an entity can neither confirm nor check it.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(environment): record the second review round's checks

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): read writes, links and creation cuts only from evidence that rules out other causes

- A read-only POST repeats only a read that finished before it began; the veto compares
  the latest read finished before a call with the first begun after it, and counts a
  recorded absence on one side as a change. `modify` and `replace` are write words.
- A redirected or unanswered write lets a later item read adopt the entity it may have made.
- A creation cut counts a read still running when the POST began as after it, and a list
  read shows its listed entities' own ids, not those they nest.
- A linked create's item read must come before any other tool's write.
- Line shapes are charged to the budget as they are built.
- A `fields=*` list line is whole, not a field selection.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(environment): record the third review round's checks

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): tie linked creates to their returned ids and order state by the lines left

- A linked create is observed only in a read of an id its response returned,
  envelopes included, and serving takes its recorded path values in either spelling.
- A list read shows a suffix-named identity (`projectId`) distinct across its entities.
- A read-only POST compares the read that finished last before it began.
- An entity's first sighting comes from the lines left after variant lines are dropped.
- The pairwise variant pass stops at a work limit, leaving every line checked.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(environment): record the fourth review round's checks

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): keep foreign keys, stale reads and double decoding out of write evidence

- A list read counts a suffix-named field as its entities' ids only when no
  id-named field identifies them, so an `ownerId` beside `id` cuts no window.
- A read-only POST is compared with the read that finished last among those no
  other write follows, so a consuming call stays a write.
- A linked create's source path values compare as spelled on both sides, so no
  spelling is decoded twice.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(environment): record the fifth review round's checks

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): compare linked sources by position, decode path fields once and bound read and variant scans

- A linked create's source values compare segment by segment, so a template
  naming two parameters alike cannot accept a changed first one.
- Serving fills a linked create's path fields with its arguments decoded once,
  as the gate learned them.
- The read-only check reads each related line's records once per call, between
  the nearest other writes.
- Member signatures are JSON arrays, so a name holding the separator still
  conflicts; the variant work limit counts line pairs as well as entities.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(environment): record the sixth review round's checks

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): replay late and variant-only items, fill linked path fields from the exemplar and charge membership spans

- An item line first recorded after the initial window that names a new shaping
  parameter and shows other members is a replayed variant, like an initial one.
- An item only a replayed line showed, which the state never started with,
  answers with its own recording; one the state held and lost stays missing.
- A linked create fills its path fields from its exemplar's path decoded once,
  whichever accepted spelling the call sends.
- Each membership change span is charged against the budget before it is kept.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(environment): record the seventh review round's checks

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): blame a failed read on the writes whose fields it disagrees on

A failed item read with no interfering unmodelled write contradicts its creates and
deletes, and an update only when a field the update wrote or its response showed
differs. A create is contradicted when a later read embeds it elsewhere and the state
does not. A create member is fixed to its recorded value when a constant it would
have to change derives from it: its text inside a constant, a referenced entity's name,
or an instant beside a sent date, matched by related field names.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): explain unsent update changes and nested clocks by what the recording shows

An update's unsent change can be a modified-by stamp beside a created-by counterpart, a
value derived from the one sent member stored nowhere (accepted only while the target
holds the recorded before-value), or a sent clock the call left in its own second.
Objects whose clocks all moved with the request are nested clocks, opaque tokens of a
type's own length are generated, and an enveloped body is learned as its one object.
A write takes only its recorded query, an echoed reference to a nested object and a
state word paired with other fields stay at their recorded values, and a slug is tried
before a token.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): order collections by whole lines and sort only arrays a read-back proves sorted

A collection's initial order comes from the first list line that shows every entity its
type's sightings do and applies no order. A view renders a string-array field sorted
only after a counted read-back shows the written values in an order that neither the
written order nor existing-values-first explains, and never once a read showed it
unsorted. Sort and direction parameters share one vocabulary and are never learned as
inert, and a create placed by disagreeing sorts or a random id falls back to last.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* feat(environment): serve bulk writes at a collection and target item actions

A DELETE, PUT or PATCH at a collection, or a POST at its delete path, whose body names
held entities by id is applied to each named entity, and serving refuses it unless every
id is held. A POST to an entity's action path that answers nothing or the entity is an
update of it; it is served only when a recorded call found each scalar it sets already
at that value. Served bulk bodies advertise their items, and compare probes pass them.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): decide late item lines against the lines left, charge replay keys and bisect rewrite cuts

- Item lines first recorded after the initial reads are decided once variants
  are dropped, against the lines left: a new shaping parameter showing other
  members, or a selection of some fields leaving members out, replays.
- Each replay key is charged against the budget before it is kept.
- A rewrite span's earliest write is found by bisection over the writes.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(environment): record the eighth review round's checks

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): set aside response members a path mints anew on every read

A top-level UUID or clock that differs on every repeat of one path's successful reads,
with no write between, is left out of that path's item, list and document comparisons
and named in the view's per_response_members. Serving keeps the recorded value.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): count only reads finished before a linked create began as showing its id held

A list still running when the call began may show what the call made, as the
creation cut already decides, so it no longer refuses the link.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): count a list total nested in a paging object

An item total in a top-level object beside the list, such as pageInfo.totalRows, is a
counter on the same evidence as a top-level one: it must equal the number listed, and
one that differs marks the line partial. Serving and the gate render it from the state
through its JSON pointer and leave the object's other members as recorded.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): take a created entity's nested clocks from its read-back

After a create's generated values pass, its stored nested clocks take the instants the
first later read of the entity shows, as an update's already do, so a create whose
response and read spell the clock differently is confirmed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): import the compare tool definition as a type

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(environment): describe the new write rules and trim repeated parsing

The environment and capture docs describe sorted arrays, per-response members, nested
counters, item actions, bulk writes, the new write rules and refusals, and their known
limits. `_order` is a direction only. Sorted-array retries and the per-response scan
parse only the reads they need, and identifier words come from one helper.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): confirm no update whose clocks or fields the evidence cannot show it writing

An unsent clock an update left in the second it ran keeps the update unconfirmed until
another call shows the clock stamped. A field the state lacks that the read and the
update's response both show is the update's change, so without a rule for it the read
confirms nothing. A modified-by name stamp brings its id sibling along, and created
entities are indexed by id when a failed read is checked for embeds.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): show pinned query and body values in the served catalog and apply bulk writes atomically

A pinned query parameter is required with its recorded value as the only choice, and a
call that leaves it out takes that value. A fixed body member shows its recorded value,
and an enveloped body its wrapping, in the schema, the description and the refusal.
Bulk writes go through the same query pin and update or remove their entities at once.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(environment): specify and record the write-rule changes

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): keep served creates' items missing, check same-type earlier reads, and bound late lines and read-only stretches

- An item only a replayed line showed replays only while no served create has
  added to its type, so a created and deleted item stays missing.
- A linked create's earlier item-read veto checks only reads of the candidate's
  type or collection that show the item, so another type's id does not hide it.
- An item read answered with JSON null shows no id to a creation cut.
- Late lines' member names are charged before they are kept.
- The read-only check precomputes nearest writes and passes once over each
  write-free stretch per path, skipping lines whose reads all agree.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(environment): record the ninth review round's checks

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): link creates past empty item reads, sweep repeated reads once and keep only returned ids for creation cuts

- A linked create passes over a later item read naming its id whose body shows
  no item, such as JSON null, and links the next one.
- Read-only repeated reads are found in one sweep: each path's reads wait by
  end time until a call's start passes them, and a write clears them.
- A creation cut keeps, and charges, only the shown ids some creation returned.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(environment): record the tenth review round's checks

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): take bulk ids literally, check each recorded call's query once, and keep learned sorts open to contrary reads

Bulk writes take body ids as written, refuse a call naming one entity twice, size updates from the replacements
installed, generate each entity's values from that entity, and delete only with the members their exemplar sent
besides the ids. Comparison translates bulk body ids with the target collection's mapping.

Update rules echo a creation clock sent back unchanged, require every held clock, and refuse a nested clock once any
recorded call kept its object. A recorded call sent with another query than its exemplar's, including item actions,
documents and deletes, neither confirms nor teaches its plan, and only successful calls with that query relax a
create's guards.

A counted read listing a learned array unsorted drops the sort from its view, sort evidence comes only from the
entities the read covers, and a top-level counter whose name starts with a slash is named by its escaped pointer.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(environment): record the review fixes' checks

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* perf(environment): compare variant lines through shared entities and parse each compared body once

The variant pass indexes a tool's lines by the entities they show and compares only lines that share one, charging
the index to the budget while it is held; the pairwise work limit still decides when a tool keeps every line checked.
Read-only decisions keep each response body's canonical digest, taken once while its parse is charged, across calls
and veto passes. The creation cut no longer parses reads at or after the write that ends the initial reads, and reads
the ids a body shows inside its lease. Adoption looks up a read's target only after an unplanned write before it.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(environment): record the eleventh review round's checks

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* perf(environment): list a type's collections only when the path's own holds no id, and parse variant base lines once

The write gate's item target lists the type's held collections, and the removed targets', only when the path's own
collection holds none of its ids, so a served item view's reads no longer walk every entity of the type. The variant
pass parses the base lines' queries once for all the variants they key, and not at all when it found none.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(environment): record the twelfth review round's checks and the sweep's cost

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(environment): record the 50-capture sweep's wall, CPU and peak memory

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): remove duplicate schema after main integration

* fix(environment): honor recorded bulk identity kinds

* fix(environment): retain scoped evidence and honor write defaults

* test(environment): keep SDK regressions independently scoped

* docs(environment): record scoped write verification and measured costs

* fix(environment): serve bulk writes whose response lists each entity they wrote

A bulk call answering an array with one element per entity its body names, in the body's order, is classified by
its elements, so the existing renderer serves it from the entities each call writes. Responses listing entities out
of order, with differing members or id spellings, wrapped elements or an envelope still refuse.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(environment): measure the 50 captures at the scoped-envelope head

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): classify bulk responses and release verifier transforms

* fix(environment): classify and verify complete bulk responses

* fix(javascript): retain capture context along covered mutation paths

* test(environment): verify bulk responses through the real MCP SDK

* test(environment): separate bulk response proof regressions

* fix(environment): preserve request absence and generated value relationships

* fix(environment): verify complete nested clocks on creates

* fix(javascript): erase inline type imports before binding values

* fix(environment): share complete generated value verification

* fix(verifiers): load the Windows source graph only when needed

* fix(environment): validate write responses before stored clock restamps

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: Kaoru0822 <[email protected]>
Co-authored-by: gensokyo-dev <[email protected]>
Co-authored-by: morluto <[email protected]>
2026-10-24 20:52:03 +08:00
gensokyo-dev 347b6e4d95 test(verifiers): assert shared command diagnostics and identity (#7002) 2026-10-24 12:51:53 +00:00
吴佳浩Alben 2e86294c86 fix(firmware): stop recommending a raise at the staging budget ceiling (#6971)
The staging refusal told callers to increase max_output_files or max_output_bytes within its schema maximum. At the schema ceiling that recovery does not exist, so the refusal was unactionable exactly where the least room remains.

firmwareStagingBudgetRemediation now branches on the selected value against the schema maximum: below the ceiling it names the room and the control, at the ceiling it states that a further raise is unsupported and points at a smaller firmware byte range. The schema maxima move into named constants so the advice and the accepted range cannot drift apart.

Verified with a module test for both budgets at the ceiling and one below it (red on the previous single-form remediation), plus the existing firmware suites.
2026-10-24 21:35:30 +09:00
gensokyo-dev 73320bb2c9 fix(ci): isolate browser verifier D-Bus sessions (#6999) 2026-10-24 12:15:46 +00:00
morluto 2250e67bc2 fix(verifiers): consolidate native client command lifetimes
Migrate the remaining native verifier commands to shared ownership with explicit stdin contracts, command-before-fixture cleanup, and failure receipts. Preserve observed catalog counts and unknowns, retained artifacts, native assertions, and selected launch inputs.

Remove the obsolete raw exec export and share output persistence across native verifiers. Exercise actual entrypoints with authored failed preflights and acknowledged descendants, plus filesystem failures and opaque error preservation.
2026-10-24 19:40:50 +08:00
morluto cd1878f3fe fix(javascript): bound loop capture and rest-copy expansion 2026-10-24 19:36:20 +08:00
gensokyo-dev be2811e414 fix(javascript): exclude erased imports from runtime bindings (#6994) 2026-10-24 11:22:23 +00:00
morluto 0402012a95 fix(verifiers): share command ownership and preserve stdin
Move command and retained resource lifetimes into a shared verifier owner, keeping package workspace release as a separate dependency. Migrate Pi and Hermes verification to that owner while preserving open pipes, explicit EOF, diagnostics, and retained labs.

Require positive writable finalization before accepting EOF delivery. Cover premature close, opaque failures, late ownership delivery, mixed cleanup outcomes, exact retry, and real descendant cleanup with authored process boundaries.
2026-10-24 19:16:42 +08:00
gensokyo-dev 9895f1870c test(evaluation): find failure gate within cleanup callbacks (#6988) 2026-10-24 10:50:50 +00:00
gensokyo-dev dd02d8ff69 fix(javascript): bound cyclic mutation captures by their paths (#6986) 2026-10-24 10:11:27 +00:00
morluto b22b0ef22a refactor: retire test-only fallback exports
Remove duplicate MCP defaults, the fixed CDP deadline message, and the canned Windows authority reason after verifying their only consumers were tests.

Pin independent expected budget facts, exercise active default and selected CDP deadlines, and verify exact authored native diagnostic propagation. Preserve malformed-input, cancellation, omission, and authority assertions. Verified 222 focused cases, 9 README cases, build, static checks, docs, discovery, and duplication at 3.58%.
2026-10-24 18:04:35 +08:00
morluto 89006609f1 fix(javascript): separate erased types from runtime semantics
Record erasure during definition indexing and reuse it across runtime references, captures, effects, module links, routes, Electron and Vite. Preserve source type links and emitted decorator and computed-key effects; keep actual runtime reads and escapes conservative.

Validation: 1,647 focused tests and 9 documentation tests passed, with build, static and documentation gates. The inherited reference-lifetimes stress file exhausts a 512 MiB worker on both unmodified 18451e479 and this change; that file remains unverified. TypeScript 7.0.2 emit was inspected for ES2022 and ES2018 without executing emitted code.
2026-10-24 18:00:43 +08:00
Ariel Fikru Avicennaandgensokyo-dev cab78de506 feat(ghidra): analyze GameCube/Wii DOL executables through Ghidra (#6240)
* feat(domain): parse and validate GameCube/Wii DOL headers

Add a pure parser for the 0x100-byte DOL header (7 text and 11 data
slots, BSS, entry point). Used slots must lie after the header and
inside the file and the 0x80000000..0x81800000 load window; text slots
are 4-byte aligned; sections may not overlap in memory or in the file;
the entry point must be inside a text section. BSS may overlap data
sections, as .sdata/.sdata2 do in shipped DOLs.

Refs #1741

* feat(binary): admit caller-selected GameCube/Wii DOL targets

* feat(ghidra): import GameCube/Wii DOL targets through a section-map pre-script

* test(ghidra): cover DOL launcher arguments, attestation and raw profile digests

* test(ghidra): add the real verify:ghidra:dol lane and DOL docs

* fix(ghidra): read only the DOL header and section ranges instead of the whole file

* test(ghidra): derive DOL lane addresses from readelf and admit Gekko from the install catalog

* fix(ghidra): attest DOL block permissions, space, overlay and load state

* fix(ghidra): keep load-image permissions optional for older inspections

* fix(contracts): accept DOL targets in binary_session output

* fix(ghidra): adapt the DOL lane to the current upstream address schema

Validate DOL load-image coordinates with the upstream observed-address
schema, drop a duplicate import left by the rebase, and re-pin the raw
profile digests to the unmodified upstream resolver.

* fix(ghidra): preserve bridge inventory symbol provenance

* fix(ghidra): align DOL verification with shared workflow contracts

* feat(domain): parse and validate GameCube/Wii DOL headers

Add a pure parser for the 0x100-byte DOL header (7 text and 11 data
slots, BSS, entry point). Used slots must lie after the header and
inside the file and the 0x80000000..0x81800000 load window; text slots
are 4-byte aligned; sections may not overlap in memory or in the file;
the entry point must be inside a text section. BSS may overlap data
sections, as .sdata/.sdata2 do in shipped DOLs.

Refs #1741

* feat(binary): admit caller-selected GameCube/Wii DOL targets

* feat(ghidra): import GameCube/Wii DOL targets through a section-map pre-script

* test(ghidra): cover DOL launcher arguments, attestation and raw profile digests

* test(ghidra): add the real verify:ghidra:dol lane and DOL docs

* fix(ghidra): read only the DOL header and section ranges instead of the whole file

* test(ghidra): derive DOL lane addresses from readelf and admit Gekko from the install catalog

* fix(ghidra): attest DOL block permissions, space, overlay and load state

* fix(ghidra): keep load-image permissions optional for older inspections

* fix(contracts): accept DOL targets in binary_session output

* fix(ghidra): adapt the DOL lane to the current upstream address schema

Validate DOL load-image coordinates with the upstream observed-address
schema, drop a duplicate import left by the rebase, and re-pin the raw
profile digests to the unmodified upstream resolver.

* docs(ghidra): qualify the DOL memory window

* refactor(ghidra): own DOL BSS ranges in the parsed header

* docs(ghidra): record real DOL workflows and task costs

* docs(ghidra): record final DOL integration verification

* fix(mcp): share the native procedure search row contract

* docs(ghidra): record public procedure search verification

---------

Co-authored-by: gensokyo-dev <[email protected]>
2026-10-24 17:56:31 +08:00
morluto d01ee7b19a fix(verifiers): own setup terminal and snapshot lifetimes
Join identity-verified PTY family cleanup and native exit before workspace removal, retaining failed cleanup capabilities for retry. Replace duplicate kill-and-reject loops with shared process ownership and deadlines.

Capture configuration before setup using owned file handles and compare raw bytes. Preserve interactive cancellation, terminal restoration, redirected output, and selected inputs; remove the test-only forwarding export. Verified 122 focused cases, 9 README cases, build, static checks, docs, discovery, and duplication at 3.58%.
2026-10-24 17:54:18 +08:00
Saiyam JainandSaiyam Jain ddde7276c4 test(erlang): cover lambdas names filter and chunks miss in BEAM views (#6973)
Add lambdas names-filter selector cases to checkSelectors:
- names: ['start'] returns the matching lambda with resolved fields
- names: ['missing'] returns an empty page

Add a chunks no-match case:
- names: ['Xyzz'] returns an empty page for an absent chunk tag

Also clarify in docs/erlang-beam.md what names selects for each
collection: the four-character IFF tag for chunks, the resolved
function name (never null) for lambdas, and the decoded atom name
string for atoms.

Co-authored-by: Saiyam Jain <[email protected]>
2026-10-24 17:39:57 +08:00
gensokyo-dev 48c203f356 fix(build): prepare release dependency types during installation (#6978) 2026-10-24 09:24:43 +00:00
morluto 18451e4797 test(release): type patched history options before runtime setup 2026-10-24 17:21:08 +08:00
morluto df5c48ffda docs(cutter): explain bridge descriptor discovery configuration 2026-10-24 17:21:08 +08:00
morluto 9dfa44f776 fix(cancellation): retain caller reasons at admission checkpoints 2026-10-24 17:21:08 +08:00
morluto 3fb26985c4 fix(native): classify dyld refusals before export fallback 2026-10-24 17:21:08 +08:00
morluto af3f025688 fix(il2cpp): accept names in native type-reference views 2026-10-24 17:21:08 +08:00
morluto 43c7255dbd fix(android): preserve binary class selectors across metadata calls 2026-10-24 17:21:08 +08:00
Dery Ferdikaandgensokyo-dev a45ee81167 fix(ghidra): enforce request limits before encoding (#6917)
* fix(ghidra): enforce the bridge request ceiling before writing

Hopper and Cutter both measure an oversized payload and refuse it before the
request reaches its bridge, using the shared REA_BRIDGE_MAX_REQUEST_BYTES
ceiling. Ghidra had no ceiling on either end: GhidraWire.request wrote the
stringified request straight to the socket, and the Java bridge reads request
lines with an unbounded readLine() after its bounded 64 KiB handshake.

Serialize once, measure the encoded payload, and refuse before the operation
is registered as pending and before any socket write, so an oversized request
never reaches the bridge. Carry the typed constraint as the cause and
re-attribute it in projectPrimarySessionError, so callers see the same
resource constraint Hopper and Cutter already report.

The Ghidra bridge reads no ceiling of its own, so its recovery text names that
only the REA client applies the setting instead of asking for a bridge restart.

Validation: src/ghidra/GhidraClientWire.test.ts (3 cases, red then green),
src/config/bridgeMaxRequestBytes.test.ts (16), src/ghidra/GhidraRequestQueue.test.ts
(25); tsc --noEmit over the changed set exits 0; oxlint reports 0 errors;
oxfmt --check clean; verify:module-boundaries passes. The two failures in the
wider src/config+src/ghidra+src/hopper+src/cutter sweep
(captureDecoderHeap, GhidraAnalysisSeeds symlink EPERM) reproduce identically
on pristine bded9c628 with this change stashed. The Ghidra fixture suite
cannot run on this Windows host: fakeGhidra.mjs renames bridge-endpoint.json
and hits EBUSY on 3 of 3 runs, so the fixture-level test was not added.

* fix(ghidra): preflight request bytes before encoding

---------

Co-authored-by: gensokyo-dev <[email protected]>
2026-10-24 17:17:52 +08:00
f2fa7f648d feat(android): project a structured manifest component inventory (#5025)
* feat(android): project a structured manifest component inventory

* fix(android): derive provider export defaults from the target SDK and match namespaced attributes

* fix(android): bound resolved names and authorities in the component inventory

* fix(android): separate nonliteral exported values from omission

* fix(android): bound manifest component projection

* fix(android): preserve permission override semantics

* fix(android): retain unresolved package identities

* test(android): cover multibyte manifest name budget

* fix(android): measure inherited permission once

* fix(android): preserve unknown symbolic permissions

* fix(android): preserve manifest component uncertainty

* fix(android): report unsupported manifest facets

* fix(android): preserve manifest identities

* fix(android): preserve alias target references

* fix(android): validate alias targets and authorities

* fix(android): bound alias target expansion

* fix(android): index alias target spellings

* fix(android): preserve alias identities and isolate legacy defaults

* fix(android): keep legacy component defaults typed as unavailable

* docs(android): record manifest projection and real JADX costs

* fix(android): preserve SDK defaults and manifest namespaces

* docs(android): verify SDK and namespace ownership corrections

* fix(android): accept DEX class names and hexadecimal SDK levels

* docs(android): record DEX and SDK follow-up verification

* fix(android): validate manifest package identity separately

* docs(android): record final package-identity verification

* fix(android): preserve exact DEX component and alias names

* docs(android): record exact-name integration verification

* fix(android): retain declared foreground service type text

* docs(android): record final declared-value validation

---------

Co-authored-by: BaaRai <[email protected]>
Co-authored-by: morluto <[email protected]>
Co-authored-by: gensokyo-dev <[email protected]>
2026-10-24 17:14:23 +08:00
morluto d9660eb032 fix(verifiers): share package command and workspace ownership
Join canonical owned command cleanup before releasing package verifier workspaces. Replace raw package command wrappers, the updater supervisor, WASM cleanup composite, and boolean-only retention with explicit capability ownership.

Preserve selected environments, npm arguments, no-deadline calls, clean exit status, and distinct aggregate/per-stream output budgets. Exercise actual timeout descendants, cancellation, retained cleanup retry, and workspace admission.
2026-10-24 16:58:18 +08:00
morluto de9a8898dc fix(artifacts): preserve opaque close hook failures
Tag close outcomes with Result so rejected undefined remains a failure. Preserve all hook attempts, retained resource owners, aggregate causes, and cleanup retry.
2026-10-24 16:56:49 +08:00
morluto e3a6ce40f5 fix(release): recover transient commit-history reads 2026-10-24 16:47:56 +08:00
JANG BYUNGKUNandgensokyo-dev 3ce311757d feat(il2cpp): link generic method instances behind --generic-instances (#5785)
* feat(il2cpp): read generic containers on demand and render method spec names

Generic container indices and records are demand readers on the same metadata snapshot, so links that do not ask for generic instances allocate nothing. Type rendering keeps one graph walk with its workspace, depth and cycle guards; the C#-like names and the Il2CppDumper GetMethodSpecName names are two styles over it.

* feat(il2cpp): link generic method instances with admitted rows and kept issues

generic_instances links every genericMethodTable row through methodSpecs, genericInsts and genericMethodPointers, choosing the one CodeRegistration layout whose counts bound every row. The table is reserved against the report budget before any row is resolved; rows and issues are admitted before retention, and a contradicting row stays unknown with an address-backed issue instead of failing the link. Views page the rows by definition, state, address and name, unrequested and historical collections stay unknown, and Ghidra seeds fold instances after the definitions.

* feat(il2cpp): accept --generic-instances on the CLI and describe it in tool contracts

* docs(il2cpp): describe generic method instances and their limits

* fix(il2cpp): keep issued generic rows unknown and name their row

A method spec that contradicted the metadata still reported its pointer slot as code, while the schema and provider limitation promised unknown. Such rows now keep no address, every issue carries its genericMethodTable row, the domain validator requires unknown exactly on issued rows, and shared specs name one other row instead of all of them. The row-size lower bound moves to the metadata admission owner, the hex helper is shared, and budget wording says checked rather than reserved.

* test(il2cpp): reject ambiguous generic layouts and pin budget, yield and limit paths

* fix(il2cpp): keep unmapped generic slots per row and reject undeclared arguments

Layout selection no longer requires every genericMethodPointers slot to be
mapped; each row reads its own slot, so one unmapped slot leaves only that
row unknown with an issue. Method specs whose class or method argument count
differs from the owner's declared generic container are issued and unnamed.
Fixtures now declare the containers their generic rows fill.

* fix(il2cpp): page generic method issues instead of retaining them in summaries

Summaries keep generic_methods without its issues and count them in
counts.generic_method_issues; the generic_method_issues collection pages
them by rows, indices and fields, as code_gen_modules already does. Docs
also name the method_indices filter the request schema accepts.

* fix(il2cpp): read split generic arguments per slot and method containers on demand

Generic argument arrays may span adjacent loader ranges: admit them with
hasPointerArrayCoverage and read each slot through readPointer, instead of
requiring the whole array in one mapping.

Method generic container indices are read on demand from the method record,
like type containers, so no link path allocates a per-method column. An owner
whose container index is out of range now leaves the method spec unnamed with
an issue instead of naming it unchecked.

* fix(il2cpp): report generic entries past the address space at their row

A damaged methodSpecs or genericMethodPointers pointer could make a spec or
slot address exceed 64 bits. The issue kept that address, which the address
schema rejects, so inspection failed instead of leaving the row unknown.
Check the entry range first and attach the issue to the genericMethodTable
row (or its slot field).

* chore(il2cpp): remove unrelated formatting changes

* fix: restore adb float pattern and mobile fixture from main

The merge commit's lint-staged run rewrote these two main files: oxlint --fix
dropped the escape in [+\-], which is invalid under the RegExp v flag.

* fix(il2cpp): preserve unreadable generic method identities

* docs(il2cpp): record generic-instance workflow verification

* docs(il2cpp): qualify final integration checks

---------

Co-authored-by: gensokyo-dev <[email protected]>
2026-10-24 16:19:05 +08:00
ShuaixinHuang c350343c33 fix(http): preserve legal response header names (#6936) 2026-10-24 16:11:02 +08:00
ShuaixinHuang 2c3568c375 fix(cli): preserve literal HTML text in Markdown tables (#6932) 2026-10-24 16:11:00 +08:00
ShuaixinHuang c298d54e88 fix(zip): validate trailing archive record candidates (#6937) 2026-10-24 16:10:31 +08:00
Adolanium b09c6b0752 fix(apple): keep dyld candidates inside the analyzed root on Windows (#6955)
FilesystemTreeView mapped root-relative dyld paths to host paths with path.join. On Windows a component such as ..\outside\x.dylib was normalized out of the analyzed root, so a file outside it was opened, parsed and hashed. Components containing a backslash or colon are now absent on Windows, Windows link text is split on backslashes, and every host path is checked to stay inside the root before it is opened.
2026-10-24 16:10:18 +08:00
Vinícius Andrade 689f738c95 fix(process): preserve the caller abort reason in regex worker cancellations (#6959)
* fix(process): preserve the caller abort reason in regex worker cancellations

Both cancellation outcomes of matchRegexValues, the pre-admission refusal
and the abort observed while the worker runs, now carry the caller's
signal.reason as the error cause. Hopper's regex search returns that result
unchanged, so cancelled regex search_procedures and search_strings results
regain the exact reason the caller supplied.

* test(process): assert the caller abort reason for cancelled regex matches

Cover the pre-cancelled refusal and an abort raised while the worker is
running, asserting the cause is the identical reason object.
2026-10-24 16:10:07 +08:00
morluto 8328a9559f fix(gdb): keep console recovery available for multiple inferiors 2026-10-24 16:08:26 +08:00
morluto 77be87db99 fix(artifacts): describe observed inventory scope accurately 2026-10-24 16:08:26 +08:00
morluto 0c49878b9a fix(macho): preserve subtype labels across dylib consumers 2026-10-24 16:08:26 +08:00
morluto abf7d300b3 fix(pe): share header identity across native metadata readers 2026-10-24 16:08:26 +08:00
morluto 0f3a15de0d fix(javascript): preserve factory export helper identity 2026-10-24 16:08:26 +08:00
781760a195 feat(apple): inspect IPS crash reports (#6511)
* feat(apple): inspect IPS crash reports

Add inspect_apple_ips for caller-selected Apple .ips files. Parse the
first-line metadata object and, when bug_type is the string 309, the
crash body: process identity, exception and termination facts, threads,
usedImages, lossless integer registers, and documented classification
rules. Other bug types including 288 stackshots return no crash result.
Opened Mach-O and dSYM UUID joins remain unobserved.

Related to #4297

* fix(apple): keep IPS register literals exact

Use the arm64e fixture integer that matches 0x8527000193ed4944 instead
of an IEEE-754-rounded decimal, and count the lossless JSON example
length from the source text.

* fix(apple): budget IPS parsing and keep typed presence

Parse the IPS metadata and crash body through a projection of the
documented members. Members the report does not interpret are
syntax-checked without being materialized, and thread, frame, image and
the new max_registers budgets run before each row is parsed, so a large
selected file fails with a typed resource error instead of first
expanding every row. Fixed allowances bound JSON nesting, unlisted key
names and termination reasons.

Present values whose JSON type differs from the documented one are no
longer read as absence: they are listed in coverage.type_mismatches and
coverage becomes partial, including wrong-type or missing threads and
usedImages collections. osVersion keeps the producer dictionary or the
string form. Frame roles follow physical frame depth, so the top frame
of a non-faulting thread is current_instruction and inlined rows share
the following frame's role. SIGKILL classes compare the canonical hex
once, so 0xdeadd0cc no longer matches dead10cc. Invalid UTF-8 is refused
instead of being replaced, and JSON strings are decoded by copying
unescaped spans.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Ra7LM2NyQ7DumdssxbUq5g

* fix(apple): bound integer text expansion and reconcile coverage

* refactor(apple): retain image metadata at its owning row

* refactor(apple): validate row capacity through reconciled coverage

* test(apple): verify resource recovery at the public error boundary

* docs(apple): record complete IPS task and corpus measurements

* docs(apple): record integrated checks and stdio corpus evidence

* docs(apple): record PE integration and baseline cleanup controls

* fix(native): bound IPS string decoding and preserve invalid identities

* refactor(native): use exact optional IPS source evidence

* docs(native): verify IPS escape capacity and complete-task costs

* test(native): verify IPS through production stdio MCP

* style(tests): format IPS advertised schema bindings

* fix(native): preserve unresolved IPS references and recorded frame semantics

* docs(native): record IPS reference review evidence

* fix(apple-ips): enforce JSON and report consistency

* docs(apple-ips): record final consistency verification

---------

Co-authored-by: Matt Van Horn <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: gensokyo-dev <[email protected]>
2026-10-24 16:02:59 +08:00
977c9727e6 feat(environment): give more write tools a state target (#6385)
* fix(environment): keep read-only POSTs out of the write list

A POST that repeats the latest read of its path, tunnels only read
operations, names a lookup in its path's last meaningful word, or renews
a session no longer counts as a state write: it is replayed, does not cut
initial windows, and does not end observation searches. A tool stays a
write when a related read line changes across one of its calls.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): take an entity an unplanned write made when a read first shows it

A successful item read of an entity the gate's copy never held, after a
write no built plan models, puts the entity into the copy instead of
failing the read, so later writes to it are checked as usual. An entity a
modelled write touched is never taken back.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): plan a create into the type its returned id is read under

A POST whose path names no state type becomes a create into the type
whose item read first names an id its response returned, when the id is
new to the state; serving creates in that read's collection. A successful
POST that returned an id a type's reads only show after it also ends that
type's initial window, so entities a capture created stay out of the
initial state.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): end a type's initial reads at the write its reads disagree across

When a type's reads disagree across a write under another root, the
type's initial window now ends at the earliest write within that change
and the state is extracted again, instead of leaving every view of the
type replayed. The cut composes with the creation cut; the earlier wins.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): replay request lines that show entities in another form

A read line that shows the same entities in another form, by an extra
query parameter that adds or drops members, or conflicts with a base
line on a shared entity, is a variant: it leaves the view's checks and
the entity observations, the view names it with the query keys that pick
it out, and a call matching those keys replays its recording. A filter
role whose name matches no field needs two agreeing values.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(environment): specify and record the state-target changes

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): keep a read-only POST a write when its evidence is incomplete

Body-level container operations are skipped like query ones, a lookup is named only
by the path's last literal segment and never alongside a write word, a tool with a
redirected or unanswered call is not reclassified, and the mutation veto is resolved
over all candidates at once so another read-only call cannot explain a read change.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): answer a field selection only when the state shows it exactly

Serving tests a field selection before any variant key: a projection value a replayed
read gave, or one no checked line gave other than `*`, replays. A list sets aside a
line asking for some fields only before the identity proof, and a dropped variant's
membership change no longer rewrites the type.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): hold adopted entities and linked creates to what the recording shows

An adopted entity is charged against the view's budget like a created one. A linked
create holds only for its exemplar's path arguments: serving refuses others and the
gate counts such a call as unmodelled. A response's identity-named id links at any
length, but a short one only to a type read before the call.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(environment): record the review fixes' measurements

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): release a discarded planning pass before planning again

A rewrite cut clears the first pass, so the budget rewinds to a checkpoint taken after
the write cutoffs and created ids before the state is extracted again. Rewrite spans are
charged and appended without spreading them into an argument list.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): link no create whose id an earlier read showed, and read only whole batches

A linked create is rejected when an earlier list of its type or read of its collection
showed the returned id. Body ids are walked without spreading children into an argument
list. An RPC call reads only when every operations entry names an operation of its own.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): let the gate skip exactly the reads serving replays

Serving's replay decision moves to the domain as replayReason, and the write gate uses it:
a read serving replays can neither approve nor veto a write, and a read of every field
beside a replayed selection is checked. A selection read missing a required field is
still skipped, since part of an entity can neither confirm nor check it.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(environment): record the second review round's checks

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): read writes, links and creation cuts only from evidence that rules out other causes

- A read-only POST repeats only a read that finished before it began; the veto compares
  the latest read finished before a call with the first begun after it, and counts a
  recorded absence on one side as a change. `modify` and `replace` are write words.
- A redirected or unanswered write lets a later item read adopt the entity it may have made.
- A creation cut counts a read still running when the POST began as after it, and a list
  read shows its listed entities' own ids, not those they nest.
- A linked create's item read must come before any other tool's write.
- Line shapes are charged to the budget as they are built.
- A `fields=*` list line is whole, not a field selection.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(environment): record the third review round's checks

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): tie linked creates to their returned ids and order state by the lines left

- A linked create is observed only in a read of an id its response returned,
  envelopes included, and serving takes its recorded path values in either spelling.
- A list read shows a suffix-named identity (`projectId`) distinct across its entities.
- A read-only POST compares the read that finished last before it began.
- An entity's first sighting comes from the lines left after variant lines are dropped.
- The pairwise variant pass stops at a work limit, leaving every line checked.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(environment): record the fourth review round's checks

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): keep foreign keys, stale reads and double decoding out of write evidence

- A list read counts a suffix-named field as its entities' ids only when no
  id-named field identifies them, so an `ownerId` beside `id` cuts no window.
- A read-only POST is compared with the read that finished last among those no
  other write follows, so a consuming call stays a write.
- A linked create's source path values compare as spelled on both sides, so no
  spelling is decoded twice.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(environment): record the fifth review round's checks

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): compare linked sources by position, decode path fields once and bound read and variant scans

- A linked create's source values compare segment by segment, so a template
  naming two parameters alike cannot accept a changed first one.
- Serving fills a linked create's path fields with its arguments decoded once,
  as the gate learned them.
- The read-only check reads each related line's records once per call, between
  the nearest other writes.
- Member signatures are JSON arrays, so a name holding the separator still
  conflicts; the variant work limit counts line pairs as well as entities.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(environment): record the sixth review round's checks

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): replay late and variant-only items, fill linked path fields from the exemplar and charge membership spans

- An item line first recorded after the initial window that names a new shaping
  parameter and shows other members is a replayed variant, like an initial one.
- An item only a replayed line showed, which the state never started with,
  answers with its own recording; one the state held and lost stays missing.
- A linked create fills its path fields from its exemplar's path decoded once,
  whichever accepted spelling the call sends.
- Each membership change span is charged against the budget before it is kept.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(environment): record the seventh review round's checks

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): decide late item lines against the lines left, charge replay keys and bisect rewrite cuts

- Item lines first recorded after the initial reads are decided once variants
  are dropped, against the lines left: a new shaping parameter showing other
  members, or a selection of some fields leaving members out, replays.
- Each replay key is charged against the budget before it is kept.
- A rewrite span's earliest write is found by bisection over the writes.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(environment): record the eighth review round's checks

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): count only reads finished before a linked create began as showing its id held

A list still running when the call began may show what the call made, as the
creation cut already decides, so it no longer refuses the link.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): keep served creates' items missing, check same-type earlier reads, and bound late lines and read-only stretches

- An item only a replayed line showed replays only while no served create has
  added to its type, so a created and deleted item stays missing.
- A linked create's earlier item-read veto checks only reads of the candidate's
  type or collection that show the item, so another type's id does not hide it.
- An item read answered with JSON null shows no id to a creation cut.
- Late lines' member names are charged before they are kept.
- The read-only check precomputes nearest writes and passes once over each
  write-free stretch per path, skipping lines whose reads all agree.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(environment): record the ninth review round's checks

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(environment): link creates past empty item reads, sweep repeated reads once and keep only returned ids for creation cuts

- A linked create passes over a later item read naming its id whose body shows
  no item, such as JSON null, and links the next one.
- Read-only repeated reads are found in one sweep: each path's reads wait by
  end time until a call's start passes them, and a write clears them.
- A creation cut keeps, and charges, only the shown ids some creation returned.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(environment): record the tenth review round's checks

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* perf(environment): compare variant lines through shared entities and parse each compared body once

The variant pass indexes a tool's lines by the entities they show and compares only lines that share one, charging
the index to the budget while it is held; the pairwise work limit still decides when a tool keeps every line checked.
Read-only decisions keep each response body's canonical digest, taken once while its parse is charged, across calls
and veto passes. The creation cut no longer parses reads at or after the write that ends the initial reads, and reads
the ids a body shows inside its lease. Adoption looks up a read's target only after an unplanned write before it.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(environment): record the eleventh review round's checks

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* perf(environment): list a type's collections only when the path's own holds no id, and parse variant base lines once

The write gate's item target lists the type's held collections, and the removed targets', only when the path's own
collection holds none of its ids, so a served item view's reads no longer walk every entity of the type. The variant
pass parses the base lines' queries once for all the variants they key, and not at all when it found none.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(environment): record the twelfth review round's checks and the sweep's cost

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: Kaoru0822 <[email protected]>
2026-10-24 15:58:32 +08:00
gensokyo-dev 4a041ae753 perf(artifacts): bound stable reads to one snapshot buffer (#6966)
* perf(artifacts): read stable files into one bounded buffer

* perf(artifacts): fill stable snapshots through direct descriptor reads

* docs(perf): qualify stable snapshot memory with real ELF and BEAM tasks

* test(artifacts): count descriptor close retries from admission

* test(artifacts): capture the retained descriptor after asynchronous admission

* docs(perf): record integrated qualification and cleanup fixture correction
2026-10-24 15:49:41 +08:00
Adolanium c2068b94d7 fix(browser): parse repeated CSP and policy header field lines separately (#6963)
CDP joins repeated response header field lines with LF. Only the Link projection split them, so a repeated Content-Security-Policy lost the first directive of each later policy, and a repeated Referrer-Policy reported no policy. Header normalization now keeps each field line, CSP parses every line and comma-separated policy, single-valued fields combine lines with a comma, and Link uses the shared lines.
2026-10-24 15:45:05 +08:00
Dhruvilkumar Ashokbhai Chodvadiyaanddhruvac29 e00ee310f5 fix(functions): name the rejected side and supplied kind in function Evidence errors (#6969)
fix(functions): name the rejected side and supplied kind in Evidence errors

Function comparison previously reported only that analyze_function/rea.analysis Evidence was required, leaving callers to discover which side was wrong and what was supplied. Include the side, its operation and predicate, and the expected pair in the input issue.

Co-authored-by: dhruvac29 <[email protected]>
2026-10-24 15:44:39 +08:00
morluto 72ba1cd92b fix(verifiers): centralize failure and cleanup ownership
Own operation results and exact rejections in withVerifierCleanup, including
undefined, null and independent cleanup failures. Remove the optional-primary
cleanup contract and migrate every caller, preserving returns, annotations,
signal restoration and dependent cleanup order.

Make Frida/JEB reporters fail on reasonless rejections and retain JEB fixture
files while provider cleanup is incomplete. Own the nested packaged WASM
verifier and expose its composite process/root cleanup capability for retry.
Update runnable docs and isolated fixture dependencies.

Validation: 547 focused cases in 20 files, eight isolated boundary unit checks,
static/build/docs/discovery gates and duplication check (3.58%). Node process,
SDK and descriptor cleanup are exercised; provider and packaging results in
orchestration tests are simulated. Pack/install subprocess ownership remains
with the existing shared package runner outside the nested-command change.
2026-10-24 15:38:35 +08:00
Dery Ferdikaandgensokyo-dev 98ec00f406 fix(setup): preserve cleanup ownership and resource reports (#6920)
* fix(setup): report host-owned cleanup resources alongside the primary's

SetupHost.close documents that it reports the resources a setup host still
owns, but its contract returned only a string. runSetup therefore built the
ProviderCleanupError from the primary failure's cleanupResources alone, so a
resource the host failed to release reached the projection only inside a
free-text reason while the error asks the caller to review the reported local
resources.

Return a SetupCloseReport instead. closeSetupResources absorbs the structured
resources of any cause that reports them, through
ArtifactReaderFailure.cleanupObservation, and leaves opaque failures
message-only so no resource name is invented. runSetup then unions the host's
resources with the primary's before building the typed cleanup error.

Two boundary tests read the close result directly: the skill-cleanup case now
asserts the retained backup path in the report reason rather than in a bare
string, and the shared ownership helper takes an opaque close result because it
only ever awaits undefined from either host it is given.

Validation: RED then green in src/application/Setup.recovery.test.ts, where
the new case previously received only the primary resource; 116 tests across
the seven Setup and cleanup-ownership suites pass; tsc --noEmit over the
changed set, its setup callers, and both boundary tests exits 0; oxlint
reports 0 errors; oxfmt --check is clean; verify:module-boundaries passes.
The two failures in src/setupEnvironment.test.ts reproduce identically on
pristine bded9c628 with this change stashed.

* fix(setup): preserve cleanup ownership through host reports

* fix(setup): bind finalization reports to cleanup owners

* fix(setup): retain live resources in returned cleanup views

* fix(setup): preserve completed cleanup and exact installer resources

* fix(setup): retain delegated cleanup across authority changes

* fix(setup): compose callback and source cleanup authorities

* fix(setup): retain producer cleanup failure reasons

* fix(cli): render structured setup cleanup resources

* fix(setup): separate failure evidence from cleanup ownership

* fix(setup): normalize close failures and unify lease binding

* fix(setup): preserve direct lease resource attribution

---------

Co-authored-by: gensokyo-dev <[email protected]>
2026-10-24 15:08:10 +08:00
morluto bdf15b9bd5 refactor(javascript): share the reconstruction input contract
Remove the reconstruction-only request schema and consume the canonical parsed
JavaScript application input. Production already owns parsing at the service
boundary; direct test seams now use that same schema instead of a weaker path
contract. Preserve format, filesystem, source selection and lifecycle validation.

Add a NUL-path regression for canonical admission and typed input-error
projection. Verified 63 focused cases plus static, build, docs, discovery and
duplication checks; no target execution or real-provider claims.
2026-10-24 15:02:26 +08:00
morluto d68d03e88f test(mcp): consolidate fixture connection and cleanup ownership
Route application and provider-environment harnesses through the shared MCP
connection owner. Join accepted handshakes during shutdown, retain each failed
close for retry, preserve primary and independent cleanup failures, and release
provider files only after their session closes.

Surface BinarySession close Result errors in production and artifact fixture
teardown. Add real SDK and descriptor-boundary regressions for startup races,
concurrent cleanup, mixed failures and recovery. Use a provider-free plist for
descriptor ownership so installed deep engines cannot change the test outcome.
2026-10-24 14:49:13 +08:00
gensokyo-dev d87e73f2db test(javascript): execute the registered Webpack export fixture (#6951) 2026-10-24 05:27:20 +00:00
gensokyo-dev b5c42dfc2a perf(entropy): separate physical reads from report resolution (#6950)
* perf(entropy): separate physical reads from report blocks

* test(entropy): type the injected positional short-read boundary

* test(entropy): retain full catalog transport capacity in task probe

* docs(perf): record real entropy task costs and limits

* docs(perf): record latest-main entropy qualification
2026-10-24 13:16:08 +08:00
Kaoru0822andClaude Opus 5.5 b869fa762a test: align stale fixtures with recent native and launcher contracts (#6945)
* test(native): expect artifact_changed when a bound metadata target is lost

41e10b240 made a bound metadata target that fails the source boundary
(`path`) or disappears (`ENOENT`) report AnalysisArtifactChangedError, as
its comment and docs/testing.md state, but left the module test expecting
AnalysisInputError. The test now asserts the shipped contract, so main's
Test shard and Portable tests lanes pass again.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* test(package): ship the CLI process environment owner in launcher fixtures

c2caae88f moved launcher environment preparation into
scripts/lib/cli-process-environment.mjs, made scripts/rea.mjs import it and
made verifyPackagePack require it in the packed listing, and updated the
dispatcher fixtures, but two other fixtures still omit it:

- packageTarListing's fake tar listing, so every case stops at "package
  omitted the CLI process environment owner" before its own assertion;
- deferredRuntime's copied compiled package, so the copied launcher fails
  to resolve its import instead of reporting the corrupted catalog.

Both fixtures now include the module, as the package ships it.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* test(native): expect XML plist validation in typed-value provenance

9f5dd56e2 records a successful `plutil -lint` of an XML plist source in
provenance, as docs/native-investigation.md states, but the macOS typed-value
CLI test still expected only the `file` step for both formats. XML sources now
expect the lint step; binary sources are unchanged.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-24 05:08:18 +00:00
morluto 21a20306b4 test(sqlite): preserve worker failures through cleanup retry 2026-10-24 12:34:10 +08:00
morluto f774dc420b fix(javascript): bind factory export roles to parameter values 2026-10-24 12:34:10 +08:00
e5965adb29 feat(cli): accept JavaScript source selections from a JSON array or file (#5029)
* feat(cli): accept JavaScript source selections from a JSON array or file

Add --source-paths-json to analyze and analyze-javascript-application.
It takes an inline JSON array or a JSON file path of exact inventoried
paths and appends them after any --source-path values, so a long
selection no longer has to fit on one command line (cmd.exe through npx
on Windows stops at 8,191 characters). Entries stay artifact-relative and
go through the existing source_paths validation; the native lane rejects
the option like --source-path.

Refs #4987

* fix(cli): cancel --source-paths-json file reads with the command

Pass the command's abort signal through parseCliJsonInput to
readJsonInputFile, so SIGINT/SIGTERM stop a selection-file read instead
of waiting for it to finish. A cancelled read now propagates its abort
rather than being reported as a file read failure.

* test(cli): preserve ASAR identity for JSON file selections

* docs(cli): record source-selection task costs and identity verification

---------

Co-authored-by: KJLavender <[email protected]>
Co-authored-by: Kaoru0822 <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: gensokyo-dev <[email protected]>
2026-10-24 12:32:41 +08:00
morluto ace47789a8 docs(setup): describe Codex registration preservation 2026-10-24 12:30:37 +08:00
morluto 60242ec82d test(cli): retain process outcomes across workflow assertions 2026-10-24 12:30:37 +08:00
morluto 1d896e03b7 fix(artifacts): retain original I/O failure evidence 2026-10-24 12:21:44 +08:00
morluto 915e56e3e5 fix(setup): preserve launcher identity during registration repair 2026-10-24 12:11:54 +08:00
morluto 850abe5e6f refactor(managed): remove obsolete throwing domain facades 2026-10-24 12:11:54 +08:00
morluto ddfd060210 test(artifacts): verify ZIP cancellation through extraction rollback 2026-10-24 12:07:33 +08:00
morluto aa9043f6b6 fix(native): preserve qualified Swift protocol identities 2026-10-24 12:07:33 +08:00
morluto 6843dbea9b fix(native): preserve bounded JVM identity before FAT admission 2026-10-24 12:07:33 +08:00
morluto 21097b192b fix(javascript): preserve catch destructuring alias ownership 2026-10-24 12:07:33 +08:00
gensokyo-dev f9fabdfcb2 test(cli): time Kuna cleanup from producer readiness (#6944) 2026-10-24 04:03:26 +00:00
gensokyo-dev ead9fdcda9 feat(erlang): query selected BEAM table facts (#6943)
* feat(erlang): query selected BEAM table facts

* docs(perf): record BEAM selected-view measurements
2026-10-24 12:00:18 +08:00
morluto c2caae88f8 refactor(cli): share process environment preparation
Give the launcher and shared command worker one lightweight owner for package-version binding, Windows empty-home handling and Android browser-cache defaults. Preserve per-command preparation and environment restoration, package the owner explicitly, and retain its package and JavaScript recovery CI scopes.

Validation: dispatcher/version/shared-session, home fallback, evaluator/MCP/process cleanup and CI scope tests passed. Functional archive/install/repack, installed static CLI analysis and MCP checks passed; no real Codex/model trial or Windows/Termux runtime qualification was performed.
2026-10-24 11:50:11 +08:00
morluto 3ad465a666 refactor(evaluation): move agent grading into development ownership
Keep the maintained manual evaluator, fixture rubrics and semantic tests together under tests/evaluation/model-evals instead of shipping them in dist. Load the same typed owners from the manual runner and update the architecture and client guide. Grading/report contracts and executable logic are unchanged.
2026-10-24 11:50:11 +08:00
4ef916da03 feat(pe): inspect import and delay-load tables (#6509)
* feat(pe): inspect import and delay-load tables

Add inspect_pe_imports / inspect-pe-imports to read PE32/PE32+ import
and delay-load directories without executing the image. The report keeps
DLL names, named and ordinal functions, ILT/IAT RVAs, and a pefile
import hash of the standard import table. A missing directory is
observed absence, not proof the image never imported APIs.

* fix(pe): default PE import fixture options

Allow peImportImage() without an argument so CLI and filesystem tests
can use the KERNEL32 sample without repeating an empty options object.

* test(pe): keep import path cases on the dedicated MCP contract

inspect_pe_imports already rejects relative MCP paths in its own
boundary test. Leave the shared absolute-path matrix to tools whose
handler issue projection matches that suite's expected shape.

* fix(native): index virtual-to-file mapping lookups

mappedFileOffset scans every mapping twice per span, so a parser that
translates one span per table entry costs sections x entries. Add
fileMappingLookup, which builds an O(n log n) interval index once and
answers each span in O(log n) with the same accept, reject and
translation results. A differential test checks it against
mappedFileOffset over randomized overlapping mappings and zero fills.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Ra7LM2NyQ7DumdssxbUq5g

* fix(pe): report partial import evidence and exact pefile hashes

- Withhold imphash with an explicit reason whenever the standard import
  table is partial (unreadable names or thunks, missing tables), or when
  pefile 2024.8.26 would hash a different row sequence: invalid or
  truncated names, ordinal 0, descriptors without functions, tables its
  bounds or repeat/spread heuristics discard, and its 8192-thunk import
  symbol cap. Delay-load issues do not withhold it.
- Hash ordinal imports with pefile's complete ordlookup tables and its
  ord<decimal> fallback instead of declining unmapped ordinals.
- Report a nonterminal descriptor whose FirstThunk is zero, and a
  delay-load descriptor whose IAT or INT RVA is zero, as malformed_entry
  partial coverage instead of a complete empty inventory.
- Directory evidence lists the file spans actually consumed for the
  descriptor array and terminator; discontiguous mappings produce
  separate spans and separate Evidence ranges.
- Carry exhausted limits as PeImportLimitFailure.limit and derive
  recovery from it; the fixed 16 MiB retained-text ceiling reports a
  fixed allowance instead of max_file_bytes advice.
- Translate RVAs through the indexed mapping lookup and yield with a
  cancellation check every 1024 table entries.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Ra7LM2NyQ7DumdssxbUq5g

* fix(pe): read only own ordinal tables for DLL names

DLL names such as constructor and __proto__ pass pefile's name rule but
resolved inherited object members, throwing before the ord<n> fallback.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Ra7LM2NyQ7DumdssxbUq5g

* fix(native): preserve PE import facts and prove hash compatibility

* test(native): verify PE import workflows and measured costs

* refactor(native): narrow PE facts across the container union

* docs(native): record integrated PE import validation

* fix(native): bound every PE import RVA span

* docs(native): measure RVA guard completed workflows

* fix(pe): reconcile import coverage with retained rows

* fix(pe): retain partial imports at descriptor mapping gaps

---------

Co-authored-by: Matt Van Horn <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: gensokyo-dev <[email protected]>
2026-10-24 11:44:12 +08:00
morluto f508a1a47e refactor(native): remove unreachable command routing branches 2026-10-24 11:41:34 +08:00
morluto f2eb27206f refactor(release): remove obsolete tag CLI and proposal output 2026-10-24 11:41:34 +08:00
Hasan Zayni 39aca92239 fix(windows): explain when Windows refuses to load the verified native addon (#6832)
When Smart App Control or an App Control policy refuses
rea-windows-x64.node, the loader reported the failure as the artifact
stage. Doctor and provider diagnostics then told the user to restore or
rebuild the native files, although their version and SHA-256 had
already matched the package manifest.

The loader now tags a failure of the module load itself, after the
manifest and digest checks pass, as the load stage. The reason keeps the
Windows error text and says that restoring the file cannot help, that
Ghidra and JADX need the native controls, that rea setup can still
register clients, and that REA must run where policy allows the file or
the maintainers can be asked about a signed build. Readiness keeps the
documented runtime_missing code for unloadable artifacts.

Setup does not depend on the native controls: with the addon
unavailable, the published 6.7.0 CLI still planned and applied the
claude_code registration, so no setup gate is changed.

Fixes #5723
2026-10-24 11:37:58 +08:00
morluto ae24b7879c refactor(native): consolidate command operation admission 2026-10-24 11:35:19 +08:00
morluto dd198d037f test(native): distinguish plist validation from conversion 2026-10-24 11:35:19 +08:00
morluto 8736fe3226 fix(release): avoid unused root commit file lookups 2026-10-24 11:35:19 +08:00
morluto 0f411508c3 fix(environment): decouple capture chronology from clock formats
Use exact recorded start facts for state extraction independently of derived clock formatting limits. Preserve millisecond interval policy, missing-time fallback, redacted-write cutoffs, and bounded timestamp retention; remove the superseded clock comparator.

Two source and two compiled CLI regressions reproduced post-write values being selected as initial state. State/clock/date, CLI/MCP view, and static checks passed after the fix.
2026-10-24 11:31:39 +08:00
morluto fbe77dc479 refactor(verification): separate readiness evidence scopes
Move the synthetic evaluator and its unchanged semantic tests out of the shipped runtime. Retain actual CLI and MCP Evidence separately from the synthetic nine-stage baseline in a versioned verifier artifact, execute its reported conformance checks, and assign the verifier to its CI lane.

Validation: evaluator/state/classifier source suites, compiled CLI/MCP views, verify:readiness, static checks, build and docs passed. Readiness observed coverage is static analysis; fixture runtime behavior and reconstruction closure were not executed.
2026-10-24 11:31:39 +08:00
morluto ba6cec31f5 fix(native): preserve plist admission through finalization 2026-10-24 11:27:27 +08:00
morluto 1564fc8d9b fix(managed): retain completed facts through close cancellation 2026-10-24 11:27:27 +08:00
morluto 41e10b240c fix(filesystem): distinguish admission and bound-target failures 2026-10-24 11:27:27 +08:00
gensokyo-dev d662fa54a6 fix(test): reserve heap headroom for shared CLI workers (#6941) 2026-10-24 03:23:25 +00:00
morluto 008931a178 refactor(hopper): move verifier assertions out of runtime 2026-10-24 11:19:45 +08:00
morluto cd10ee1f8a refactor(macho): centralize FAT declaration facts 2026-10-24 11:09:17 +08:00
morluto 80b7bbc8bd fix(environment): share exact recorded timestamp interpretation 2026-10-24 11:09:17 +08:00
morluto f3269feaad test(mobile): preserve encrypted bundle path facts across adapters 2026-10-24 11:02:45 +08:00
morluto cad14f3b35 test(javascript): cover primitive spread changes through CLI and MCP 2026-10-24 11:02:45 +08:00
morluto 7f70797b98 test(native): align plist fixtures with selected source validation 2026-10-24 10:57:37 +08:00
morluto 9f5dd56e2f fix(native): validate XML plist source admission 2026-10-24 10:56:25 +08:00
morluto 2bd981b017 refactor(tests): consolidate compiled CLI session ownership 2026-10-24 10:50:03 +08:00
morluto 6021efd371 refactor(browser): move bundle fixture composition out of runtime 2026-10-24 10:49:46 +08:00
morluto 7958e15892 fix(pcap): preserve header snapshot limit with inferred lengths 2026-10-24 10:35:50 +08:00
morluto e09b06369d refactor(cli): remove redundant alias coverage metadata
Remove the duplicate command-alias enumeration and convention-equivalent tool mappings. Keep explicit semantic exceptions, independent MCP-only policy and exact runtime alias-target coverage.
2026-10-24 10:30:05 +08:00
morluto bd1b2c74ae fix(browser): resolve companions from the selected manifest directory
Resolve the filesystem-selected manifest parent once before deriving script paths and reuse the shared containment rule. Preserve selected manifest spelling and capture metadata while retaining source integrity, symlink refusal and cleanup. Cover differing and identical lexical siblings through both artifact consumers and public CLI/MCP parity.
2026-10-24 10:30:05 +08:00
gensokyo-dev e2463372ea feat(python): query selected pyc code-object facts (#6928)
* feat(python): query retained pyc code-object facts

* test(python): bind complete pyc selection benchmark tasks

* test(python): include server instructions in token accounting

* docs(perf): record pyc selected-facts task measurements

* test(python): keep oracle dependencies separate from selected targets

* docs(perf): record guarded oracle requalification

* test(python): narrow complete Evidence through its owning contract
2026-10-24 10:23:06 +08:00
morluto 315b23403f perf(cli): prepare positional token schemas only for the selected command 2026-10-24 10:20:36 +08:00
morluto cc18e9ad0d fix(native): decode plist sources within result capacity
Derive ordinary plist depth from existing JSON result capacity while preserving archive limits. Decode supported XML and safe JSON directly from owned source bytes; retain conversion for OpenStep, unsafe numeric classification and unsupported encodings. Preserve typed scalar facts, truthful provenance, cleanup ownership and explicit capacity recovery.
2026-10-24 10:17:23 +08:00
morluto 2f2b07f750 fix(environment): count scenario bodies after request admission
Move body-retention coverage accounting to the manifest request-line owner. Preserve source retention facts by ordinal and remove the competing eligibility rule from scenario projection.
2026-10-24 10:17:23 +08:00
morluto 37f24125ad fix(javascript): resolve proven URL constructor aliases
Remove the redundant spelling gate after the shared intrinsic owner proves native constructor and source-base identity. Retain local-constructor and replaced-export uncertainty through public application analysis.
2026-10-24 10:17:23 +08:00
morluto 263a98c6ce fix(managed): preserve malformed signature uncertainty in comparisons
Consume the signature producer status before inferring operand-resolution limits. Preserve exact byte-match provenance while malformed paired signatures remain unknown through current and historical Evidence, CLI and MCP.
2026-10-24 10:17:23 +08:00
morluto 6298c84eea refactor(javascript): consolidate semantic graph boundaries
Retire the unused graph serialization facade and use one typed query implementation after canonical request admission. Migrate tests to the owning schemas while preserving graph commitments, Unicode transport, provenance, uncertainty, traversal and reconstruction controls.
2026-10-24 10:13:24 +08:00
Iven Kooandgensokyo-dev 9df94a62cd feat(javascript): link assignment right-hand sides to their writes (#6206)
* feat(javascript): link assignment right-hand sides to their writes

A declaration carries its value forward, but a later assignment to a
variable, a property or through a compound operator stopped the forward
trace at the right-hand-side read. The semantic IR now records one
syntactic assignment flow per simple assignment target (ranges, operator,
owning callable), and the graph projection connects each retained read
of the right-hand side to the write occurrence through a defines
relation. Compound assignments also consume the previous value of the
target; the written value itself stays unknown, keeping the boundary of
#4342 intact.

Fixes #6193

* fix(javascript): preserve computed key assignment ownership

* test(javascript): retain nested callback assignment ownership

* docs(javascript): record assignment influence verification

* test(mcp): isolate mocks and acknowledge cancellation boundaries

* test(javascript): retain CLI diagnostics and current-main verification

---------

Co-authored-by: gensokyo-dev <[email protected]>
2026-10-24 10:04:38 +08:00
morluto 2968d1e932 test(snapshot): author canonical native inventory rows 2026-10-24 09:58:40 +08:00
morluto fb9f35cf7b test(javascript): expose subsequent analysis recovery errors 2026-10-24 09:58:40 +08:00
morluto 584120e802 refactor(process): remove the redundant scenario parsing alias
Use the authoritative scenario schema directly in all 25 test consumers. Preserve unknown-input annotations and every lifecycle, permission, normalization, identity and cleanup assertion; retain the separate branded capture validation boundary.
2026-10-24 09:57:15 +08:00
morluto 254f9d9ea2 refactor(coverage): move fixture authoring out of the runtime
Retire test-only coverage and verifier constructors while preserving the live authenticated reader and evaluation path. Derive fixture inputs from the canonical contract schema and author commitments with an independent canonical JSON hash. Remove redundant success assertions while retaining typed failures and semantic controls.
2026-10-24 09:56:54 +08:00
morluto 044c9705ea refactor(validation): exercise authentication headroom at its boundary
Remove the unused estimator export. Check storage and escaping through typed authentication refusals with fixed allowances, preserving bounded traversal and acceptance controls.
2026-10-24 09:47:50 +08:00
morluto 3da7454ed8 refactor(manifests): consolidate commitment ownership
Remove test-only completion and historical manifest parsers, use authoritative completion schemas, and keep graph hashing with the validated manifest producer. Verify commitments with independent canonical JSON and SHA-256 checks through domain and public CLI workflows.
2026-10-24 09:47:50 +08:00
morluto f975f98ef4 refactor(verification): remove unintegrated conformance replay scaffolding
Remove the isolated package, replay and trust-gate modules with their synthetic-only tests. No application, CLI/MCP, verifier or package export consumes them. Keep active provider completion, managed conformance, fixture verification, readiness and reconstruction coverage paths.
2026-10-24 09:47:50 +08:00
morluto 85e38ac199 perf(apple): reuse scalar schema during plist projection 2026-10-24 09:37:20 +08:00
morluto 33f384120a fix(apple): preserve asset catalog rejection context
Retain the selected catalog path with captured assetutil failures and malformed metadata. Map two explicit selected-path invalid-catalog diagnostics to the existing format error while preserving other failure classifications. Verify the observed diagnostic through the real CLI and stdio MCP using a truncated temporary catalog and an unchanged valid control.
2026-10-24 09:36:15 +08:00
morluto 7eaa7920a7 fix(process): validate rendered omission causes in saved captures
Reject normalization omission counts that exceed unknown-size rendered frames at the shared capture parser. Preserve historical captures without the optional stage counter and expose the same constraint through CLI and MCP diagnostics.
2026-10-24 09:36:14 +08:00
morluto 9c8f404421 refactor(artifacts): retire the test-only materialization facade
Use the active in-scope materializer directly from the existing inventory fixture, with the same explicit resource-scope admission and cleanup owner. Keep staged snapshot mutation and extraction lifecycle regressions intact.
2026-10-24 09:28:35 +08:00
morluto 959ab0c10b refactor(firmware): remove unused hardware attribution scaffolding
Remove the unintegrated memory-map and peripheral-attribution modules with their dedicated tests. No application, CLI/MCP contract, verifier or package export consumes them; the shipped firmware analysis and publication workflows remain unchanged.
2026-10-24 09:28:28 +08:00
morluto 06822aed63 fix(native): require canonical dispatch inventory rows
Remove the undocumented name alias from list_names fallback and validate the canonical value rows with the existing strict policy. Keep record defaults in the public input contract and require explicit decoder budgets. Preserve provenance, partial coverage, source coordinates and typed failures in active application and MCP tests.
2026-10-24 09:28:17 +08:00
morluto 96ad94f69e fix(managed): attribute malformed comparison inputs to their arguments
Pass the caller-selected input field to the shared managed parse-failure
owner. Derive filename and field from one comparison selection, reporting
leftPath or rightPath while ordinary inspection continues to report path.
Preserve typed input classification and completed-left partial Evidence.

Replace weaker direct malformed-input cases with a compiled CLI workflow
covering both sides, the healthy comparison, and sibling inspection.
Document the input-attribution contract.

Validation: the public regression reproduced the generic path issue;
focused CLI, comparison and shared-reader controls passed after repair.
No managed assembly or external analysis provider was executed.
2026-10-24 09:17:10 +08:00
morluto 676d29f9ee fix(native): preserve qualified feature trace observations
Use one containing-procedure parser beside its declared schema for
navigation, address facets, and feature trace references. Keep the trace
cache and reference type bound to parsed facts, returning typed malformed
output before SDK projection instead of passing unchecked JSON onward.

Retain same-address search alternatives such as Kuna's distinct string
encodings. References identify target addresses, so observe and emit each
target once while retaining duplicate rows from the provider observation.
Remove address-only match collapse and redundant residual deduplication.
Document these semantics.

Validation: regression-first runs reproduced four malformed-resolution
failures and three alternative/reference-replay failures. Focused MCP and
consumer controls passed after repair. Real native engines were not run.
2026-10-24 09:17:10 +08:00
morluto 87745af52a fix(il2cpp): consume resolved ELF program declarations
Retain relevant program facts in the shared native directory and consume them for mappings and relocations. Remove redundant table coordinates and presence flags. This preserves extended program counts so PT_DYNAMIC beyond the raw sentinel remains visible, while load and relocation admission keep their distinct policies.
2026-10-24 09:14:21 +08:00
morluto b6519aa410 refactor(go): retain parsed build module facts
Parse module metadata at the image reader that validates its framing. Consume those facts in the provider while preserving raw bytes, text uncertainty, source locations, parser semantics and the public report contract.
2026-10-24 09:14:21 +08:00
morluto 8defd220a9 refactor(objc): retire obsolete class discovery paths
Remove the test-only selection/projection facade and its unused synthesized binding scan. Keep lexical and identity controls at their active owners, with combined behavior covered through EnhancedTools.
2026-10-24 09:14:21 +08:00
morluto 3ae7daa101 fix(test): match the optional Ajv diagnostics contract 2026-10-24 09:11:31 +08:00
morluto 3d3ee837c4 test(mcp): reuse immutable schema validators within analysis view suite 2026-10-24 09:11:31 +08:00
morluto 54f5c55f62 fix(verify): assert structured source-map omission coverage 2026-10-24 09:11:31 +08:00
morluto eea3271a33 test(cli): load only Frida contracts for focused CLI checks 2026-10-24 09:11:30 +08:00
morluto 31de177404 perf(javascript): preselect captured prefix replacements 2026-10-24 09:11:30 +08:00
morluto 45d0c3e299 fix(javascript): retain proven browser global receivers 2026-10-24 09:11:30 +08:00
morluto 673a659074 test: isolate module mocks without changing test groups 2026-10-24 09:11:30 +08:00
morluto 1d2b26e2d5 test(mcp): avoid exact prose in retained native view discovery 2026-10-24 09:11:30 +08:00
morluto 446333cb7f fix(javascript): deduplicate shared projected source facts 2026-10-24 09:11:30 +08:00
morluto d81d6b6707 fix(javascript): preserve semantic gaps for unparseable sources
Successful worker transport can carry an explicit failed parser status
with no semantic projection. Preserve that producer fact through the
shared source-availability interpretation used by full and incremental
graph construction, rather than dropping the source's semantic scope.

Retain the module when capacity permits, per-family incomplete-module
unknowns, and unknown omitted extents. Preserve existing operational
failure reasons and limits without classifying syntax as resource loss.
Document this coverage behavior.

Validation: regression-first run had 3 expected failures; all 22 selected
projection and public CLI source-selection/HTML cases passed after repair.
No JavaScript target execution or live external provider was involved.
2026-10-24 08:58:22 +08:00
morluto 54646e8dc4 fix(environment): preserve raw UTF-16 token boundaries
Pair a preceding low surrogate only with its adjacent high surrogate.
Unpaired code units must not make an earlier letter become the token's
neighbor. Keep UTF-16 offsets and source strings unchanged at the shared
delimiter owner used by rule discovery, copy checks, and replacement.

Cover valid astral letters, digits, emoji, and lone-surrogate controls,
plus create/read behavior through a CLI-started environment MCP server.
Validation: regression-first run had 4 expected failures; after the fix,
437 selected domain, application, and server-boundary tests passed.
2026-10-24 08:58:22 +08:00
morluto 8681752c7b fix(cli): share doctor argument ownership across dispatchers
Use the existing builtin argument split for the invalid-log-level doctor exemption, removing independent flag scanners from the launcher and reusable CLI test worker. Empty output values and doctor-looking command data now preserve configuration refusal precedence; shell completion stays transport data.

Check fresh and shared frontends with production argument parsing, retain both doctor readiness paths, and verify non-doctor requests stop before the real launcher imports command handlers.
2026-10-24 08:54:44 +08:00
morluto 9adc61243b refactor(evm): project admitted interface facts once
Validate engine output in the worker and transport output at reply admission. Have the typed function projector consume admitted producer facts in the provider, removing the fused parse/project facade and discarded worker projection.

Preserve duplicate order, missing inferences and raw metadata. Keep malformed producer checks at the schema and cover the provider handoff with source identity, diagnostics and cleanup assertions.
2026-10-24 08:54:44 +08:00
morluto 09c2a2d241 fix(browser): preserve main-frame identity and scope refusal context
Read screenshot identity from the selected frame-tree root instead of a
filtered inventory that could substitute a child. Reuse the frame record
parser for initial and event identities, rejecting malformed roots before
capture while preserving known origin refusals.

Share explicit origin-scope diagnostics across CDP workflows without
changing policy or conflating startup uncertainty, document replacement,
worker protection, and Inspector restrictions. Document the recovery and
make screenshot navigation fixture ordering explicit.

Validation: 577 focused checks passed across 13 files; one platform-specific
invalid-byte-filename case skipped. Build, typecheck, lint, formatting,
generated documentation checks, README conformance, and discovery passed.
No full suite or live browser/provider execution.
2026-10-24 08:48:19 +08:00
morluto f1dc595272 test(macho): frame load commands beyond the target probe 2026-10-24 08:40:20 +08:00
morluto 0ee3a35bd0 test(binary): compare layout evidence under the same resource budget 2026-10-24 08:40:20 +08:00
morluto a417209080 fix(browser): keep transport credentials out of timeout diagnostics 2026-10-24 08:40:20 +08:00
morluto 06bafdef14 fix(verify): unwrap canonical popup scenario secrets 2026-10-24 08:40:20 +08:00
morluto 3c0b26df67 refactor(gpu): retain parsed AMD target identities
Carry exact target keys and canonical processor facts from Clang bundle and ELF metadata producers into inventory agreement checks. Remove the string-only handoff and downstream reparsing while keeping public spelling, selection, uncertainty and admission behavior.

Cover unrecognized targets, conflicting notes and identity retained before malformed notes through the provider boundary. Preserve distinct unselected-payload behavior for standalone ELF and known bundle targets.
2026-10-24 08:37:15 +08:00
morluto 9d4293be27 fix(apple): preserve byte identity in metadata names
Use one lossless Mach-O name projection for symbols and dependency paths, retaining undecodable bytes and partial binding facts. Keep referenced NIB string failures unknown and preserve their source nodes through the shared lazy projection. Valid Unicode, readable sibling metadata and weak-bind scope remain usable.

Exercise the producer and CLI/MCP consumers, and align date-overlap and object-capacity fixtures with their owning archive contracts without relaxing admission or omission assertions.
2026-10-24 08:30:40 +08:00
morluto 1c07bee5b6 refactor(providers): retire obsolete decoding paths
Remove the test-only Objective-C property facade that discarded completeness. Verify its remaining semantics through portable Mach-O metadata decoding, including retained partial properties and located issues.

Decode the single IDA text result directly while preserving native JSON and structured-content precedence. Update the stale SDK deadline assertion to check the busy tool through its diagnostic owner.
2026-10-24 08:27:26 +08:00
morluto e8b2273ab6 style(tests): format merged DMG cancellation coverage 2026-10-24 08:25:01 +08:00
morluto b7c1d79da3 test(ghidra): distinguish descriptor failure origin from callback tagging 2026-10-24 08:23:24 +08:00
morluto c765b3b413 fix(browser): report launch timeout stage and startup diagnostics 2026-10-24 08:22:38 +08:00
morluto f2dfda4b36 test(contracts): consolidate concurrent limitation fixtures 2026-10-24 08:22:14 +08:00
morluto e6f252509f test(ghidra): retain endpoint failure ownership facts 2026-10-24 08:22:14 +08:00
morluto 6ed96f4372 test(javascript): separate option ownership and capture scenarios 2026-10-24 08:22:14 +08:00
morluto 2ff85c0e4c fix(javascript): separate stored receiver and initializer traversal 2026-10-24 08:22:14 +08:00
morluto 0ba81bd9e4 test(ghidra): isolate start failure from stop evidence 2026-10-24 08:22:14 +08:00
morluto 3e6ac23b50 perf(javascript): defer ancestry for member identifier roles 2026-10-24 08:22:14 +08:00
morluto aeeb4b72c1 test(javascript): preserve authenticated filesystem coverage facts 2026-10-24 08:22:14 +08:00
morluto 4394e8b592 fix(javascript): bound precise alias projection histories before expansion 2026-10-24 08:22:14 +08:00
morluto eb721ebbf1 fix(javascript): retain stored receiver capture authority 2026-10-24 08:22:14 +08:00
morluto d3a33fcfca test(browser): separate resource metadata and omission scenarios 2026-10-24 08:22:14 +08:00
morluto 5502a563d5 test(javascript): preserve module and renderer fixture authority 2026-10-24 08:22:14 +08:00
morluto be2a0b98ae test(apple): preserve serialized UID and cancellation identities 2026-10-24 08:22:14 +08:00
morluto fcd5192743 test(lifecycle): target owned finalization boundaries 2026-10-24 08:22:14 +08:00
morluto d442ac752e fix(javascript): retain completed recovery hashes on cancellation 2026-10-24 08:22:14 +08:00
morluto 5940600a22 fix(reference): preserve historical backslash import bases 2026-10-24 08:22:14 +08:00
morluto c2455152c9 fix(pcap): retain independent probe cleanup owners 2026-10-24 08:22:14 +08:00
morluto 4643844ea5 test(kuna): wait for probe readiness within command deadline 2026-10-24 08:22:14 +08:00
morluto 2496256cb7 fix(javascript): require complete receiver ownership proofs 2026-10-24 08:22:14 +08:00
morluto cc4f805541 fix(javascript): retain unresolved export iteration sources 2026-10-24 08:22:14 +08:00
morluto 27976a8fcb test(mcp): preserve typed input refusal and finalization contracts 2026-10-24 08:22:14 +08:00
morluto 67d02f4787 test(artifacts): bind workflow provenance and retained evidence sources 2026-10-24 08:22:14 +08:00
morluto 662e79330f test(apple): author admitted archives and typed binary UID references 2026-10-24 08:22:14 +08:00
morluto 04ba5caf63 fix(artifacts): preserve cancellation observed during directory sync 2026-10-24 08:22:14 +08:00
morluto c010fafe45 test(cli): align fixtures with current owning contracts 2026-10-24 08:22:14 +08:00
morluto 0de322eb91 fix(contracts): retain option descriptions and portable float patterns 2026-10-24 08:22:14 +08:00
morluto 4f6166e04d fix(process): retain CLI input owners through final cleanup 2026-10-24 08:22:14 +08:00
morluto d421e3eeef fix(cli): retain SIGPIPE status through dispatch cleanup 2026-10-24 08:22:14 +08:00
morluto 36f26a1bfe test(binary): compare bounded layouts against identical mutated bytes 2026-10-24 08:22:14 +08:00
morluto a4e4863029 test(apple): distinguish root discovery from dylib resolution coverage 2026-10-24 08:22:14 +08:00
morluto 64f261bc6f test(native): preserve admitted identity and partial boundary facts 2026-10-24 08:22:14 +08:00
morluto 48821e82a1 fix(native): distinguish Swift protocol identity from member context 2026-10-24 08:22:14 +08:00
morluto d1585fc600 fix(ci): constrain SQLite runtimes to supported security patches 2026-10-24 08:22:14 +08:00
morluto 4118004348 test(browser): distinguish partial HTTP samples from closed inventories 2026-10-24 08:22:14 +08:00
morluto 934f538f64 test(javascript): align public fixtures with proven source facts 2026-10-24 08:22:14 +08:00
morluto 002f9df43a fix(javascript): derive program strictness from parser context 2026-10-24 08:22:14 +08:00
morluto 7f3e32c69b fix(verify): compare matching browser source selections 2026-10-24 08:22:14 +08:00
morluto 4becc220b8 test(browser): preserve partial capture and ownership contracts 2026-10-24 08:22:14 +08:00
morluto 9d569c344a fix(cli): coerce web execution observation duration 2026-10-24 08:22:14 +08:00
morluto 800454c0ee test(setup): assert invalid DSH override inspection results 2026-10-24 08:22:14 +08:00
morluto 5c5f8e7cc4 fix(ci): reject omitted release proposal repository identity 2026-10-24 08:22:13 +08:00
morluto c6104d5095 test(lifecycle): isolate provider cleanup ownership fixtures 2026-10-24 08:22:13 +08:00
morluto 396dcb2b0f fix(domain): preserve structured cleanup failure reasons 2026-10-24 08:22:13 +08:00
morluto 4d22c83b07 test(setup): align boundary assertions with owned contracts 2026-10-24 08:22:13 +08:00
morluto de9d52cb5d fix(setup): preserve rejected registration setting details 2026-10-24 08:22:13 +08:00
morluto 1be8e9601b test(contracts): align catalog and admission fixtures with current contracts 2026-10-24 08:22:13 +08:00
morluto 9a165287ad fix(ghidra): centralize SIGKILL evidence projection 2026-10-24 08:22:13 +08:00
morluto 455f816d0b fix(ida): separate explicit secrets from HTTP credentials 2026-10-24 08:22:13 +08:00
morluto 46a5f55f74 test(providers): preserve cleanup and uncertainty contracts 2026-10-24 08:22:13 +08:00
morluto f42ade1dda fix(ghidra): admit observed procedure symbol metadata 2026-10-24 08:22:13 +08:00
morluto 7c88ef14af fix(artifacts): retain failed native DMG attachment provenance 2026-10-24 08:22:13 +08:00
morluto 11feab01da fix(environment): return input permission refusals through Result 2026-10-24 08:22:13 +08:00
morluto 6323013d83 fix(javascript): bound query unions and refresh semantic fixtures 2026-10-24 08:22:13 +08:00
morluto 07005b8fe6 fix(javascript): preserve local reference store ownership 2026-10-24 08:22:13 +08:00
morluto 59553c76be test(javascript): align semantic fixtures with runtime context 2026-10-24 08:22:13 +08:00
morluto c3ad8fc024 test(javascript): preserve fixture runtime context for path analysis 2026-10-24 08:22:13 +08:00
morluto 54a663ddd5 test(lifecycle): align cleanup fixtures with owned resource contracts 2026-10-24 08:22:13 +08:00
morluto e389d6ea4e fix(ci): filter finalized release proposals before pagination 2026-10-24 08:22:13 +08:00
morluto 4d88e4f07e test(setup): isolate multi-client recovery from skill admission 2026-10-24 08:22:13 +08:00
1a48a82020 feat(analysis)!: default function MCP output to retained summaries (#5660)
* feat(ghidra): bound one procedure's decompile without closing the session

Fixes #5340

* fix(ghidra): report decompile limit as a request timeout

Keeps BinarySession from marking every Ghidra operation unavailable after one procedure's decompile reaches the limit, and names the MCP tool-call deadline in the remediation.

* fix(ghidra): budget the decompile limit and report cancellation-closed sessions

Adds REA_GHIDRA_DECOMPILE_TIMEOUT_SECONDS to the derived Codex/Kimi tool deadline and forwards it through setup. Marks a remote failure that settles after caller cancellation so the timeout projection no longer promises session reuse.

* feat(ghidra)!: default analyze_function to a retained summary

analyze_function was the one analysis tool never wired into REA's
detail: summary|complete convention, so it always returned the complete
native dossier inline (0.2-10 MB, dominated by value-flow and
references). Agents on tool-result-capped harnesses never reached the
pseudocode.

Add `detail` to analyze_function, defaulting to summary: return a
compact dossier (procedure identity, pseudocode size, facet counts,
value-flow availability) and retain the complete dossier Evidence in
the session. Page pseudocode, references, assembly and value-flow
through normalized_result.parent_evidence_id with inspect_analysis_view,
which already projects every native facet. detail: complete returns the
full dossier inline as before. Delivery detail never reaches the
provider or Evidence identity.

BREAKING CHANGE: analyze_function now defaults to the compact summary
view instead of the complete dossier inline. Pass detail: "complete"
for the previous behavior.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Claude-Session: https://claude.ai/code/session_01AoJZDPcrgmMsCqLG4LfiFb

* feat(cli): expose analyze_function detail on the function command

Wire `rea function` into the shared CLI detail-delivery convention
(--detail, --evidence_output) so the CLI and MCP adapters offer the same
summary | complete choice for analyze_function. Summary delivery writes
the complete dossier Evidence to a bundle and returns the compact
summary view; it fails before analysis when --evidence_output is
missing, so no provider work is wasted. The CLI default stays complete
because a one-shot command has no in-process Evidence retention.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Claude-Session: https://claude.ai/code/session_01AoJZDPcrgmMsCqLG4LfiFb

* refactor(ghidra): harden analyze_function summary after independent review

FlatlineRoundtable cross-model review (6 lanes) converged on the summary
projection's availability semantics being underspecified. Document that
facet_availability is authoritative over raw pseudocode length, that
code_units are UTF-16 (the unit inspect_analysis_view pages by), and that
value_flow.truncated is meaningful only when value-flow is available.
Parenthesize the nullish-coalescing/comparison for clarity. No behavior
change; declined claims (value-flow "truncated collapse", unresolved_calls
dead code) were schema-impossible or already correct.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Claude-Session: https://claude.ai/code/session_01AoJZDPcrgmMsCqLG4LfiFb

* test(ghidra): cover analyze_function detail wiring on both adapters

Close the roundtable's top convergent finding: the summary path had only
domain-projection coverage, not the server/CLI wiring where the breaking
behavior lives.

MCP server (registerEnhancedTools): the default delivers a retained
summary whose complete parent is recorded for paging; delivery detail is
stripped before the provider request; detail: complete returns the full
dossier inline; a registration without retention rejects a summary
request before any provider work.

CLI (rea function): the default returns the complete dossier; --detail
summary without --evidence_output fails before analysis; with the bundle
sink it returns the compact summary and writes the complete Evidence
bundle.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Claude-Session: https://claude.ai/code/session_01AoJZDPcrgmMsCqLG4LfiFb

* fix(ghidra): keep analyze_function delivery detail out of provider inputs

Address the five automated review findings on #5660.

P1: `detail` lived in the shared enhancedInputSchemas.analyze_function, so
inspect_native_api composed `{procedure, detail: "summary"}` and the strict
Ghidra analyze_function schema rejected it. Delivery detail is now owned by
the analyze_function MCP tool contract; the handler separates it before any
provider call, and executeFunctionAnalysisEvidence parses the provider input
only. The MCP regression test validates provider input exactly as the
production Ghidra client does and exercises inspect_native_api.

P2: the analyze_function summary Evidence now carries the parent's
relationship_coverage like native facet views, so partial or unknown
caller/callee discovery is not reported as complete.

P2: the summary reports the dossier's declared facet_availability, nulls the
counts of facets the provider declared unavailable, and lists each reason in
unknowns, so Kuna's empty references, comments and basic blocks are not
mistaken for observed absence.

P2: inspect-analysis-view evidence-file sources accept the Evidence bundle
that --evidence_output writes, selecting a record with source.evidence_id
when the bundle holds several; the CLI test reads the saved bundle back
through that route.

Tests that assert the complete dossier over public analyze_function now pass
detail complete, and the oversized-dossier test asserts the summary view it
previously expected to be unsupported.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01AoJZDPcrgmMsCqLG4LfiFb

* fix(ghidra): keep omitted function facets unknown in the summary

The analyze_function summary coerced an omitted unresolved_calls record to
a count of 0 while the native facet view reports the same dossier as "not
recorded", and it carried no counts at all for the pageable
swift_string_literals facets. Count optional records as null when the
dossier omits them, name them in unknowns, and add nullable counts for both
Swift literal facets. The summary Evidence envelope also dropped the
function address: createAnalysisViewEvidence now derives one function-view
predicate for both the address location and relationship coverage.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01AoJZDPcrgmMsCqLG4LfiFb

* fix(scripts): request complete analyze_function dossiers in real-provider checks

The real-provider check scripts call analyze_function over MCP and then
consume the complete dossier: requireFunctionDossier, functionDossierSchema,
.procedure/.pseudocode reads, operation assertions, compare_functions inputs
and the oversized-dossier rejection path. The summary default broke them.
Every such call site now passes detail: "complete". Provider-direct callers
are unchanged because detail is an MCP contract field; the admission-error
sites stay unchanged because provider failures return before the summary
branch.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01AoJZDPcrgmMsCqLG4LfiFb

* fix(ghidra): keep unavailable value-flow counts unknown in the summary

summarizeDossier coerced the five value_flow_* counts to zero when
native_value_flow was null (IDA, Kuna) or declared unavailable (Ghidra),
so the summary reported empty collections while value_flow.available said
otherwise. The counts are now null unless value flow is available, with the
reason already named in unknowns.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01AoJZDPcrgmMsCqLG4LfiFb

* fix(evidence): budget authentication headroom by file shape and serialized size

The evidence-file headroom reserved two parsed copies, calibrated for one
record, and charged strings at two bytes per character. Bundle
authentication additionally holds the schema-parsed bundle, the re-parsed
records and two JSON.stringify results, which expand control characters and
lone surrogates sixfold: a bundle whose raw_result is a million NUL
characters estimated at 2 MB parsed but needed 12 MB of serialized text.

The budget now has a parsed term and a serialized term per file shape, owned
by EvidenceAuthenticationHeadroom and shared by the analysis-view reader and
the bundle importer, which had no guard. Two transient peaks the budget did
not name are removed at their producers: the canonical digest hashes a large
leaf directly instead of concatenating it into its buffer, and the JSON
validator walks containers by cursor so a wide array no longer queues one
entry per member.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01AoJZDPcrgmMsCqLG4LfiFb

* fix(mcp): route analyze_function summary replies to dossier consumers in docs and prompts

The MCP guide, the version-comparison and crash-triage prompt contracts,
the authored skill and the Kuna, IDA and function-matching docs still told
agents to pass analyze_function replies straight to compare_functions or
build_call_path. Under the summary default those consumers reject the reply.
They now name the retained-evidence reference for compare_functions, the
observations fields of match_native_functions, and detail: "complete" for
build_call_path, which accepts only inline dossiers.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01AoJZDPcrgmMsCqLG4LfiFb

* test(evidence): pin validator member order and keep interrupted validation yielding

Independent review of the cursor validator found the interrupted-validation
fixture relied on the old last-first walk: its over-deep head was reached
before the first cooperative yield. The padding now precedes the deep
element so the walk yields first, which is the invariant the test guards.
A new case pins first-defect-in-member-order reporting, skipped
non-enumerable keys no longer count toward the yield cadence, and the
import-headroom test loads the reader through a fresh module graph so it
holds under the services project's shared workers.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01AoJZDPcrgmMsCqLG4LfiFb

* fix(ghidra): align deadline bounds and cancellation outcomes

* refactor(ghidra): shorten deadline recovery guidance

* fix(mcp): clarify the scope of inferred decompile deadlines

* test(ghidra): verify deadline workflows and document measured costs

Separate failure and registration cases by their distinct contracts.
Record actual Linux Ghidra, CLI and MCP evidence with measured context,
elapsed time, CPU and memory, and explicit remaining coverage limits.

* docs(ghidra): pin integration and distinguish baseline format failures

* fix(ghidra): own failure lifecycle wording in the provider

* test(ghidra): budget timeout fixture generation before allocation

* fix(analysis): preserve summary provenance and bound bundle guidance

* fix(ghidra): align capability and traversal deadline guidance

* style(config): align deadline guidance formatting

* docs(ghidra): record capability and traversal verification

* docs(analysis): use absolute paths for Evidence files

* docs(analysis): record completed function summary measurements

* docs(analysis): update main integration validation

---------

Co-authored-by: Syed Rishad <[email protected]>
Co-authored-by: Claude Opus 4.8 <[email protected]>
Co-authored-by: gensokyo-dev <[email protected]>
2026-10-24 08:18:53 +08:00
morluto bf6e49a8f4 refactor(native): retain one plist JSON parse owner
Keep the JSON parse attempt and exact numeric source literals together through optional XML classification. Replace the probing parse and raw-string parser with readPlistJson, preserving duplicate-key behavior, signed zero, ambiguous number types, prototype omissions and failure precedence.

Project fresh results from private facts and preserve native command controls, captures and provenance. Verify parser reuse and real provider-boundary conversion choices, plus compiled CLI and MCP behavior with real plutil.
2026-10-24 08:13:10 +08:00
morluto 76500f12b5 fix(native): preserve selected paths and binding identities
Reuse filesystem-aware path selection for resource-only plist sessions so directory symlinks and trailing separators retain their meaning. Parse dyld bind names once as lossless text or exact bytes, preserving partial binding facts and independent valid metadata without invented class identities. Keep coverage issues separate from known opcode applicability and exercise both producer and generated Mach-O consumers.
2026-10-24 08:01:25 +08:00
morluto 64fa1db130 fix(managed): honor native platform symbol lookup
Restrict Windows A/W suffix and stdcall candidates to their applicable native format. Preserve original metadata and per-observation Evidence ownership. Exercise identity contradictions and wrong-role controls through the shared CLI and stdio MCP workflows.

Fixes #6926
2026-10-24 08:01:25 +08:00
morluto 79805f244a fix(web): preserve JSON identity and coverage contracts
Decode base64 JSON body bytes at the shape owner without replacement characters. Keep incomplete request correspondence unknown and use explicitly complete authored inventories for positive comparison controls. Repair semantic truncation and storage fixtures to satisfy their owning contracts without weakening assertions.
2026-10-24 08:01:25 +08:00
morluto f90d40720f refactor: retire redundant parsing and workflow facades
Keep Mach-O UUID interpretation at reconciliation, environment values at the details owner, and inventory scope admission at the public operation. Remove the obsolete UUID scalar, duplicate DSH dispatch, two intermediate inventory exports, and unused body-cutoff predicate.

Keep test resource scopes explicit and cover oversized bodies through the real comparison workflow. Correct stale DSH invalid-result assertions and model DMG cancellation with the command owner, retaining an opaque AbortError control and mount cleanup checks.
2026-10-24 08:00:55 +08:00
morluto 6b3eee1ccf test(setup): assert registration facts through their owner
Verify fresh provider budget resolution without coupling inspection to probe counts, while preserving caller-selected positive timeouts and file bytes. Compare effective peer registrations before and after updates instead of rebuilding legacy OpenCode entries in a different representation.
2026-10-24 07:49:34 +08:00
morluto f11892265d fix(ghidra): preserve unknown process stop facts
Project the supervisor's compact stop flag to an explicit boolean only when an owned snapshot is available. Require confirmed false before treating SIGKILL as external or attaching cgroup kill evidence, so missing diagnostics remain unknown.

Cover known false, true and missing facts while preserving operation-scoped OOME precedence and real owner cleanup behavior.
2026-10-24 07:49:27 +08:00
morluto f65a98f9ad fix(web): track aggregate cleanup through live owners
Bind multi-owner shutdown errors to their live typed cleanup dependencies so retained aggregates narrow and clear as each resource is released. Keep unknown or already-resolved rejections conservatively incomplete.

Cover both release orders, retained MCP Evidence, late cancellation and retry through the SDK session shutdown boundary.
2026-10-24 07:43:49 +08:00
morluto 4610843efd fix(filesystem): preserve failure provenance across cleanup owners
Carry descriptor outcomes, cancellation, retention and physical origin as named facts so wrappers cannot drop ownership context. Replace raw primary failure unwrapping with provenance facts across artifact consumers, and use Result for Kuna span reads including undefined rejections.

Exercise actual descriptors, rollback, cancellation and cleanup retry; update brittle snapshot and completion fixtures to use their producing boundaries.
2026-10-24 07:43:49 +08:00
Syed Rishadandgensokyo-dev a062529bb2 feat(ghidra): bound one procedure's decompile without closing the session (#5662)
* feat(ghidra): bound one procedure's decompile without closing the session

Fixes #5340

* fix(ghidra): report decompile limit as a request timeout

Keeps BinarySession from marking every Ghidra operation unavailable after one procedure's decompile reaches the limit, and names the MCP tool-call deadline in the remediation.

* fix(ghidra): budget the decompile limit and report cancellation-closed sessions

Adds REA_GHIDRA_DECOMPILE_TIMEOUT_SECONDS to the derived Codex/Kimi tool deadline and forwards it through setup. Marks a remote failure that settles after caller cancellation so the timeout projection no longer promises session reuse.

* fix(ghidra): align deadline bounds and cancellation outcomes

* refactor(ghidra): shorten deadline recovery guidance

* fix(mcp): clarify the scope of inferred decompile deadlines

* test(ghidra): verify deadline workflows and document measured costs

Separate failure and registration cases by their distinct contracts.
Record actual Linux Ghidra, CLI and MCP evidence with measured context,
elapsed time, CPU and memory, and explicit remaining coverage limits.

* docs(ghidra): pin integration and distinguish baseline format failures

* fix(ghidra): own failure lifecycle wording in the provider

* test(ghidra): budget timeout fixture generation before allocation

* fix(ghidra): align capability and traversal deadline guidance

* style(config): align deadline guidance formatting

* docs(ghidra): record capability and traversal verification

---------

Co-authored-by: gensokyo-dev <[email protected]>
2026-10-24 07:42:50 +08:00
morluto fc2dddc1d9 fix(managed): retain facts without inventing delta identities
Recognize baseline-dependent #JTD metadata at the shared layout owner before table sizing and decoding. Report unsupported partial metadata across all inspection workflows, preserve admitted PE/CLI and metadata header facts, and keep member identities and inventories unknown without the baseline.

A generated PE carrier reproduced complete coverage and a wrong standalone MethodDef token before the fix. Producer and actual CLI/MCP checks now cover all three inspections, normal metadata, retained comparison after source deletion, exact caller selection, source digests, unchanged bytes, and known header facts. Released .NET writer/reader sources establish framing applicability; natural PE occurrence and real CLR/Edit-and-Continue execution were not verified.
2026-10-24 07:31:55 +08:00
morluto 36f528efcb fix(javascript): share immediately returned loader facts
Reuse the existing direct-call returned-reference owner for immediate synchronous function results and feed proven loader facts into historical imports. Simple returned require retains its identity; mixed, opaque, and canonical shim alternatives retain explicit uncertainty rather than disappearing.

Focused semantic, Electron, and public CLI regressions passed, including CommonJS, ESM, async/generator, mutable argument, direct-loader, and source-provenance controls. No runtime target or Proxy evaluator was introduced.

Fixes #3535
2026-10-24 07:28:09 +08:00
morluto 9d7779d365 fix(cli): validate selected local paths before resolution
Reuse the nonempty NUL-free local path contract before normalization in artifact commands, auxiliary inputs, and output destinations. Empty selections now fail at parsing instead of selecting the working directory; explicit relative paths preserve their target.

Public CLI checks cover primary and auxiliary empty paths, exact relative-file digest, explicit current-directory selection, symlink parent semantics, and environment export inputs. Correct the desktop fixture to assert the existing CLI validation envelope.
2026-10-24 07:28:09 +08:00
morluto 231e458246 fix(native): preserve parsed section and architecture facts
Classify lipo parse failures once for native and artifact consumers, retain valid read-only ELF ranges independently of optional section names, and map IL2CPP reader architecture through the existing CPU identity owner before Mach-O parsing.

Regression-first checks reproduced malformed lipo output, lost unnamed-section compiler paths, and the amd64/x86_64 adapter mismatch. Focused parser, artifact, Rust CLI/MCP, Go, and IL2CPP controls passed; authored fixtures do not establish real-provider behavior.

Fixes #5982

Fixes #3397
2026-10-24 07:28:09 +08:00
morluto 3d4f55652b refactor(mcp): consolidate contract and summary policy owners
Resolve application and managed workflow contracts through the canonical name-based resolver, removing catalog-position overloads and the obsolete local facade.

Use the existing retained-summary policy for eight offline registrations. Pass only validated delivery detail to that owner while preserving exact typed service inputs and validation, admission, logging, and Evidence recording order.

Align export-shape MCP parity coverage with the existing absolute-path contract using an absolute dot alias. Preserve exact caller-path parameters, canonical artifact parity, distinct Evidence identity, and the export comparison assertions.
2026-10-24 07:09:54 +08:00
morluto 0785223582 fix(managed): share nominal scope across member comparisons
Resolve declaring TypeDef tokens within their own source and reuse the retained nominal identities for pairing, absence, and aggregate coverage. Remove display-name membership interpretation while preserving partial known pairs and whole-inventory matching.

A generated managed fixture reproduced false additions and removals for distinct nested owners sharing a display name before this change. All 305 focused producer and public CLI-to-stdio-MCP checks passed, including comparisons after source removal, missing identities, and genuine whole-inventory changes. Clarify selected-scope semantics in the docs; no live managed runtime was used.
2026-10-24 07:07:06 +08:00
morluto 794932e033 test(setup): isolate registration failure recovery intent
Select registration without skill installation in the synthetic-client recovery matrix. Preserve all four specific remediation assertions and verify the plan, failure reason, and absence of unrelated installation work.
2026-10-24 07:07:06 +08:00
morluto 13fcf58276 fix(session): own selected snapshot read cleanup
Route MCP snapshot reads through the session's existing file-resource owner so cleanup remains visible, blocks replacement, and can be retried by close_binary. Keep reads inside the serialized open callback and remove superseded adapter resource plumbing.

Validate typed errors, exact descriptor recovery, unchanged source bytes, replay provenance, and same-connection lifecycle ordering. Focused snapshot, target cleanup, and MCP lifecycle checks passed with simulated providers; no live provider was exercised.

Fixes #6216
2026-10-24 07:07:06 +08:00
morluto edc805eca2 refactor(process): own selected artifact read failures
Classify selected-input read failures at the shared artifact boundary and remove duplicate phase-based translations from TShark and sigrok. Keep other read stages raw for their existing owners and retain cleanup resources until retry succeeds.

Extend the descriptor outcome matrix with post-run private-copy integrity failures, late cancellation, retained command output, failed cleanup, and successful retry.
2026-10-24 06:53:53 +08:00
morluto 0134e15ab1 refactor(process): centralize captured command failure facts
Project admitted output facts beside the command owner and remove adapter-local copies. Preserve byte-count omission, explicit signal and exit facts, and caller-specific truncation and failure policies.

Update ADB timeout regressions to assert the existing prerequisite frame and original process cause, including captured output on both frames.
2026-10-24 06:53:53 +08:00
morluto 091d6209af fix(managed): compare retained declaring-type selection scope
Derive aggregate selected-scope agreement from retained nominal type identities instead of requested spellings. Keep incomplete or historical selected scope unknown while preserving caller selections, whole-inventory distinction, paging and member absence behavior.

Cover equivalent requests, same-label nested owners, missing and partial identity facts, and complete empty selections. Exercise CLI inspection Evidence through stdio MCP comparison after removing its selected source file.
2026-10-24 06:48:16 +08:00
morluto 60bb202ca3 test(providers): assert runtime and snapshot ownership contracts
Keep the selected running JVM visible when its version is unsupported, while retaining missing-runtime fallback controls. Assert the native snapshot binding and composed workflow Evidence profiles separately, preserving their exact commitments through a rejected snapshot switch.
2026-10-24 06:48:16 +08:00
f8ff1579ad test(reference): preserve optional resolver identity regressions (#6913)
fix(reference): retain optional CommonJS resolver dependencies

Co-authored-by: Plastic-time <[email protected]>
Co-authored-by: gensokyo-dev <[email protected]>
2026-10-24 06:46:22 +08:00
morluto 1a808dd2a2 test(perf): reuse keyed archive CLI and MCP sessions 2026-10-24 06:43:44 +08:00
morluto 91c28e2735 fix(apple): preserve XML plist UID reference identity 2026-10-24 06:43:44 +08:00
morluto 2886cc1efe test(process): compare progress with retained PTY chunks 2026-10-24 06:43:44 +08:00
morluto dbf8e1eacc test(browser): cover SAZ capture-level null member limitation 2026-10-24 06:43:44 +08:00
morluto 3472cc4570 test: require retained authority and successful empty-child updates 2026-10-24 06:43:44 +08:00
morluto d7efb40997 test(ghidra): decouple stopped signal regression from recovery prose 2026-10-24 06:43:44 +08:00
xjh 1081008c9d fix(python): preserve source boundaries in pyc previews (#6492)
Preserve source code-point prefix endpoints in the shared marshal UTF-8 decoder, including separately encoded surrogate values. Keep native TextDecoder spans, full validation, byte identity and the existing 80-unit budget. Update the PyInstaller consumer without changing its text contract.

Validation: 207 relevant source tests;24 affected compiled CLI/MCP/filesystem CI tests; independent CPython3.11.2 compiler/marshal oracle and real argparse;18 equivalent performance tasks; build/check passed. Codex reviewed7c0f7abc6b without major findings.

CI bypass authorized for demonstrated baseline-only failures:101 source and179 built cases reproduced on actual tested main ba9c5bc68. Cancelled shard remainder is unverified. See PR body and discussion for scope and evidence.

Fixes #6476
2026-10-24 06:42:13 +08:00
morluto 667ab5ae08 fix(adb): own numeric declaration validation and diagnostics
Parse declarations before selecting values, admit safe integers once, and retain bounded raw numeric issues. Preserve display and package selection policies while distinguishing absent and malformed values.

Keep completed display facts in typed physical-size failures, share the existing diagnostic sample limit, and verify real CLI/MCP contracts with a strict fake ADB.
2026-10-24 06:34:33 +08:00
morluto 0b1af48a37 fix(protobuf): preserve MIME admission and transport facts
Parse MIME parameters once with strict quoted-value and duplicate handling,
and compare equivalent default binary profiles. Keep excluded HAR MIME
unknown, explicit framing independent, and nominal gRPC sanitization intact
across malformed or repeated declarations.

Add public CLI/MCP and native-header regressions with source identity and
partial-coverage assertions. Preserve upstream wire and schema admission.

Fixes #6836
2026-10-24 06:24:55 +08:00
morluto b6e107aded fix(process): quiesce terminal producers before final observation
Stop interactions and sampling before final observation, and stop the owned
target before failure-path rendering. Cache finalized terminal facts through
disposal and cleanup retry while preserving the original failure.

Version deferred terminal observation order for every normalization profile,
including disabled PID and port normalization. Add public CLI/MCP descendant,
partial-evidence and compatibility controls without replacing upstream owners.

Fixes #6443
2026-10-24 06:24:55 +08:00
morluto d5df7fd944 fix(reference): preserve source scope and loader authority
Share parser-owned package and runtime facts across dependency consumers.
Preserve uncertain loader requests and explicit wrapper bindings without
inventing unbound require authority in known modules. Cover selected source
grammar through the public MCP boundary and retain ambient/type dependencies.

Open each selected directory before loading its scoped policy, retaining
partial directory failures and cleanup ownership. Preserve explicit caller
negation precedence and source diagnostics across inventory projections.

Clarify how version comparison retains container provenance without claiming
semantic drift. Use independent unknown source inputs to keep candidate-limit
fixtures meaningful as conditional branch evaluation becomes more precise.

Fixes #6891
Fixes #6882
Fixes #6883
Fixes #6859
2026-10-24 06:24:55 +08:00
morluto 52aeeee764 fix(ida): preserve native workspace admission and startup outcomes
Validate the selected workspace lookup through stat before canonicalization,
preserving typed configuration causes and physical traversal semantics. Cover
readiness without writes and tie startup cleanup regressions to explicit
cancellation or timeout producer outcomes.

Fixes #6828
2026-10-24 06:23:23 +08:00
morluto 30db499db3 fix(javascript): preserve graph facts across omissions and versions
Omit absent inventory facts without erasing explicit unknowns. Compare canonical file occurrences by source path and digest while preserving complete container provenance in observations and Evidence.

Consolidate candidate-capacity fixtures around unresolved alternatives and update inventory coverage and source-occurrence oracles. Preserve resource, identity, relationship uncertainty, cancellation, and transfer regressions.
2026-10-24 06:20:36 +08:00
9dde3fd6a4 fix(cli): advertise required CLI selector choices (#6759)
* fix(artifacts): retain caller selection and applicable publication recovery

* fix(artifacts): retain DMG producer failures and bound integrity recovery claims

* fix(apk): classify malformed supported Zip64 records as input failures

* fix(browser): compare script and tool facts without metadata identity churn

* fix(environment): retain path segments counters and parent diagnostics

* fix(discovery): retain Frida optional provider failure dispatch

* test(native): share Swift context in vtable fixture admission

* fix(native): narrow capability refusal before retaining its reason

* fix(javascript): retain private receiver and iterative disposal effects

* fix(native): preserve negative Mach-O admission identity

* fix(javascript): bind Next page admission to source context

* fix: preserve process filesystem scope and admission failures

* fix(evidence): reject runtime locations for managed static facts

* fix(hopper): bind ordinary executable opens to selected bytes

* fix(browser): admit legacy capture uncertainty at retained input boundaries

* fix(unknowns): distinguish rejected creation from committed mutation delivery

* fix(evidence): preserve authentication failures and diagnose prior commitments

* fix(cli): preserve explicit Evidence record pairing in bundle comparison

* fix(artifacts): publish addressable inspection sources through both adapters

* docs(evidence): define authentication and committed delivery recovery

* fix(javascript): own reference roles and invocation result identities

* fix(hopper): pass private X authority cookie through stdin

* fix(ghidra): preserve catalog and metadata admission failures

* fix(jeb): retain selected inputs and completed primary replies

* fix(native): preserve literal environment map members

* test: retain direct hash cleanup retainer contract

* test: distinguish corrected port normalization commitment

* fix: recheck attached worker policy after final artifacts

* fix(javascript): share iteration and equality invocation facts

* fix(artifacts): preserve typed resource limit diagnoses

* fix(javascript): preserve semantic query identity and source coverage

* docs(readme): align translated setup and analysis facts

* fix(cli): reject surplus positional arguments before execution

* fix(kuna): retain setting-specific doctor recovery

* fix(setup): preserve semantic configuration refusal during uninstall

* fix(setup): retain publication operation failures and recovery facts

* fix(setup): distinguish Hopper inspection and archive staging failures

* docs(managed): distinguish oracle conformance from host verification

* docs(skill): keep one setup prerequisite policy

* fix(firmware): preserve admission and extraction coverage outcomes

* fix(contracts): preserve safe defaults and client literal guidance

* fix(javascript): retain worker storage and projection failures

* fix(evidence): preserve valid output paths near filesystem pathname limits

* fix(cli): accept named native API selectors beginning with a dash

* fix(artifacts): retain DMG carrier observations when child traversal is unavailable

* fix(artifacts): retain dylib enumeration readers through failed cleanup

* fix(browser): resolve empty storage-world origins from exact frame evidence

* fix(browser): retain unknown popup storage initialization coverage

* test(reconstruction): isolate derived surface ownership in fixtures

* test(filesystem): probe actual permission denial before host assertions

* test(managed): assert large issue retention without full-array diffs

* fix(javascript): preserve source facts across graph projections

* fix(javascript): preserve copied values and export publication lifetimes

* fix(javascript): preserve per-field publication and provenance coverage

* fix(javascript): retain initialized class self binding at body reads

* fix(artifacts): retain permission omissions through JavaScript lookups

* test(graph): narrow summary owner and complete PE fixture facts

* test(javascript): supply source graph to native handoff checks

* fix(sourcemaps): bind cleanup status to remaining codec owners

* fix(browser): retain excluded source-map declaration coverage

* test(javascript): preserve exact empty property keys in declarations

* fix(browser): keep code absence unknown without worker inventory

* fix(browser): preserve WebMCP optional fields and replay coverage gaps

* fix(browser): publish executable examples with truthful capture coverage

* fix(browser): preserve observed document invalidation and scope recovery

* fix(native): share exact-name xref ambiguity resolution

* fix(hopper): own bookmark API capability checks

* fix(native): preserve procedure selector coordinate intent

* test(discovery): select the declared GDB console contract

* fix(ghidra): bind session refusal to retained cleanup owner

* fix(apple): retain selected dylib identity after path removal

* fix(apple): preserve asset failures and authored resource coverage

* fix(apple): retain binary UID identity through archive projection

* fix: retain completed historical replies through finalization

* fix: retain unresolved browser secret declarations as input errors

* test(browser): resolve network fixture secrets through typed input contract

* fix(environment): distinguish destination refusal and write access failures

* fix(artifacts): preserve exact ZIP and DMG caller cancellation reasons

* fix(artifacts): require regular source-map target occurrences

* fix(reference): separate local filesystem diagnostics from graph commitments

* fix(setup): retain proven PATH aliases in installed registrations

* test(package): join owned updater after registry fixture failure

* fix(javascript): preserve recovery acquisition and cancellation failures

* fix(session): retain cleanup blockers and snapshot request identity

* fix(evidence): preserve format-owned JSON recovery diagnostics

* test(evidence): cover BOM recovery across bundle commands

* fix(tracing): retain caller reasons at traversal checkpoints

* fix(inventory): require released cleanup before capability fallback

* fix(hopper): drain retained publishers after current close failure

* fix(native): preserve unknown Swift extension contexts

* fix(apple): qualify unexamined nib content coverage

* fix(frida): preserve session refusal and close execution effects

* fix(rizin): retain cancellation and recover cleanup correlation

* fix: retain Electron file admission facts across adapters

* fix: preserve selected Electron display authorization

* fix(javascript): retain unavailable HTML reference scopes

* fix(javascript): preserve capture timing and detached receiver authority

* fix(ida): preserve producer failure and caller abort outcomes

* fix(cli): retain nested facts in Markdown tables

* fix(cli): present argument guidance without validator cause dumps

* docs(setup): define optional native update verification limits

* docs(clients): distinguish discovery and schema recovery boundaries

* docs(setup): explain npm workspace launcher failures

* fix(verification): bind Copilot schema profile to observed inputs

* fix(javascript): scope Next package admission by container identity

* fix(apple): align asset allowance and category fixture types

* fix(protocols): honor runtime queue and Frida identity types

* test: parse Electron listing Evidence before comparison

* fix(evidence): omit absent format recovery metadata

* fix(contracts): validate historical source examples as exact JSON values

* fix(javascript): consume per-field CommonJS publication facts

* fix(gdb): correlate raw-output completion and retain read-only TTY proof

* fix(native): retain completed LLDB facts across output-close cancellation

* fix(hopper): retain failed private-display probe cleanup authority

* style(lifecycle): format traversal and fallback edits

* fix(apple): own ordered nib collections and registry parentage

* fix(process): attribute trace omissions without changing shared successors

* fix(cli): share builtin argument ownership with MCP routing

* fix(cli): count emitted documents and normalize token windows

* fix(inspector): retain captures through authorization cleanup failures

* fix(artifacts): retain declared ASAR companion occurrence facts

* fix(javascript): separate endpoint syntax correspondence from target identity

* fix: distinguish native process exit from sampling failure

* fix(javascript): retain selected-root access and filesystem failure facts

* fix(browser): retain truthful cleanup facts and document request ownership

* fix(native): share exact Objective-C class symbol interpretation

* fix(go): retain completed artifact facts through finalization

* fix(native): preserve seed analysis failures in value traces

* fix(native): classify observed dSYM inspection refusal

* test(apple): assert observed collection edge contract

* fix(javascript): retain primary interruption through worker cleanup

* fix(cutter): retain completion-read failures during cleanup retry

* fix(cli): validate output windows and finish pending writes

* fix(mcp): require absolute JavaScript application inputs

* fix(capabilities): report missing navigation targets

* fix(cli): support system Bash completion registration

* fix(cli): retain narrowed token-count format handling

* test(ida): preserve exact optional resource method shape

* test(javascript): retain valid unknown endpoint coverage shape

* fix(ghidra): try keg JDKs after failed PATH launchers

* fix(browser): retain capacity refusal through terminal progress

* fix(reference): require exact optional JSON diagnostics at the source schema

* fix(apple): retain reader outcomes and cleanup ownership

* fix(elf): qualify exports without dynamic symbol declarations

* fix(mips): retain unexamined RELR declaration coverage

* fix(metadata): preserve canonical PowerPC subject identity

* fix(native): preserve LLDB discovery failure classifications

* fix(native): report absent launch process before cleanup classification

* fix(rizin): preserve primary outcomes across final cleanup

* fix(browser): preserve admission failures and retain policy transport cleanup

* fix(objdump): preserve input issues and provider launch failures

* test(apple): attribute omitted edges to their node capacity limit

* chore(metadata): synchronize packaged skill version

* fix(cli): consume canonical output validation state

* fix(views): preserve owning layout field diagnostics

* test(pyinstaller): distinguish path and metadata allowance failures

* fix(native): compose the selected command cause once

* fix(setup): require HTTPS throughout Hopper download redirects

* fix(ghidra): narrow fallback probe failures before projection

* fix(native): require observed symbol origin for named identity

* fix(javascript): retain unavailable source processing scopes

* fix(cli): share declared output-error redaction

* docs(windows): explain MCP registration launcher limits

* fix(session): preserve binding commitments during restoration

* fix(environment): retain primary facts through server cleanup failure

* fix(pe): distinguish unsupported overlapping header layouts

* fix(cli): keep positional admission placeholders omittable

* test(cli): expose JavaScript progress workflow failure output

* test(browser): match retained subframe limitations structurally

* fix(javascript): preserve capture and property-key effect ownership

* fix(ghidra): attribute import policy conflicts to settings

* fix(native): validate Mach-O command framing before admission

* docs(mcp): state plist inspection session prerequisite

* fix(electron): separate script frame admission from declared source labels

* test(environment): read asynchronous owner observations through contracts

* test(javascript): distinguish resolver URLs from existing module targets

* fix(ghidra): retain neutral recovery for observed SIGKILL

* fix(hopper): preserve unknown relationship completeness

* docs(native): clarify Darwin runtime breakpoint names

* fix(browser): preserve unknown script ownership through reconciliation

* fix(electron): keep optional provenance fields within JSON evidence values

* fix(native): retain Mach-O architecture discriminant narrowing

* test: give oversized registration groups semantic owners

* test: group boundary registration by observed behavior

* fix: normalize retained terminal observations with sampled identities

* fix: identify missing Electron lineage ownership markers

* fix(ida): retain registration constraints and identity refusal guidance

* fix(kuna): retain published exports through staging cleanup failures

* fix(nativeaot): charge only examined candidate table entries

* docs: clarify batch decompilation retention limits

* fix(javascript): distinguish deferred bootstrap installer writes

* fix(native)!: preserve UI scalar facts and selection failures

BREAKING CHANGE: Accessibility node value can also be a JSON number or boolean. Strings and null retain their prior meaning.

* fix(javascript): retain activation order for captured scope exits

* test(pyinstaller): use an absolute drive path for refusal coverage

* fix(javascript): preserve route selection owners and uncertainty

* fix(javascript): prove native URL constructor paths

* refactor(tests): separate fixtures and registration groups

* test(native): distinguish qualified symbol identity controls

* fix(ida): preserve busy details across timeout attribution

* fix(cli): unify builtin and global completion scope

* test(ghidra): group exit-signal evidence controls

* fix: retain narrowed terminal observation values

* fix(cli): bind completion input to public registry type

* fix(flutter): qualify unsupported unified snapshot profiles

* test(native): bind origin comparison to containing entries

* fix(cli): remove completion markers on system Bash

* test(ida): preserve physical workspace traversal controls

* fix(native): retain unknown UI executable identity

* fix(native): unify LLDB discovery preparation failures

* fix(evidence): preserve primary semantics during pending cleanup

* fix(evidence): retain owner finalization reports across cleanup envelopes

* test(browser): inspect provenance in owned graph observations

* test(flutter): assert canonical unsupported profile error code

* fix(javascript): capture primitive return values at evaluation

* fix(mcp): own cancellation and raw input throughout startup

* test(ghidra): construct exact import policy targets

* fix(gdb): preserve configured executable launch recovery

* test(ghidra): own fixed import target fixtures

* docs(readme): align localized guide link occurrences

* test(evidence): retain structured input issues through cleanup owners

* test(mcp): await observed EOF before releasing startup

* fix(electron): retain bounded source frame claims

* fix(elf): validate declared file extents before provider admission

* fix(elf): align metadata and admission fixture error types

* test(wasm): place parsed interface comparison at provider boundary

* test(ida): place capability composition at provider boundary

* test(browser): place PNG contract execution at provider boundary

* perf(test): share SDK catalog compilation across deferred clients

* fix(cli): trim repeated selector discovery guidance

* revert(test): remove unproven SDK compiler sharing

This reverts commit 56eca648d. The equivalent completed two-client measurement increased elapsed and CPU time, so compiler sharing has not established a benefit.

* refactor(managed): retain decoded metadata token facts

* test(managed): scope cleanup failures to snapshot reads

* test(cli): bound selector rejection cases by command

* test(mips): distinguish damaged ELF admission from partial ABI evidence

* test(reconstruction): declare authored function symbol identity

* test(rizin): register terminal admission at provider boundary

* test(process): align root admission fixture with observer allocation ownership

* test(mips): project inspected ABI facts without undefined overrides

* refactor(browser): remove obsolete web script reconciliation

* refactor(javascript): remove obsolete in-process projection runner

* refactor(managed): retain TypeDef row ownership through projections

* test(process): assert canonical admission failure through cleanup envelopes

* fix(managed): select types from retained definition records

---------

Co-authored-by: morluto <[email protected]>
Co-authored-by: gensokyo-dev <[email protected]>
2026-10-24 06:19:39 +08:00
morluto 488c21ab2e test(ghidra): distinguish stopped signal evidence from OOM attribution 2026-10-24 06:13:53 +08:00
morluto cef641414d fix(native): validate thin Mach-O CPU identity before decoding 2026-10-24 06:13:53 +08:00
morluto 59022b6a3a fix(browser): share decoded response emptiness across capture views 2026-10-24 06:13:53 +08:00
morluto 836cbd3244 fix(browser): retain composed artifact cleanup owners across retries 2026-10-24 06:13:53 +08:00
morluto a5de93a214 fix(reconstruction): qualify original process evidence at its owner 2026-10-24 06:13:53 +08:00
morluto 3914e72f1b fix(setup): admit client policy from the current snapshot 2026-10-24 06:13:53 +08:00
Deepak Kambalaandmorluto ba9c5bc686 fix(pe): accept WIN_CERTIFICATE padding counted in dwLength (#6721)
* fix(pe): accept WIN_CERTIFICATE padding counted in dwLength

* perf(pe): reuse decoded PKCS#7 ASN.1 tree

* style: format files inherited from main for CI

---------

Co-authored-by: morluto <[email protected]>
2026-10-24 06:04:41 +08:00
morluto 1252f17f0e fix(ci): model bundled runtime entries and dispatcher fixtures 2026-10-24 06:01:35 +08:00
morluto 0a91b6c67b fix(adb): retain malformed numeric listing evidence 2026-10-24 06:01:35 +08:00
Akshat Gupta 960b742581 fix(ghidra): report kernel OOM kills as a memory constraint (#6748)
* fix(ghidra): report kernel OOM kills as a memory constraint

A kernel or container OOM killer ends Ghidra with SIGKILL and prints no
java.lang.OutOfMemoryError, so the failure fell through to the generic
execution_failure that points at `rea doctor`.

On Linux with cgroup v2, read the process's own memory.events oom_kill
count before launch and again after a SIGKILL. When it rose, project
resource_constraint (memory) with the group's memory.max, and advise on
the whole process tree (native decompiler processes included) instead of
a larger Java heap. Otherwise name the signal as a possible OOM kill.

Fixes #5859

* fix(ghidra): scope OOM-kill attribution to what the cgroup proves

Review follow-up for the SIGKILL projection:

- The process supervisor records stop_requested before it sends any
  signal, and Ghidra diagnostics carry it, so a SIGKILL from REA's own
  stop is never reported as an outside kill. Only process and protocol
  failures qualify; launcher (start) failures can include REA's rollback.
- Word the memory constraint as an OOM kill in REA's cgroup or a child
  group; the count is per cgroup, not per process tree.
- Keep the open_binary reopen guidance for an established session.
- Resolve the cgroup directory through the cgroup2 mount in
  /proc/self/mountinfo, so subtree mounts are joined from their root,
  and read kernel files to EOF within a budget.
- Report memory_max as "max" for an unlimited group and null only when
  the limit could not be read.
2026-10-24 05:53:05 +08:00
Cassie_FoxandClaude Sonnet 5.5 36cae90161 docs(readme): show a dark showcase image on dark themes (#6915)
The showcase illustration was a single light PNG, so it stayed a white
slab on GitHub's dark themes. Redraw it as a light and a dark SVG, and
swap the linked image in README.md and all 21 translations for a
<picture> with a prefers-color-scheme: dark source. Alt text and link
targets are unchanged. The PNG is deleted because nothing else uses it.
website/figures.md notes the pair.

🤖 Generated with Claude Code (Sonnet 5.5) 🦊

Co-authored-by: Claude Sonnet 5.5 <[email protected]>
2026-10-24 05:51:22 +08:00
morluto bcd18d2898 fix(managed): select types from retained definition records 2026-10-24 05:36:41 +08:00
morluto 097b604039 merge: consolidate upstream projection cleanup 2026-10-24 05:34:43 +08:00
morluto 81727c3da3 test(process): assert canonical admission failure through cleanup envelopes 2026-10-24 05:33:03 +08:00
morluto f470350528 refactor(managed): retain TypeDef row ownership through projections 2026-10-24 05:32:59 +08:00
morluto 15edc71914 refactor(javascript): remove obsolete in-process projection runner 2026-10-24 05:32:43 +08:00
morluto 1778a3d494 refactor(browser): remove obsolete web script reconciliation 2026-10-24 05:32:28 +08:00
morluto 360ced26d1 test(mips): project inspected ABI facts without undefined overrides 2026-10-24 05:27:39 +08:00
morluto b227c301e5 test(process): align root admission fixture with observer allocation ownership 2026-10-24 05:26:57 +08:00
morluto 658c29c9d6 test(rizin): register terminal admission at provider boundary 2026-10-24 05:25:09 +08:00
morluto 5ddd7dc0d4 test(reconstruction): declare authored function symbol identity 2026-10-24 05:25:09 +08:00
morluto ac6e0e258a test(mips): distinguish damaged ELF admission from partial ABI evidence 2026-10-24 05:25:09 +08:00
morluto 8d3ed58cb7 merge: consolidate upstream managed token facts 2026-10-24 05:25:09 +08:00
morluto f4179f511f test(managed): scope cleanup failures to snapshot reads 2026-10-24 05:21:04 +08:00
morluto 271ef32f0c refactor(managed): retain decoded metadata token facts 2026-10-24 05:21:04 +08:00
morluto eac392d085 revert(test): remove unproven SDK compiler sharing
This reverts commit 56eca648d. The equivalent completed two-client measurement increased elapsed and CPU time, so compiler sharing has not established a benefit.
2026-10-24 05:19:41 +08:00
morluto 5909430771 perf(test): share SDK catalog compilation across deferred clients 2026-10-24 05:16:18 +08:00
morluto 872d46a0d9 test(browser): place PNG contract execution at provider boundary 2026-10-24 05:13:41 +08:00
morluto 0adcd83b1a test(ida): place capability composition at provider boundary 2026-10-24 05:13:41 +08:00
morluto c25ee582c5 test(wasm): place parsed interface comparison at provider boundary 2026-10-24 05:13:41 +08:00
morluto 53601d0bdf merge: consolidate upstream identity and representation owners 2026-10-24 05:13:41 +08:00
morluto e8a44f8679 fix(elf): align metadata and admission fixture error types 2026-10-24 05:08:24 +08:00
morluto a2235b09ac fix(apk): classify damaged Zip64 tail framing
Separate malformed locators and fixed records from unsupported disk layouts or numeric representation limits at the ZIP tail owner. Preserve the classic 65535-entry fallback and existing downstream parsed facts.

Add public CLI and stdio MCP valid, damaged, restored and unsupported controls; document the bounded validation scope.

Fixes #6471
2026-10-24 05:05:23 +08:00
morluto a8609d562f fix(process): preserve observation-root failures and omitted symlinks
Keep typed search denial and acquired snapshot facts through cleanup and finalization. Record unfollowed symlink coverage at the snapshot owner, preserve physically enumerated parents, and retain uncertainty beneath omitted links without losing proven sibling deletions.

Update parsed omission contracts, public CLI/MCP regressions and documentation without introducing a new lifecycle or coverage framework.

Fixes #6446
Fixes #6444
2026-10-24 05:05:22 +08:00
morluto a8fc3307cd fix(protobuf): own descriptor file identity before registry admission
Coalesce equal parsed file definitions and reject conflicting same-name definitions before registry import resolution. Preserve field presence, known and opaque facts through one budgeted SDK parse while retaining original descriptor bytes and digest.

Keep unique files in their original order, reuse the existing admission traversal, and cover public CLI/MCP import, ordering, presence and source-provenance controls. Document REA ambiguity policy separately from protoc precedence.
2026-10-24 05:05:22 +08:00
morluto 462b73ac9b fix(native): bind core segment names to their architecture
Validate recorded-core program-header projections against the selected pyelftools architecture table after binding e_machine to the snapshot. Preserve numeric unknowns and reject names borrowed from another machine or inconsistent header bytes.

Add source-binding malformed controls and authored auxiliary-header carriers to the existing real CLI/MCP verification lane. Document pinned parser semantics separately from payload interpretation and real crash coverage.

Fixes #6538
2026-10-24 05:05:22 +08:00
morluto 1081f81484 fix(browser): preserve unknown worker target ownership
Parse optional CDP worker relationships once for browser and Electron collectors. Keep absent ownership unavailable, malformed relationships invalid, and proven foreign targets outside the selected scope; retain existing origin and file authorization.

Add public CLI protocol fixtures and documentation for coverage and withheld details.

Fixes #6475
2026-10-24 05:05:22 +08:00
morluto d13a036e34 fix(reconstruction): retain scoped unknowns in overall claims
Select active constituent-dimension unknowns for an overall comparison claim while preserving comparison identity and exact scope for individual claims. Use the same selected facts for classification, citations, limitations and recommended probes; remove redundant aliases.

Cover stdio MCP registration, aggregate and independent claims, resolved latest revisions, source provenance and foreign comparison scopes.

Fixes #6879
2026-10-24 05:05:22 +08:00
morluto 651afa4455 fix(elf): validate declared file extents before provider admission 2026-10-24 05:02:22 +08:00
morluto aa4572828f fix(electron): retain bounded source frame claims 2026-10-24 05:02:21 +08:00
morluto d1fdaaf386 test(mcp): await observed EOF before releasing startup 2026-10-24 05:02:21 +08:00
morluto 1849a828b2 test(evidence): retain structured input issues through cleanup owners 2026-10-24 05:02:21 +08:00
morluto fc2d91a45f docs(readme): align localized guide link occurrences 2026-10-24 05:02:21 +08:00
morluto 33d00a0601 test(mcp): verify recovery guidance through initialized clients 2026-10-24 05:02:09 +08:00
morluto e05beb39c2 refactor(apple): reuse artifact stream byte validation 2026-10-24 05:02:09 +08:00
morluto 250e209f32 refactor(managed): give MethodDef row facts one owner 2026-10-24 05:02:09 +08:00
d65c027c2d fix(wasm): retain glue query and fragment across line terminators (#6802)
fix(wasm): retain multiline glue query and fragment literals

Co-authored-by: gongfpp <[email protected]>
Co-authored-by: gensokyo-dev <[email protected]>
2026-10-24 05:00:12 +08:00
gensokyo-dev 57b1f82367 perf(native): query retained export table facts (#6907)
* perf(native): query retained export table facts

* docs(perf): record native export task comparisons

* fix(native): preserve omitted export projection fields

* docs(perf): refresh final export view qualification

* test(native): verify sparse export view digests
2026-10-24 05:00:05 +08:00
PJY | ProviDER d29ae43aeb docs: add missing src/ directories to architecture.mermaid (#6908)
* docs: add 21 missing src/ dirs to architecture.mermaid (#6901)

* docs: restore KUNA and AUX node declarations lost in merge

* docs: remove final conflict marker
2026-10-24 04:53:25 +08:00
Cassie_FoxandClaude Sonnet 5.5 beb41f8145 docs(website): add dark-mode styles to the favicon and social card (#6909)
The favicon tile (#20252b) is almost invisible on a dark browser tab
strip, and the social card opened on its own is light only. Add one
prefers-color-scheme: dark block to each SVG. Each edit is a pure
insertion, and the light render is 0 pixels different in magick and in
CairoSVG. CairoSVG ignores the block, so the PNG that link previews
fetch stays light. website/figures.md says so.

🤖 Generated with Claude Code (Sonnet 5.5) 🦊

Co-authored-by: Claude Sonnet 5.5 <[email protected]>
2026-10-24 04:53:00 +08:00
morluto b03a15e6d1 test(ghidra): own fixed import target fixtures 2026-10-24 04:49:29 +08:00
morluto 2b8e388bbe fix(native): unify legacy Mach-O platform interpretation 2026-10-24 04:49:21 +08:00
morluto 5f4bd6ba34 refactor(cli): execute validated model and GPU requests 2026-10-24 04:49:21 +08:00
morluto 171f56a5ed refactor(setup): own parsed registration command facts 2026-10-24 04:49:21 +08:00
morluto 6d4cc6e6a4 refactor(managed): share compressed integer format decoding 2026-10-24 04:49:21 +08:00
morluto b9212b5979 fix(gdb): preserve configured executable launch recovery 2026-10-24 04:47:45 +08:00
morluto 9ecf48bd9d test(ghidra): construct exact import policy targets 2026-10-24 04:47:45 +08:00
morluto 2a4c70e3b7 fix(mcp): own cancellation and raw input throughout startup 2026-10-24 04:45:14 +08:00
morluto 7356434202 merge: consolidate upstream ownership and coverage fixes 2026-10-24 04:43:33 +08:00
morluto 87a5be3000 fix(environment): normalize values from owned response facts
Require a real clock-field context for numeric time normalization and actual rendered write rules for generated strings. Preserve literal read, replay and missing-response values, and retain generated aliases and distinctness within each response.

Keep clock context separate from entity identity while traversing arrays. Add domain and public CLI controls for measurements, business strings, confirmed writes, missing responses and generated-value relationships, with documented inference limits.

Fixes #6878
Fixes #6837
2026-10-24 04:42:09 +08:00
morluto 3e3c9eadb5 fix(protobuf): reject ambiguous descriptor identities
Validate message-local field numbers and fully qualified registry identities during existing descriptor admission before constructing the schema registry. Preserve repeated wire occurrences, enum aliases and independent scopes, with bounded name allocation and retained capture provenance through CLI/MCP refusals.

Fixes #6834
2026-10-24 04:42:09 +08:00
morluto 3b94f2c2ef fix(signals): honor selected files and input format options
Keep staged recording names as relative file operands, including a literal dash. Recognize exact selected input-option refusal captions even when sigrok exits successfully or a caller key contains a newline; retain producer diagnostics and typed request failures across CLI/MCP.

Fixes #6875
Fixes #6839
2026-10-24 04:42:09 +08:00
morluto 52ca222bd0 fix(il2cpp): preserve loader ownership and ABI field boundaries
Resolve pointer and scalar facts through their shared loader-aware owners before registration search or type rendering. Preserve unresolved names and attributes, read count fields at their declared width, and admit declared zero-fill registration tails without accepting unmapped bytes.

Cover relocation overlap and adjacency, loader-bound fields, ABI padding, nullable names, and CLI/MCP behavior; document format and verification limits.

Fixes #6881
Fixes #6873
Fixes #6871
2026-10-24 04:42:09 +08:00
morluto 27b077edea fix(javascript): capture primitive return values at evaluation 2026-10-24 04:39:50 +08:00
morluto 5a099ef9ef test(flutter): assert canonical unsupported profile error code 2026-10-24 04:38:08 +08:00
morluto c8be9df211 test(browser): inspect provenance in owned graph observations 2026-10-24 04:38:08 +08:00
morluto 640a0cf423 fix(evidence): retain owner finalization reports across cleanup envelopes 2026-10-24 04:38:08 +08:00
morluto c2cc81927a fix(evidence): preserve primary semantics during pending cleanup 2026-10-24 04:38:08 +08:00
morluto 6e40f9f054 fix(native): unify LLDB discovery preparation failures 2026-10-24 04:38:08 +08:00
morluto a51a9228be fix(native): retain unknown UI executable identity 2026-10-24 04:38:08 +08:00
morluto 85292fdb26 test(ida): preserve physical workspace traversal controls 2026-10-24 04:38:08 +08:00
morluto fd254b5e37 fix(cli): remove completion markers on system Bash 2026-10-24 04:38:08 +08:00
morluto 7e8c7865d4 test(native): bind origin comparison to containing entries 2026-10-24 04:38:08 +08:00
morluto f2db494bfc refactor(artifacts): remove obsolete contradiction identity input 2026-10-24 04:36:00 +08:00
morluto 9c0c2765c5 refactor(browser): share cleanup observation facts 2026-10-24 04:36:00 +08:00
morluto bc1c5073df refactor(managed): carry parsed signature type coordinates 2026-10-24 04:36:00 +08:00
Muhammad Hamzaandgensokyo-dev 9e6c6241d7 fix(environment): match served items whose route repeats a separator (#6876)
* fix(environment): match served items whose route repeats a separator

callSegments split the tool path template itself and dropped empty literal
segments, while the held collection is split by segmentsOf, which keeps an
internal empty segment. A captured item route such as /api//books/{id} was
stored under collection /api//books but searched with ["api","books"], so
collectionMatch rejected it and a served item read reported the extracted
entity as missing.

Split the call template through segmentsOf, the same owner used for the held
collection path, so a repeated separator is preserved on both sides and the
item answers from its extracted collection. Single-separator routes stay
distinct and param positions are unchanged.

Fixes #6847

* fix(environment): keep repeated-separator collections writable

Preserving empty literal segments in callSegments made createdCollection's
missing-argument guard reject a create whose route repeats a separator, such
as POST /api//books: the empty literal segment tripped the
segments.some(text.length === 0) check and answerCreate refused the valid
route. Restrict the guard to empty supplied parameter segments, so a missing
path argument is still refused while a repeated-separator collection stays
writable as well as readable.

---------

Co-authored-by: gensokyo-dev <[email protected]>
2026-10-24 04:33:51 +08:00
a91412aaf9 feat(native): export function pseudocode to resumable directories (#6170)
* feat(decompile): write function pseudocode to a directory and return a manifest

Add decompile_all and `rea decompile-all` so a caller can write one
provider pseudocode file per function, or a filtered set, and receive
only a small manifest. The result stays bounded when the program is
larger than a local model's tool-result cap.

Resume continues an export for the same target digest, provider,
analysis profile, and layout. Matching files are skipped, conflicting
files stay unchanged, and one function's failure does not stop the
rest. Cancellation leaves files already written. Requesting dwarf
layout reports that compilation-unit paths are unavailable.

Simulated-provider tests cover the manifest, resume, filters, limits,
cancellation, and the failure cap. MCP registration, CLI coverage,
schema validity, the product catalog, check:fast, and docs:check
passed. Real Ghidra was not exercised.

Refs #6090

* fix(decompile): keep unknown export identity unresumable and recover torn journals

Resume treated two missing target or profile digests as a match, so an
export of one binary could continue into another. Resume now requires both
digests, and a decompiler observation without the export's profile, or with
a different target, is recorded as profile_changed.

A crash during a journal append left a torn final line that made the export
unresumable. Text after the last newline is now treated as uncommitted and
truncated before the next append, and stale temporary files are removed.

An inventory row with an empty address aborted the whole export because its
failure record failed the journal schema. Such rows are now excluded and
counted in the limitations.

A limited resume retried the same recorded failures first and never reached
new functions. Functions not yet attempted now run before retries. File path
reservations come from every journal line, so an output_io record no longer
releases a path. The index is rebuilt from the journal in numeric address
order, and journal close and cleanup failures return a Result.

The cancellation test now cancels after a file is committed and checks that
file survives and is skipped on resume. Redundant snapshot-replay checks are
removed, the CLI limit uses the contract constant, and --snapshot no longer
claims decompile-all updates it.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01AoJZDPcrgmMsCqLG4LfiFb

* fix(decompile): refuse resume of a missing export and repair journals by byte offset

A second review round found that resume against a directory that does not
exist silently started a full new export and reported it as resumed. Resume
now requires an existing export directory.

The torn-line repair measured the committed journal from decoded text, so
damaged UTF-8 could move the truncation point past the end of the file. The
journal is now read as bytes and cut at the last newline byte, and an intact
journal is not truncated, so a concurrent append is kept.

A per-function observation for a different target is now recorded as
target_changed instead of profile_changed. A failure to remove a partly
created output directory is reported instead of hidden. The journal handle is
closed on every path again, including an unexpected throw. The path allocator
no longer gives up after 1,000 collisions and reuses a taken name.

Tests now use createTestTempDirectory, assert journal lines against the
journal schema, and cover resume of a missing directory, resume with an
unknown analysis profile, and a target-mismatched observation.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01AoJZDPcrgmMsCqLG4LfiFb

* fix(decompile): keep a function's first file name across failed retries

Reserving every journal path stopped other addresses from taking a path, but
an address looked up only its latest record. After its file went missing and
a retry failed without a path, the next resume moved the function to a
hash-suffixed name and orphaned the original. Each address now keeps the
first path the journal gave it.

A matched commit still ran the decompiler but did not count toward limit.
The limit now counts every function taken on after skipping verified files.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01AoJZDPcrgmMsCqLG4LfiFb

* fix(decompile): never follow a journal symlink and check procedure_info identity

Torn-line repair truncates the journal, and the journal was opened through
any symlink in the export directory, so repair could truncate a file outside
it. The journal must now be a regular file and is opened without following
symlinks where the platform supports it.

Body ranges from procedure_info were written without checking that the
observation belonged to this export or described the requested function. An
observation with a different provider, profile or target, or another entry
point, now leaves the body unavailable with that reason.

File, journal and index writes now use FileHandle.writeFile, which writes the
whole buffer instead of trusting one write call. The stale temporary sweep is
removed: a matching file name does not prove this export created the file.
The inverted-range test now uses one address space so it reaches the
inversion check.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01AoJZDPcrgmMsCqLG4LfiFb

* fix(decompile): bound returned caveats and keep verified checksums

Ghidra names each terminal call's address in its per-function caveats,
so decompile_all copied one new caveat per function into the returned
Evidence and the answer grew with the program. The returned Evidence
now keeps the eight most often observed distinct per-function caveats,
grouped by provider and text as they are rendered, plus a count of the
rest. Inventory caveats are unchanged, and other workflows are not
bounded. Each function keeps the caveats of the observations it used:
a written file in its header, now including procedure_info caveats,
and a failed attempt in its journal and index record. A procedure_info
observation for another target or profile is foreign and contributes
none.

A failure record for a function that was already written replaced the
record holding its checksum, so after a transient read error a resume
decompiled that function again. The journal now keeps the last
successful write separately from the latest attempt. Resume verifies
against that write and records it again once the file verifies.

A caller abort that arrives while the export finishes now returns
cancellation with the caller's reason instead of a success manifest,
as #6143 does for other late finalization. A publish, index or close
failure stays the primary error.

Also make the missing-journal resume error say what to do, and add
boundary tests: one runs decompile-all through the real CLI against a
fake Kuna to check flag mapping, the relative output directory,
filters and limit; one runs decompile_all through production MCP
registration to check that the caveat bound is applied.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01AoJZDPcrgmMsCqLG4LfiFb

* fix(decompile): keep each function's complete caveats in the index

The returned Evidence keeps eight distinct per-function caveats and
counts the rest, pointing readers at the export. A written function's
file header truncates its caveats after 4,000 characters, and its
journal record kept none, so a large function with many terminal calls
could lose caveats from both the answer and the export.

Every journal record, written or failed, now keeps the complete caveats
of the observations its attempt used, and decompile-index.json carries
them. The header stays a readable summary and, when truncated, says
that the index keeps the complete list.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01AoJZDPcrgmMsCqLG4LfiFb

* fix(decompile): report external functions instead of failing them

Ghidra classifies imports as external functions with an empty body and
returns null pseudocode for them, as its limitations state. decompile_all
recorded each one as an invalid_pseudocode failure ("not text"), so a
dynamically linked program reported every import as a decompiler failure:
34 of 116 functions in /usr/bin/xxd.

procedure_info owns the external classification. When it classifies a
function as external, the export now records status "external", counts it
in counts.external, writes no file and requests no pseudocode. The record
keeps the body fact as observed. The journal header pins the target,
provider and profile, so a resume skips a recorded external function
without asking the provider again; a limit below the external count still
leaves nothing remaining. A null pseudocode result for a function whose
classification is not external is reported as no_pseudocode.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01AoJZDPcrgmMsCqLG4LfiFb

* fix(decompile): report finalization failures after a cancelled export

When the caller's abort stopped the publish loop, decompile_all returned
that cancellation before checking the index write and the journal close,
which had already run. A failure to finalize the export was hidden, so
the caller could not tell the saved index or journal was incomplete.

Publish failures stay primary, then index and close failures. A
cancellation counts as the caller's only when the caller's signal is
aborted, and is then reported after a successful finalization with the
caller's own reason. A provider's own cancellation, without a caller
abort, stays a publish failure like any other. Tests cover an index
failure and a close failure after the caller stops the loop, a stopped
loop with a successful finalization keeping the caller's reason, and a
provider cancellation staying primary over an index failure.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01AoJZDPcrgmMsCqLG4LfiFb

* fix(decompile): remove a created output directory that cannot be secured

createPrivateDirectory ran mkdir and chmod as one step. When mkdir
succeeded and chmod failed, it returned an error before #create reached
its cleanup, leaving an empty directory behind. A fresh export then
refused the existing directory, and a resume refused it for having no
journal, so the caller had to remove it by hand.

mkdir and chmod are now separate. A mkdir failure created nothing, so
nothing is removed. A chmod failure after this call created the
directory removes it with a non-recursive rmdir: it is still empty, so a
path another process filled or replaced meanwhile is refused rather than
deleted, and that refusal is reported.

A filesystem boundary test fails chmod for the new root and checks the
directory is gone and a retry succeeds. A module test confirms that a
function written earlier and now classified as external still reports a
changed file at its path as output_exists.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01AoJZDPcrgmMsCqLG4LfiFb

* refactor(process): share the socket lease with owners other than Hopper

The socket lease that keeps two REA processes from opening the same Hopper
document lived only in HopperLease, with Hopper error messages built in.
A decompile export needs the same guarantee for its output directory.

OwnedLease now holds the lease mechanism unchanged: a listening local
socket that the operating system ends with its process, owner identity
answered by the live holder, and recovery of a stale socket serialized
through a recovery directory. Callers map a tagged LeaseFailure to their
own error. HopperLease keeps its exports and maps each failure to the
same HopperStartError message as before. On Windows the lease is a named
pipe, which needs no stale recovery.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01AoJZDPcrgmMsCqLG4LfiFb

* fix(decompile): hold the output directory per run and stop on repeated write failures

Two runs could resume the same output directory at once. Each read the
journal once, and the index each wrote reflected only its own view, so
the last writer could drop the other run's records. A run's torn-line
repair could also truncate the other run's append.

DecompileExport now holds a lease on the output directory, named by its
device and inode, from before the journal is read (or, for a new export,
from just after the directory is created and before its journal exists)
until the journal is closed. A second run is refused as a retryable
capacity constraint that reports the holder's process ID. The lease ends
with its process, so a crashed run leaves no lock to clear. The lease is
released on every open failure, and a failed release is reported.

An export also kept decompiling every remaining function when its files
had stopped being writable mid-run, for example on a full disk, failing
each one with output_io. After five consecutive functions whose files
could not be written or read, the export now stops with an output error
that carries the counts and the last failures. A verified file or any
other outcome ends a run of failures. Committed files and records stay,
the index is still written, and a later resume continues.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01AoJZDPcrgmMsCqLG4LfiFb

* docs(native): record directory export verification

Record matched real Ghidra outputs, bounded caveat and cleanup regressions,
actual catalog-inclusive context estimates, runtime measurements and coverage
limits for the pinned integration.

* fix(native): bound export summaries and reserve portable paths

Use one case-folded reservation owner for allocation and resumed journals.
Budget inline failures and provider caveats by encoded JSON bytes while
retaining complete records in the output index. Align the tool contract
with the collector order and verify SDK result-window behavior.

* docs(native): refresh final export measurements

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: gensokyo-dev <[email protected]>
2026-10-24 04:32:11 +08:00
morluto 9cd46d6192 fix(flutter): qualify unsupported unified snapshot profiles 2026-10-24 04:28:54 +08:00
morluto 094d39926b fix(cli): bind completion input to public registry type 2026-10-24 04:28:54 +08:00
morluto 96e1a60749 fix: retain narrowed terminal observation values 2026-10-24 04:28:54 +08:00
morluto 1dff1b15fb test(ghidra): group exit-signal evidence controls 2026-10-24 04:26:54 +08:00
morluto a31b4a393a fix(cli): unify builtin and global completion scope 2026-10-24 04:26:54 +08:00
morluto 19f367257d fix(ida): preserve busy details across timeout attribution 2026-10-24 04:26:11 +08:00
morluto aa521d4348 test(native): distinguish qualified symbol identity controls 2026-10-24 04:26:11 +08:00
morluto d2dc3cfc34 refactor(tests): separate fixtures and registration groups 2026-10-24 04:25:13 +08:00
morluto 55437c1431 fix(javascript): prove native URL constructor paths 2026-10-24 04:25:13 +08:00
morluto f97747c2fb fix(javascript): preserve route selection owners and uncertainty 2026-10-24 04:25:13 +08:00
morluto afc72f32e3 test(pyinstaller): use an absolute drive path for refusal coverage 2026-10-24 04:25:13 +08:00
morluto 1872fef5c0 fix(javascript): retain activation order for captured scope exits 2026-10-24 04:25:13 +08:00
morluto cc90771af1 fix(native)!: preserve UI scalar facts and selection failures
BREAKING CHANGE: Accessibility node value can also be a JSON number or boolean. Strings and null retain their prior meaning.
2026-10-24 04:25:13 +08:00
morluto 9fb63ad066 fix(javascript): distinguish deferred bootstrap installer writes 2026-10-24 04:23:56 +08:00
morluto bfa60d596e docs: clarify batch decompilation retention limits 2026-10-24 04:23:56 +08:00
morluto 6b0c4b43a4 fix(nativeaot): charge only examined candidate table entries 2026-10-24 04:23:56 +08:00
morluto bbf7943ba6 fix(kuna): retain published exports through staging cleanup failures 2026-10-24 04:23:56 +08:00
morluto 75dfd1c747 fix(ida): retain registration constraints and identity refusal guidance 2026-10-24 04:23:56 +08:00
morluto 6739b3cd80 fix: identify missing Electron lineage ownership markers 2026-10-24 04:23:56 +08:00
morluto 20ff68914b fix: normalize retained terminal observations with sampled identities 2026-10-24 04:23:56 +08:00
morluto 9877ab7d1d test: group boundary registration by observed behavior 2026-10-24 04:23:56 +08:00
morluto a3eae6e236 test: give oversized registration groups semantic owners 2026-10-24 04:23:56 +08:00
morluto a03096cb85 fix(native): retain Mach-O architecture discriminant narrowing 2026-10-24 04:23:56 +08:00
morluto 7d941e2799 fix(electron): keep optional provenance fields within JSON evidence values 2026-10-24 04:23:56 +08:00
morluto d9a855f5cb fix(browser): preserve unknown script ownership through reconciliation 2026-10-24 04:23:56 +08:00
morluto 0929fa4209 docs(architecture): clarify map scope and provider wiring
Describe the diagram as selected layer owners and dependency paths. Link the canonical composition factories and direct Frida/Cutter entry wiring, and add the omitted Kuna deep-provider node and its verified dependencies.

Documentation checks cover source links and generated facts; no exhaustive directory inventory or new provider capability is claimed.

Fixes #6901
2026-10-24 04:21:56 +08:00
morluto 85b8624a98 fix(readiness): reconcile closure identities and outcome variants
Count each verified required obligation once across retained closure observations and validate their finite status/count rules against the supplied final ledger. Compare every retained semantic outcome variant across CLI/MCP instead of selecting the first pair, preserving all observations and Evidence.

Validation: evaluator regressions reject duplicate closure, contradictory intermediate status, and later outcome drift while retaining valid controls. The readiness verifier passes with the source-owned C fixture and actual CLI/stdio-MCP static JavaScript/Electron workflows. Variant sets do not establish equal repeat counts or run pairing; final-ledger consistency does not authenticate historical ledgers or proof freshness.

Fixes #6893
Fixes #6894
2026-10-24 04:21:56 +08:00
morluto 1e688e4201 fix(wasm): preserve partial name observations and coverage
Retain module-name displays observed before optional metadata failure so strict objdump framing can use those facts. Preserve uncertainty across name sections and report unknown completeness when unattributed producer warnings concern skipped custom metadata; keep exact warnings, core validation, WAT and artifact provenance.

Validation: focused source/process regressions and 61 real WABT1.0.42 CLI/MCP verifier cases pass. The unmodified official macOS arm64 archive matches its published SHA-256. Coverage includes outer/inner malformed name metadata, valid empty-map controls, stable core/interface facts and cleanup. No inspected WASM target was executed.

Fixes #6885
2026-10-24 04:21:56 +08:00
morluto 8b3902d377 fix(native): own complete metadata mappings and ambiguity
Resolve Go pointer descriptors and strings across complete, consistent file-backed spans, including PE headers and adjacent ELF load ranges. Reuse the canonical image-mapping selection for native exports. Preserve existing gaps, alias, zero-fill and capacity refusals.

Forward parsed zero-fill ownership through IL2CPP relocation tables, destinations and mapped-span checks. Distinguish missing spans from ambiguous ownership in the shared mapping failure: unresolved relocation targets may remain unknown, while ambiguous raw registration bytes cannot be reused as a resolved count.

Validation: parser, provider, service, native-export, IL2CPP and public CLI/MCP regressions cover PE32/PE32+ headers, split ELF descriptors/contents, relocation count conflicts, ordinary unresolved targets and unchanged inputs. Authored carriers establish static parsing, not Windows loading or actual legacy Go linker emission.

Fixes #6831
Fixes #6830
2026-10-24 04:21:56 +08:00
morluto d397ac7427 docs(native): clarify Darwin runtime breakpoint names 2026-10-24 04:19:12 +08:00
morluto 23664efdbb fix(hopper): preserve unknown relationship completeness 2026-10-24 04:19:12 +08:00
morluto ecfac46c8d fix(ghidra): retain neutral recovery for observed SIGKILL 2026-10-24 04:19:12 +08:00
morluto 80d4a74a06 test(javascript): distinguish resolver URLs from existing module targets 2026-10-24 04:19:12 +08:00
morluto b7ebccf8d3 fix(navigation): reject malformed containing-procedure results
Move the unchanged resolution contract into its domain owner and use it when
building navigation context. Preserve provider failures instead of guessing
absence from error text or malformed result shapes. Return null only for an
explicit valid negative resolution.

Remove the obsolete error-message matcher and partial name parser. Cover four
malformed result shapes, original error identity, valid positive/negative facts
and the typed MCP unreadable-output response. Original source failed six cases.

Validation: navigation source/MCP and advertised schema checks passed. Compact
and full generated catalogs are byte-identical to the parent. Static, docs,
discovery and duplication checks passed.
2026-10-24 04:18:07 +08:00
morluto d69752fd52 refactor(filesystem): share stable read state comparisons
Reuse sameRegularFileState for artifact, native plist and entropy readback.
Remove private copies of identity/size/timestamp checks and consistently include
mode in the existing stable-state contract.

Keep initial identity-only artifact admission, byte-count checks, capacity and
error precedence, symlink choices, cancellation and descriptor cleanup intact.

Validation: stable-file, plist, artifact outcome, entropy and public CLI/MCP
lifecycle regressions passed; static and documentation checks passed.
2026-10-24 04:18:07 +08:00
morluto 5477a6b83e refactor(hopper): reuse canonical Mach-O header facts
Use the existing bounded thin-header decoder for loaded and embedded FAT
identities, thin-image resolution, and both prepared-copy readback paths.
Preserve raw unsigned CPU/subtype facts and endian handling.

Keep the initial eight-byte dispatch, FAT framing, selection and ranking,
source digests, error ordering, cancellation and cleanup authority local.

Validation: Mach-O container, Hopper image/copy lifecycle and file-offset close
boundary suites passed. No attached Hopper GUI or real-provider run performed.
2026-10-24 04:18:07 +08:00
morluto ce3c27fe92 refactor(managed): unify coded-index identity and validation
Decode metadata coded indexes once into null, valid or invalid facts. Migrate
all existing consumers and remove parallel token and failure-reason decoders.
Keep required-null policy with each column, preserving tokens, reason text,
offsets, issue ordering and readable partial metadata.

Retain known table/row coordinates even when invalid; TypeRef regressions
preserve row-zero and out-of-range enclosing facts without granting validity.
Attribute naming consumes the same validated constructor coordinate.

Validation: metadata/member/native-boundary/signature source suites and public
CLI/MCP recovery workflows passed; static, docs and duplication checks passed.
2026-10-24 04:18:07 +08:00
morluto 09ba16873c fix(gpu): compare AMDGPU processors from parsed target facts
Give accepted target IDs one owner for processor identity and canonical case,
while preserving exact target spelling for selection and Evidence. Use that
fact for payload/bundle comparison instead of splitting on a colon.

Keep feature differences unknown unless exact target keys agree. Cover legacy
plus-feature declarations in both directions and mixed processor case with
individually named physical-byte regressions. The original code failed twelve
of the twenty-one comparison cases; the updated matrix passes all cases.

Validation: GPU source, physical boundary, compiled CLI and lifecycle suites
passed. Static, docs, schema and duplication checks passed. No GPU execution.
2026-10-24 04:18:07 +08:00
morluto c5d86a164d test(environment): read asynchronous owner observations through contracts 2026-10-24 04:16:47 +08:00
morluto df00588325 fix(electron): separate script frame admission from declared source labels 2026-10-24 04:16:47 +08:00
morluto aad3d88cc0 docs(mcp): state plist inspection session prerequisite 2026-10-24 04:16:47 +08:00
morluto 5796220f23 fix(native): validate Mach-O command framing before admission 2026-10-24 04:16:47 +08:00
morluto 84c327ee65 fix(ghidra): attribute import policy conflicts to settings 2026-10-24 04:16:47 +08:00
morluto c6ac60acfe fix(javascript): preserve capture and property-key effect ownership 2026-10-24 04:16:47 +08:00
morluto b3433d0187 test(browser): match retained subframe limitations structurally 2026-10-24 04:16:47 +08:00
morluto 701519a5ce test(cli): expose JavaScript progress workflow failure output 2026-10-24 04:14:51 +08:00
morluto 1bf9191bd0 fix(cli): keep positional admission placeholders omittable 2026-10-24 04:14:51 +08:00
morluto d9d77726ff fix(pe): distinguish unsupported overlapping header layouts 2026-10-24 04:14:17 +08:00
morluto 77ebfb5f8a fix(environment): retain primary facts through server cleanup failure 2026-10-24 04:14:17 +08:00
morluto 6f8dc9c139 fix(session): preserve binding commitments during restoration 2026-10-24 04:14:17 +08:00
morluto cc983b3235 docs(windows): explain MCP registration launcher limits 2026-10-24 04:14:17 +08:00
morluto ee2433a408 fix(cli): share declared output-error redaction 2026-10-24 04:14:17 +08:00
morluto 1cc16128ea fix(javascript): retain unavailable source processing scopes 2026-10-24 04:14:17 +08:00
morluto 62f7b5e4cd fix(native): require observed symbol origin for named identity 2026-10-24 04:14:17 +08:00
af3b039d31 fix(native): preserve whitespace in Mach-O segment and section names (#6842)
Fixes #6818

Co-authored-by: Abelo9996 <[email protected]>
Co-authored-by: gensokyo-dev <[email protected]>
2026-10-24 04:13:45 +08:00
morluto 98ddd04433 fix(ghidra): narrow fallback probe failures before projection 2026-10-24 04:09:23 +08:00
morluto 18acae9188 fix(setup): require HTTPS throughout Hopper download redirects 2026-10-24 04:08:59 +08:00
morluto 53405fbe44 fix(native): compose the selected command cause once 2026-10-24 04:08:59 +08:00
morluto fc11134242 test(pyinstaller): distinguish path and metadata allowance failures 2026-10-24 04:08:59 +08:00
morluto fc52858966 fix(views): preserve owning layout field diagnostics 2026-10-24 04:08:59 +08:00
morluto fc706570d9 fix(cli): consume canonical output validation state 2026-10-24 04:08:59 +08:00
morluto 94081b0103 chore(metadata): synchronize packaged skill version 2026-10-24 04:08:59 +08:00
morluto c6bc25f593 test(apple): attribute omitted edges to their node capacity limit 2026-10-24 04:07:03 +08:00
morluto 76381b29d4 fix(objdump): preserve input issues and provider launch failures 2026-10-24 04:06:57 +08:00
morluto c86288db1b fix(browser): preserve admission failures and retain policy transport cleanup 2026-10-24 04:06:57 +08:00
morluto cab6527125 fix(rizin): preserve primary outcomes across final cleanup 2026-10-24 04:06:57 +08:00
morluto a2d0af6023 fix(native): report absent launch process before cleanup classification 2026-10-24 04:06:57 +08:00
morluto 301993db04 fix(native): preserve LLDB discovery failure classifications 2026-10-24 04:06:57 +08:00
morluto e909a927c4 fix(metadata): preserve canonical PowerPC subject identity 2026-10-24 04:06:57 +08:00
morluto 22d3236c31 fix(mips): retain unexamined RELR declaration coverage 2026-10-24 04:06:57 +08:00
morluto 0a9bcbed42 fix(elf): qualify exports without dynamic symbol declarations 2026-10-24 04:06:57 +08:00
morluto 59a00d77fb fix(apple): retain reader outcomes and cleanup ownership 2026-10-24 04:06:57 +08:00
morluto 779242aef0 fix(reference): require exact optional JSON diagnostics at the source schema 2026-10-24 04:06:56 +08:00
morluto 88bbf9c006 fix(browser): retain capacity refusal through terminal progress 2026-10-24 04:05:23 +08:00
morluto 6ff682505c fix(ghidra): try keg JDKs after failed PATH launchers 2026-10-24 04:05:23 +08:00
morluto dfafc04f6d test(javascript): retain valid unknown endpoint coverage shape 2026-10-24 04:05:23 +08:00
morluto f33ab9bf38 test(ida): preserve exact optional resource method shape 2026-10-24 04:05:23 +08:00
morluto ce9cb72559 fix(cli): retain narrowed token-count format handling 2026-10-24 04:05:23 +08:00
morluto 7eb9eb021c fix(cli): support system Bash completion registration 2026-10-24 04:05:23 +08:00
morluto f8248417d7 fix(capabilities): report missing navigation targets 2026-10-24 04:05:23 +08:00
morluto 4e7fb1ee8a fix(mcp): require absolute JavaScript application inputs 2026-10-24 04:05:23 +08:00
morluto 59f0eb4244 fix(cli): validate output windows and finish pending writes 2026-10-24 04:05:23 +08:00
morluto 60a0acb396 fix(cutter): retain completion-read failures during cleanup retry 2026-10-24 04:05:23 +08:00
morluto ef00b2115a fix(javascript): retain primary interruption through worker cleanup 2026-10-24 04:05:23 +08:00
morluto d98105d650 test(apple): assert observed collection edge contract 2026-10-24 04:05:23 +08:00
morluto f83d97fe92 fix(native): classify observed dSYM inspection refusal 2026-10-24 04:05:23 +08:00
morluto e1d9ebd3cf fix(native): preserve seed analysis failures in value traces 2026-10-24 03:59:44 +08:00
morluto 3f519c5587 fix(go): retain completed artifact facts through finalization 2026-10-24 03:59:44 +08:00
morluto 782c6c6636 fix(native): share exact Objective-C class symbol interpretation 2026-10-24 03:59:44 +08:00
morluto f01f56156e fix(browser): retain truthful cleanup facts and document request ownership 2026-10-24 03:59:44 +08:00
morluto 77ed32fabc fix(javascript): retain selected-root access and filesystem failure facts 2026-10-24 03:59:44 +08:00
morluto b2cb9b6596 fix: distinguish native process exit from sampling failure 2026-10-24 03:59:44 +08:00
morluto f6b525d3af fix(javascript): separate endpoint syntax correspondence from target identity 2026-10-24 03:59:44 +08:00
morluto 6c307e9c86 fix(artifacts): retain declared ASAR companion occurrence facts 2026-10-24 03:59:44 +08:00
morluto 8b7de17833 merge: reconcile upstream boundary and format fixes 2026-10-24 03:59:44 +08:00
f713087132 fix(adb): pass int intent extras to am without leading zeros (#6843)
Fixes #6807

Co-authored-by: Abelo9996 <[email protected]>
Co-authored-by: gensokyo-dev <[email protected]>
2026-10-24 03:57:14 +08:00
morluto 09b188bb0d fix(inspector): retain captures through authorization cleanup failures 2026-10-24 03:16:30 +08:00
morluto fc404f008d fix(cli): count emitted documents and normalize token windows 2026-10-24 03:16:30 +08:00
morluto cd128795eb fix(cli): share builtin argument ownership with MCP routing 2026-10-24 03:16:30 +08:00
morluto 254439750a fix(process): attribute trace omissions without changing shared successors 2026-10-24 03:16:30 +08:00
morluto 584791fedf fix(apple): own ordered nib collections and registry parentage 2026-10-24 03:16:30 +08:00
morluto 9b7ba06661 style(lifecycle): format traversal and fallback edits 2026-10-24 03:16:30 +08:00
morluto 2fe5d972cc fix(hopper): retain failed private-display probe cleanup authority 2026-10-24 03:16:30 +08:00
morluto ef64631f07 fix(native): retain completed LLDB facts across output-close cancellation 2026-10-24 03:16:30 +08:00
morluto f3df653d9b fix(gdb): correlate raw-output completion and retain read-only TTY proof 2026-10-24 03:16:30 +08:00
morluto 9c4d194f14 fix(javascript): consume per-field CommonJS publication facts 2026-10-24 03:16:30 +08:00
morluto 562966500f fix(contracts): validate historical source examples as exact JSON values 2026-10-24 03:16:30 +08:00
morluto a954e9f488 fix(evidence): omit absent format recovery metadata 2026-10-24 03:16:30 +08:00
morluto c0e35c2e93 test: parse Electron listing Evidence before comparison 2026-10-24 03:16:30 +08:00
morluto 5f3b92531e fix(protocols): honor runtime queue and Frida identity types 2026-10-24 03:16:30 +08:00
morluto 0321bb91a7 fix(apple): align asset allowance and category fixture types 2026-10-24 03:16:30 +08:00
morluto 6103fddd45 fix(javascript): scope Next package admission by container identity 2026-10-24 03:16:29 +08:00
morluto ca01cf5a1f fix(verification): bind Copilot schema profile to observed inputs 2026-10-24 03:16:29 +08:00
morluto 292f1058d9 docs(setup): explain npm workspace launcher failures 2026-10-24 03:16:29 +08:00
morluto b791f029aa docs(clients): distinguish discovery and schema recovery boundaries 2026-10-24 03:16:29 +08:00
morluto 8bb466d0dc docs(setup): define optional native update verification limits 2026-10-24 03:16:29 +08:00
morluto 4b029a693d fix(cli): present argument guidance without validator cause dumps 2026-10-24 03:16:29 +08:00
morluto 3f5d879414 fix(cli): retain nested facts in Markdown tables 2026-10-24 03:16:29 +08:00
morluto a4e9b61e9f fix(ida): preserve producer failure and caller abort outcomes 2026-10-24 03:16:29 +08:00
morluto da0480a665 fix(javascript): preserve capture timing and detached receiver authority 2026-10-24 03:16:29 +08:00
郭立 Guo Li 9fd78a74f8 fix(native): preserve unknown ELF export coverage without a dynsym section
Closes #6792.

ELF images with `PT_DYNAMIC` but no `SHT_DYNSYM` section incorrectly reported a complete, empty export inventory. The shared ELF directory reader now owns the dynamic-segment observation; CLI/MCP report `unsupported` with a reason. Existing symbol-table results remain intact. Read-only format correction; no new execution authority or public schema.

Validation on the current main integration:

- `npm run check`, test discovery, and 166 selected Linux tests pass, including actual CLI/stdio MCP cases and PE/IL2CPP/GPU/Rust reader consumers. Original author validation: 113 tests on macOS, including regressions that failed before the fix; test discovery passed.
- Real Debian libc (1,926,232 bytes) and libLLVM 14 (109,967,296 bytes), with only section-header-count/name-index bytes changed, reproduce the old false absence in both public adapters. The reviewed implementation returns unsupported in both adapters. Independent Python maps unchanged `PT_DYNAMIC` declarations and selected symbol bytes; original `readelf` agrees. The reduced files are not loaded or executed; `readelf -D` itself cannot enumerate these reduced layouts, and that failed oracle attempt is retained.
- Unmodified libc still returns all 3,025 exports, byte-identical complete Evidence and the same Evidence ID as the baseline. Resource-guarded public probes completed without stops or payload leftovers. No speed, memory or model-token improvement is claimed for this coverage fix.

Codex review completed with no findings. CI failed: all 61 observed failures (including 47 omitted from the aggregate report by canceled shards) reproduce with identical complete failure diagnostics on the exact CI base and merge trees. Baseline-only bypass is explicitly authorized for this maintenance session; canceled/skipped coverage remains unverified. See the final CI attribution comment. The original author's readiness declaration below is preserved.

## Author readiness

Human owner: @leeguooooo

- [x] I reviewed the final diff and relevant existing code, understand the change and its edge cases, and take responsibility for the complete PR, including AI-assisted work.
- [x] I checked the evidence above, confirmed it supports the current change, and recorded meaningful validation gaps.
- [x] I declare this PR ready for initial maintainer review.
2026-10-24 03:14:34 +08:00
morluto f36893a1a2 fix(javascript): retain unavailable HTML reference scopes 2026-10-24 03:11:07 +08:00
morluto 6aae8efd35 fix: preserve selected Electron display authorization 2026-10-24 03:11:07 +08:00
morluto e94d5a5c74 fix: retain Electron file admission facts across adapters 2026-10-24 03:11:07 +08:00
morluto 57aa21af26 fix(rizin): retain cancellation and recover cleanup correlation 2026-10-24 03:11:07 +08:00
morluto 3a62b96194 fix(frida): preserve session refusal and close execution effects 2026-10-24 03:11:07 +08:00
morluto 79323128f2 fix(apple): qualify unexamined nib content coverage 2026-10-24 03:11:07 +08:00
morluto 84f4eb42e1 fix(native): preserve unknown Swift extension contexts 2026-10-24 03:11:07 +08:00
morluto 25b28ffe8b fix(hopper): drain retained publishers after current close failure 2026-10-24 03:11:07 +08:00
morluto 27e927f70f fix(inventory): require released cleanup before capability fallback 2026-10-24 03:11:07 +08:00
morluto 48af28bf71 fix(tracing): retain caller reasons at traversal checkpoints 2026-10-24 03:11:07 +08:00
morluto 7685deecf6 test(evidence): cover BOM recovery across bundle commands 2026-10-24 03:11:07 +08:00
morluto 271fecf53a fix(evidence): preserve format-owned JSON recovery diagnostics 2026-10-24 03:11:07 +08:00
morluto 9becdfe8f4 fix(session): retain cleanup blockers and snapshot request identity 2026-10-24 03:11:07 +08:00
morluto e00b2eda0b fix(javascript): preserve recovery acquisition and cancellation failures 2026-10-24 03:11:07 +08:00
morluto cb689fae96 test(package): join owned updater after registry fixture failure 2026-10-24 03:11:07 +08:00
morluto 0326ad95f6 fix(setup): retain proven PATH aliases in installed registrations 2026-10-24 03:11:07 +08:00
morluto cae03361cf fix(reference): separate local filesystem diagnostics from graph commitments 2026-10-24 03:11:07 +08:00
morluto b165b0e830 fix(artifacts): require regular source-map target occurrences 2026-10-24 03:11:07 +08:00
morluto 4d3a7a0589 fix(artifacts): preserve exact ZIP and DMG caller cancellation reasons 2026-10-24 03:11:07 +08:00
morluto 7479f0a464 fix(environment): distinguish destination refusal and write access failures 2026-10-24 03:11:07 +08:00
morluto 62d19a7289 test(browser): resolve network fixture secrets through typed input contract 2026-10-24 03:11:07 +08:00
morluto cbfc2bd9c9 fix: retain unresolved browser secret declarations as input errors 2026-10-24 03:11:07 +08:00
morluto 661aef55a9 fix: retain completed historical replies through finalization 2026-10-24 03:11:07 +08:00
morluto 1c57eed275 fix(apple): retain binary UID identity through archive projection 2026-10-24 03:11:07 +08:00
morluto dbbaf2abb0 fix(apple): preserve asset failures and authored resource coverage 2026-10-24 03:11:07 +08:00
morluto b488a10d96 fix(apple): retain selected dylib identity after path removal 2026-10-24 03:11:07 +08:00
morluto 45abd4d44e fix(ghidra): bind session refusal to retained cleanup owner 2026-10-24 03:11:07 +08:00
morluto 02c5e13cee test(discovery): select the declared GDB console contract 2026-10-24 03:11:07 +08:00
morluto 5ecd7deea7 fix(native): preserve procedure selector coordinate intent 2026-10-24 03:11:06 +08:00
morluto 51612c804f fix(hopper): own bookmark API capability checks 2026-10-24 03:11:06 +08:00
morluto c313f33674 fix(native): share exact-name xref ambiguity resolution 2026-10-24 03:11:06 +08:00
morluto 590bab9ada fix(browser): preserve observed document invalidation and scope recovery 2026-10-24 03:11:06 +08:00
morluto 1275817c75 fix(browser): publish executable examples with truthful capture coverage 2026-10-24 03:11:06 +08:00
morluto b29aa6043f fix(browser): preserve WebMCP optional fields and replay coverage gaps 2026-10-24 03:10:56 +08:00
morluto 209c42c346 fix(browser): keep code absence unknown without worker inventory 2026-10-24 03:10:56 +08:00
morluto b4502be51a test(javascript): preserve exact empty property keys in declarations 2026-10-24 03:10:56 +08:00
morluto e8374f4e58 fix(browser): retain excluded source-map declaration coverage 2026-10-24 03:10:56 +08:00
morluto d56a25e64f fix(sourcemaps): bind cleanup status to remaining codec owners 2026-10-24 03:10:43 +08:00
morluto b5d83d2bff test(javascript): supply source graph to native handoff checks 2026-10-24 03:10:43 +08:00
morluto 80bb0b4f01 test(graph): narrow summary owner and complete PE fixture facts 2026-10-24 03:10:43 +08:00
morluto 693ecec082 fix(artifacts): retain permission omissions through JavaScript lookups 2026-10-24 03:10:43 +08:00
morluto dd55e2c7d5 fix(javascript): retain initialized class self binding at body reads 2026-10-24 03:10:43 +08:00
morluto f1a9d85de5 fix(javascript): preserve per-field publication and provenance coverage 2026-10-24 03:10:43 +08:00
morluto 2206822109 fix(javascript): preserve copied values and export publication lifetimes 2026-10-24 03:10:43 +08:00
morluto 0bbd635ab8 fix(environment): preserve clock years and exact probe chronology
Reuse the shared offset-instant owner for calendar validation and exact probe ordering. Preserve first-century years, arbitrary recorded fraction digits in the supported offset profile, ordinal ties and legacy native-Date fallback. Keep original clock spelling when conversion leaves the supported four-digit year range, and refuse unrepresentable generated clocks before committing state.

Validation: focused clock/comparison and public CLI regressions cover leap years, offset equivalence, precise DELETE/GET order, spelling classification and refusal atomicity. Replay/state compatibility checks pass. Numeric state intervals and generated-clock progression retain their documented millisecond limits.

Fixes #6835
Fixes #6643
2026-10-24 03:06:58 +08:00
morluto 15190ffc49 fix(pe): preserve resource directory paths and source ranges
Retain each resource directory's type/name path and incoming entry ranges before metadata accounting. Empty branches keep their identity, and shared directory bytes retain each distinct incoming path. Validate the expanded facts in the shared service and advertise them through the canonical contract.

Validation: parser, service, filesystem and public CLI/MCP regressions cover numeric/named empty branches, shared directories and unchanged artifact/payload hashes. Controls use authored PE32/PE32+ files; Windows loader behavior remains unverified.

Fixes #6872
2026-10-24 03:06:58 +08:00
morluto 254a8e7018 fix(capture): own HAR schema and response interpretation
Correct the pinned HAR schema's missing object types and duration constraints at the shared decoder boundary. Preserve optional omission and the -1 sentinel without repairing producer measurements, and identify the owned corrections in decoder provenance.

Keep unsupported or redacted response encodings unavailable to replay and state extraction. Empty-response classification now uses the same body owner, preserving known empty content separately from unavailable content. Regenerate contract examples from decoded fixture bytes and the shared view service.

Validation: focused decoder, replay/state, CLI and MCP regressions, including unselected malformed records and confirmed write responses. Contract examples reproduce through MCP. Authored capture controls do not establish real-browser producer coverage.

Fixes #6890
Fixes #6889
2026-10-24 03:06:58 +08:00
gensokyo-dev bded9c628d test(cli): repair Markdown capacity recovery coverage (#6903)
* test(cli): repair Markdown capacity recovery coverage

* test(cli): assert the full Markdown recovery envelope
2026-10-23 19:05:06 +00:00
morluto c8f8ec656c fix(javascript): preserve source facts across graph projections 2026-10-24 03:00:10 +08:00
morluto dac544be20 test(managed): assert large issue retention without full-array diffs 2026-10-24 03:00:10 +08:00
morluto d0375a1ed0 test(filesystem): probe actual permission denial before host assertions 2026-10-24 03:00:10 +08:00
morluto 63370e9300 test(reconstruction): isolate derived surface ownership in fixtures 2026-10-24 03:00:10 +08:00
morluto d93c70c11b fix(browser): retain unknown popup storage initialization coverage 2026-10-24 03:00:10 +08:00
morluto c3dcc69c2d fix(browser): resolve empty storage-world origins from exact frame evidence 2026-10-24 03:00:10 +08:00
morluto 612bbee947 fix(artifacts): retain dylib enumeration readers through failed cleanup 2026-10-24 03:00:09 +08:00
morluto 03ae514069 fix(artifacts): retain DMG carrier observations when child traversal is unavailable 2026-10-24 03:00:09 +08:00
morluto 4990a0b9f1 fix(cli): accept named native API selectors beginning with a dash 2026-10-24 03:00:09 +08:00
morluto 4a24b7e154 fix(evidence): preserve valid output paths near filesystem pathname limits 2026-10-24 03:00:09 +08:00
morluto 5f21aeeeb3 fix(javascript): retain worker storage and projection failures 2026-10-24 03:00:09 +08:00
morluto 88e378063d fix(contracts): preserve safe defaults and client literal guidance 2026-10-24 03:00:09 +08:00
morluto 15450643f7 fix(firmware): preserve admission and extraction coverage outcomes 2026-10-24 03:00:09 +08:00
morluto 0a0f98ca4c docs(skill): keep one setup prerequisite policy 2026-10-24 03:00:09 +08:00
morluto 08e8a568f8 docs(managed): distinguish oracle conformance from host verification 2026-10-24 03:00:09 +08:00
morluto 87c5967c86 fix(setup): distinguish Hopper inspection and archive staging failures 2026-10-24 03:00:09 +08:00
morluto 756701dfe5 fix(setup): retain publication operation failures and recovery facts 2026-10-24 03:00:09 +08:00
morluto 25d7135f60 fix(setup): preserve semantic configuration refusal during uninstall 2026-10-24 03:00:08 +08:00
morluto 9d8ede44d0 fix(kuna): retain setting-specific doctor recovery 2026-10-24 03:00:08 +08:00
morluto ca8b6727c3 fix(cli): reject surplus positional arguments before execution 2026-10-24 03:00:08 +08:00
morluto 9b656dda29 docs(readme): align translated setup and analysis facts 2026-10-24 03:00:08 +08:00
morluto cf5e4f6fa3 fix(javascript): preserve semantic query identity and source coverage 2026-10-24 03:00:08 +08:00
morluto 34552ff0df fix(artifacts): preserve typed resource limit diagnoses 2026-10-24 03:00:08 +08:00
morluto e597e090b0 fix(javascript): share iteration and equality invocation facts 2026-10-24 03:00:08 +08:00
morluto 0729aba6d7 fix: recheck attached worker policy after final artifacts 2026-10-24 03:00:08 +08:00
morluto 98c3d2ff93 test: distinguish corrected port normalization commitment 2026-10-24 03:00:08 +08:00
morluto 2d06992219 test: retain direct hash cleanup retainer contract 2026-10-24 03:00:07 +08:00
morluto b413332bd8 fix(native): preserve literal environment map members 2026-10-24 03:00:07 +08:00
morluto 0f5ece1d58 fix(jeb): retain selected inputs and completed primary replies 2026-10-24 03:00:07 +08:00
morluto 3e112595da fix(ghidra): preserve catalog and metadata admission failures 2026-10-24 03:00:07 +08:00
morluto 324ca9453e fix(hopper): pass private X authority cookie through stdin 2026-10-24 03:00:07 +08:00
morluto 9bea4d10de ci: raise duplication threshold to 4 percent
Set the repository jscpd ceiling to the requested 4%. Preserve the scan
inventory, token/line minima, ignore rules and report failure propagation.

Validation: npm run jscpd passed at 3.59% duplication; scoped format check passed.
2026-10-24 02:59:00 +08:00
morluto 6b56ba2d63 test(javascript): separate map format and row-limit failures
Use a valid canonical embedded map in the decoder row-budget regression.
The old empty map fails version admission before it can reach the row limit.
Retain that malformed-map case separately and assert typed constraint metadata
for format, external-section and capacity failures from the real codec process.

Validation: source-map source suites and all sixteen physical decoder boundary
tests passed. Static, documentation and test-discovery checks passed.
2026-10-24 02:57:12 +08:00
morluto 1a38745295 refactor(ghidra): centralize shared profile commitments
Give raw and header-selected imports one owner for their twelve identical
semantic revision declarations. Preserve property order, parameter values,
omissions and all provider/language/processor override precedence.

Validation: Ghidra behavior and MIPS/RISC-V/PowerPC profile suites passed.
Seventy direct before/after cases retained identical Results, serialization,
parameter order and digests. No real Ghidra process verification was performed.
2026-10-24 02:57:11 +08:00
morluto 21ff4911bf refactor(setup): share publication byte verification
Use one bounded positional byte comparator for client configuration and skill
publication readback. Keep pathname identity, rollback, mode restoration,
cleanup and retry authority in the existing publication owners.

Preserve short-read progress, EOF, empty-file equality and original I/O errors.
Extend existing byte-exact skill recovery coverage to empty originals.

Validation: client configuration transaction, skill publication recovery and
process-global rollback suites passed; static, docs and discovery checks passed.
2026-10-24 02:57:11 +08:00
morluto e21b131f25 fix(apple): classify Interface Builder edges at their source
Pass hierarchy or connection identity from the producing call rather than
searching graph IDs containing caller-selected paths and authored IDs. Keep
connection omissions from making a retained hierarchy appear partial.

Replace mirrored ID-substring test oracles with exact endpoint relationships.
Cover marker-bearing paths and authored identities through domain, CLI/MCP,
summary and byte-retention boundaries.

Validation: affected source and compiled/public tests passed, including native
Foundation/ibtool fixture lanes; one Linux-only invalid-byte filename case
skipped on macOS. Static, docs and discovery checks passed.
2026-10-24 02:56:59 +08:00
morluto 965aff805d fix(javascript): own reference roles and invocation result identities 2026-10-24 02:50:51 +08:00
morluto 990d1de4d0 docs(evidence): define authentication and committed delivery recovery 2026-10-24 02:50:51 +08:00
morluto bd8e1fb2c1 fix(artifacts): publish addressable inspection sources through both adapters 2026-10-24 02:50:51 +08:00
morluto b885f44bef fix(cli): preserve explicit Evidence record pairing in bundle comparison 2026-10-24 02:50:51 +08:00
morluto 546b2a6cf6 fix(evidence): preserve authentication failures and diagnose prior commitments 2026-10-24 02:50:51 +08:00
morluto e713c75329 fix(unknowns): distinguish rejected creation from committed mutation delivery 2026-10-24 02:50:51 +08:00
morluto 9dac74c568 fix(browser): admit legacy capture uncertainty at retained input boundaries 2026-10-24 02:50:51 +08:00
morluto c70f5f3327 fix(hopper): bind ordinary executable opens to selected bytes 2026-10-24 02:50:17 +08:00
morluto f1dfd10bb0 fix(evidence): reject runtime locations for managed static facts 2026-10-24 02:50:17 +08:00
morluto 2f6e0506f0 fix: preserve process filesystem scope and admission failures 2026-10-24 02:50:17 +08:00
morluto 5ee5e49cb4 fix(javascript): bind Next page admission to source context 2026-10-24 02:50:17 +08:00
morluto c20b707680 fix(native): preserve negative Mach-O admission identity 2026-10-24 02:50:17 +08:00
morluto 787726817c fix(javascript): retain private receiver and iterative disposal effects 2026-10-24 02:50:17 +08:00
morluto 0211b65a81 fix(native): narrow capability refusal before retaining its reason 2026-10-24 02:50:16 +08:00
morluto c394fb105d test(native): share Swift context in vtable fixture admission 2026-10-24 02:50:16 +08:00
morluto 19d4a314f8 fix(discovery): retain Frida optional provider failure dispatch 2026-10-24 02:50:16 +08:00
morluto f49e2eeacc fix(environment): retain path segments counters and parent diagnostics 2026-10-24 02:50:16 +08:00
morluto 65e423eba6 fix(browser): compare script and tool facts without metadata identity churn 2026-10-24 02:50:16 +08:00
morluto 9038589e76 fix(apk): classify malformed supported Zip64 records as input failures 2026-10-24 02:50:16 +08:00
morluto 35aa8ef839 fix(artifacts): retain DMG producer failures and bound integrity recovery claims 2026-10-24 02:50:16 +08:00
morluto a2d2e705d3 fix(artifacts): retain caller selection and applicable publication recovery 2026-10-24 02:50:03 +08:00
morluto 549f240210 fix(applications): route bundles by discovered layout and preserve access failures 2026-10-24 02:50:03 +08:00
morluto 9b4deb2655 fix(core): bind numeric type identities and qualify note owners 2026-10-24 02:50:02 +08:00
morluto 1bc27d2378 fix(analysis): preserve snapshot mismatch recovery 2026-10-24 02:45:14 +08:00
morluto f8876e5405 fix(native): attribute deferred Evidence input failures 2026-10-24 02:45:14 +08:00
morluto 2721650884 fix(native): bind signing facts and preserve native output semantics 2026-10-24 02:45:14 +08:00
morluto 2e31d13e07 fix(native): share Swift context and preserve partial protocol facts 2026-10-24 02:45:14 +08:00
morluto 8b93eff452 fix(apple): preserve archive admission and authored UI dispatch coverage 2026-10-24 02:45:14 +08:00
morluto 7fba9919a2 fix(apple): preserve bundle occurrence identity and framework roles 2026-10-24 02:45:14 +08:00
morluto d8e90fe621 fix(nativeaot): retain snapshot digest mismatch and recovery 2026-10-24 02:45:14 +08:00
morluto fc05f68406 fix(setup): purge caches from their producing path owners 2026-10-24 02:45:14 +08:00
morluto adafef2c9e fix(setup): preserve equivalent caller launcher paths 2026-10-24 02:45:14 +08:00
morluto f19aebab76 test(cli): retain schema selection tuple types 2026-10-24 02:45:14 +08:00
morluto 0c4bf68ce8 merge: reconcile concurrent main fixes with issue remediation 2026-10-24 02:45:14 +08:00
morluto 39f362dcee fix(protocols): make admission and certificate success types exact 2026-10-24 02:41:18 +08:00
morluto 3602cc851f fix(inspector): preserve discovery uncertainty and correction paths 2026-10-24 02:41:17 +08:00
morluto e0da397dee fix(cdp): preserve explicitly selected default ports 2026-10-24 02:41:17 +08:00
morluto 6eb9d09268 fix(mcp): enforce initialization and discovery contracts 2026-10-24 02:41:17 +08:00
morluto 4c776d5918 docs: explain native matching summary construction costs 2026-10-24 02:41:17 +08:00
morluto 463c4993b4 test(javascript): bind parameterized workflows to CLI fixtures 2026-10-24 02:41:17 +08:00
morluto fd08a22742 fix(wasm): parse source-phase import glue without evaluation 2026-10-24 02:41:17 +08:00
morluto c3e4c116d5 fix(reference): retain unknown package scope for JavaScript loaders 2026-10-24 02:41:05 +08:00
morluto 97e1b93f13 fix(reference): consume Babel 8 import-type source field 2026-10-24 02:41:05 +08:00
morluto 905ae18451 fix(javascript): retain implicit property conversion and disposal effects 2026-10-24 02:41:05 +08:00
morluto 44efd920c6 fix: retain discriminated Electron action outcomes in projection 2026-10-24 02:41:05 +08:00
morluto ec0259de89 test(ida): remove readiness fixture override after refusal 2026-10-24 02:41:05 +08:00
morluto 9927cd1677 fix(ida): exclude transport secrets from analysis identity 2026-10-24 02:41:04 +08:00
morluto f3bb6b1ed0 fix(discovery): use contract prerequisites and provider effects 2026-10-24 02:41:04 +08:00
morluto 6cfb998af4 fix(managed): accept immutable parsed type selection facts 2026-10-24 02:40:22 +08:00
morluto ebe4e879e8 fix(harmony): retain path-only package and signing observations 2026-10-24 02:40:22 +08:00
morluto 6c4313b93e test(reconstruction): copy readonly citations for matcher input 2026-10-24 02:40:22 +08:00
morluto 4b2a4e8eae fix(evm): preserve reply allowance failures and selected limits 2026-10-24 02:40:21 +08:00
morluto 1ddb32956a fix(reference): keep unresolved root and Git directory reads as failures 2026-10-24 02:40:21 +08:00
morluto bbc75545ec fix(artifacts): preserve source names and describe materialized reader output 2026-10-24 02:40:21 +08:00
morluto 5f2a06f1f8 fix(native): preserve physical plist path selection 2026-10-24 02:40:21 +08:00
morluto 0d849b1ae8 docs: separate retained notices from provider version support 2026-10-24 02:40:21 +08:00
morluto c85e6d9513 refactor(native): retain contained-span mapping fast path 2026-10-24 02:40:21 +08:00
morluto f461146403 fix(native): own contiguous image span mapping across consumers 2026-10-24 02:40:21 +08:00
morluto 2596667baf docs: repair localized anchors and agent terminology 2026-10-24 02:40:21 +08:00
morluto 80bc0f5ede docs: correct setup capture and recovery boundary guidance 2026-10-24 02:40:21 +08:00
morluto 0a1ad0d465 docs(inspector): distinguish passive commands from uninterrupted execution 2026-10-24 02:39:36 +08:00
morluto 7298bd08d1 fix(firmware): retain independent failures at each extraction owner 2026-10-24 02:39:36 +08:00
morluto 115a78fe24 fix(apk): retain raw issuer identity when matching rules are unknown 2026-10-24 02:37:48 +08:00
morluto 1f0eb228e5 fix(managed): derive selected scope from parsed declarations 2026-10-24 02:37:47 +08:00
morluto ba781330d6 fix(browser): separate domain readiness and worker attribution coverage 2026-10-24 02:37:47 +08:00
morluto 91f06f55a3 ci(docs): render documentation in the selected docs lane 2026-10-24 02:37:47 +08:00
morluto 2ebaadfa84 docs(contracts): align examples and guidance with produced evidence 2026-10-24 02:37:47 +08:00
morluto ad629b2e31 fix(pcap): share capture host admission with discovery 2026-10-24 02:36:50 +08:00
morluto b7eededf5a fix(setup): share effective client dialect and alias bindings 2026-10-24 02:36:50 +08:00
morluto bc2d494030 fix(managed): retain declaration kind and runtime implementation facts 2026-10-24 02:36:50 +08:00
morluto 22cf9646a7 docs(skill): route native runtime questions through explicit workflows 2026-10-24 02:36:50 +08:00
morluto 4fb936fe86 fix(prompts): keep process capture preparation nonexecuting 2026-10-24 02:36:00 +08:00
morluto 816a9dc988 fix(electron): block trash calls in active observation hook 2026-10-24 02:36:00 +08:00
morluto d3700f709b fix(evaluation): align scenario prompts and outcome gates 2026-10-24 02:36:00 +08:00
morluto 4e2a566d6c fix: retain cancellation during Electron handle finalization 2026-10-24 02:36:00 +08: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 5eada7e410 refactor: centralize Electron execution limits and action projection 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 1707d171d7 fix: retain Electron window handles through cancelled selection 2026-10-24 02:35:59 +08:00
morluto 5934387f41 fix: attribute rendered frame omissions to their producing stage 2026-10-24 02:35:59 +08:00
morluto a0fcc60d89 fix(browser): budget retained observations independently of wire data 2026-10-24 02:35:59 +08:00
morluto 3f9357b599 fix: report JavaScript worker phases and parent budget recovery 2026-10-24 02:35:59 +08:00
morluto 93de7bc7e3 fix: preserve mixed Swift demangler record outcomes 2026-10-24 02:35:59 +08:00
morluto 99ccfb6e70 fix(ida): preserve readiness and workspace refusal semantics 2026-10-24 02:35:59 +08:00
morluto 171b33b2c4 fix(providers): retain physical refusal and diagnostic facts 2026-10-24 02:35:59 +08:00
morluto e639edf620 fix(reverse): retain output loss after successful command exit 2026-10-24 02:35:59 +08:00
morluto 7784fdbad7 fix(native): retain search row qualifications in feature traces 2026-10-24 02:35:59 +08:00
morluto 4cb1f1d820 fix(pcap): preserve HTTP/2 URI and selected-tool admission facts 2026-10-24 02:35:59 +08:00
morluto 928ba76882 fix(signals): preserve selected file and format semantics 2026-10-24 02:35:58 +08:00
morluto f783f0f1e8 fix(apk): bind certificate facts and signer comparisons to identity 2026-10-24 02:35:58 +08:00
morluto af8f0badf6 fix(wasm): preserve ambiguous identity and partial metadata 2026-10-24 02:35:58 +08:00
morluto b3c3b21e43 fix(protobuf): preserve wire grammar and schema admission facts 2026-10-24 02:35:58 +08:00
morluto 5a7bddb3ae fix(electron): consume shared native loader and export facts 2026-10-24 02:35:51 +08:00
morluto 75daf0c7df fix(javascript): preserve loader classification and exact source targets 2026-10-24 02:35:36 +08:00
morluto baf16784af fix(reference): apply package main to directory requests 2026-10-24 02:35:36 +08:00
morluto f2e5fd148a fix(reference): preserve module syntax and loader request identity 2026-10-24 02:35:36 +08:00
morluto 9974512600 fix(web): select source grammar for module tracing 2026-10-24 02:35:36 +08:00
morluto 38ecc0248e fix(javascript): preserve route flag values and lexical RPC names 2026-10-24 02:35:36 +08:00
morluto ee0ad17a1a test(sourcemaps): verify shared nested map admission 2026-10-24 02:35:36 +08:00
morluto 5f13ddd755 fix(javascript): require API identity for static application roles 2026-10-24 02:35:36 +08:00
morluto 5c16a85a67 fix(reference): bind Git metadata joins to physical directories 2026-10-24 02:35:35 +08:00
morluto b5e43edf7f fix(cli): reject empty desktop paths before resolution 2026-10-24 02:35:35 +08:00
morluto e2eacf6f29 fix(artifacts): sync nested publication and retire admitted file state 2026-10-24 02:35:22 +08:00
morluto ddb3987bb4 fix(reference): project import failures through the canonical error contract 2026-10-24 02:35:22 +08:00
morluto 4345a45982 fix(managed): share source identity and input failure ownership 2026-10-24 02:35:22 +08:00
morluto e76f308d26 fix(evidence): preserve integrity contradiction provenance through comparisons
Artifact comparison owns the inventory Evidence identities whose integrity mismatched. Residual unknowns consume those identities and static/runtime correlation keeps contradictory or unresolved findings separate from observed changes.

Adds a real-ASAR MCP comparison and correlation regression with ordinary-content controls. Validation is pending the centrally coordinated lane; single-file formatting and lint completed.
2026-10-24 02:35:22 +08:00
morluto 7b77db7eff fix(reconstruction): include constituent unknowns in overall function claims
Have the function observation owner select aggregate dimensions before residual-unknown lookup. Retain independent dimension scope, unknown citations and recommended probes through the public MCP verifier.

Validation is pending the centrally coordinated lane; single-file formatting and lint completed.
2026-10-24 02:35:22 +08:00
morluto f5b11131b3 fix(readiness): account for every retained outcome and closure identity
Compare all CLI/MCP repeats within their operation, call kind and expected-success group. Reconcile closure decreases with unique required obligations in the supplied ledger, retaining malformed observations in findings and snapshots.

Validation is pending the centrally coordinated lane; single-file formatting and lint completed.
2026-10-24 02:35:22 +08:00
morluto a35b9335a0 fix(reconstruction): evaluate one effective completion scope
Include required-claim surfaces and reachable dependencies in ownership, verifier commitments, contradiction, package proof and summary checks. Preserve all applicable package outcomes instead of accepting any passing proof.

Adds authenticated synthetic scope cases and a shared public CLI/MCP regression workflow. Validation is pending the centrally coordinated lane; single-file formatting and lint completed.
2026-10-24 02:35:22 +08:00
morluto 8425c36da2 fix(pyinstaller): attribute malformed TOC rows at their owner 2026-10-24 02:35:21 +08:00
morluto f6a1e2c892 fix(gpu): derive legacy bundle extents from codec framing 2026-10-24 02:35:21 +08:00
morluto 03077d54af fix(pe): separate certificate content from counted alignment 2026-10-24 02:35:21 +08:00
morluto 9fb8c114a0 fix(elf): classify compressed note sections as unsupported 2026-10-24 02:35:21 +08:00
morluto 66671e8748 fix(pe): retain resource directory branch identities 2026-10-24 02:35:21 +08:00
morluto c46064c3f6 fix(beam): distinguish empty optional chunk payloads 2026-10-24 02:35:21 +08:00
morluto 06032237c6 fix(python): preserve marshal scalar identity and decode limits 2026-10-24 02:35:21 +08:00
morluto a47788a6a4 fix(sqlite): budget emitted schema names across text encodings 2026-10-24 02:35:21 +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
morluto d1fbebb578 fix(setup): retain selected skill and maintenance uncertainty 2026-10-24 02:29:44 +08:00
morluto fba4faf88a fix(javascript): preserve initializer execution and lexical ownership 2026-10-24 02:29:44 +08:00
morluto 5cbcd58550 fix(javascript): bind bundle exports and mutations to factory roles 2026-10-24 02:29:44 +08:00
morluto 1998640669 fix(reference): scope ignore rules and honor local Git case policy 2026-10-24 02:29:44 +08:00
morluto 3f49545320 fix(cli): preserve canonical input bounds and required choices 2026-10-24 02:29:44 +08:00
morluto 049bc2d055 fix(analysis): preserve operation input correction fields 2026-10-24 02:29:44 +08:00
morluto ce61680c04 perf(nativeaot): index type closure and preserve scan coverage 2026-10-24 02:29:08 +08:00
morluto dba4178465 fix: skip unrelated kernel scans during ELF layout inspection 2026-10-24 02:29:08 +08:00
morluto 06fe7eecbc perf: schedule batch decompilation reads on demand 2026-10-24 02:29:08 +08:00
morluto 23ab2c4069 fix(environment): preserve literal values and recorded comparison chronology 2026-10-24 02:29:08 +08:00
morluto e327baa7b8 fix(browser): validate HAR timing and preserve opaque body encodings 2026-10-24 02:29:08 +08:00
morluto d3305887cb fix: reject PTY environment names the backend cannot preserve 2026-10-24 02:29:08 +08:00
morluto c10a360cab fix: share ordered process text normalization rules 2026-10-24 02:29:08 +08:00
morluto 5cded74b6d fix: bound browser scenario initial navigation during startup 2026-10-24 02:29:08 +08:00
morluto 597aecfb7d fix: bind Electron actions to the selected native window 2026-10-24 02:29:08 +08:00
morluto 75ca7c02ec refactor(adb): encode package verdict certainty in the result type 2026-10-24 02:29:08 +08:00
morluto b31f52946f fix(android): honor selected Java paths and retain input issues 2026-10-24 02:29:08 +08:00
morluto 2aa8d73714 fix(apktool): retain acquisition and launcher failure reasons 2026-10-24 02:29:08 +08:00
morluto 57e3e0b04d fix(adb): preserve numeric intent and typed acquisition outcomes 2026-10-24 02:29:08 +08:00
morluto 8e57b7672b docs(mcp): describe exact IL2CPP names filter coverage 2026-10-24 02:29:08 +08:00
morluto 0f07810f7a test(il2cpp): retain refusal for uncovered registration tails 2026-10-24 02:29:08 +08:00
morluto dab18c1a3f docs(il2cpp): clarify supported views and native slice selection 2026-10-24 02:29:08 +08:00
morluto 8ba739fd3c fix(android): preserve graph occurrence and library identity 2026-10-24 02:29:08 +08:00
morluto 26031bcd26 fix(il2cpp): honor loader pointers and registration field layout 2026-10-24 02:29:08 +08:00
morluto db923eba44 fix(managed): preserve verification input and coverage semantics 2026-10-24 02:29:08 +08:00
morluto c062788b21 fix(ghidra): preserve stable JDK registration paths 2026-10-24 02:29:08 +08:00
morluto 243790adc9 fix(firmware): derive extraction failure from producer severity 2026-10-24 02:29:08 +08:00
morluto a732bd0656 fix(native): distinguish malformed navigation from procedure absence 2026-10-24 02:21:58 +08:00
morluto cebd46d15b fix(native): validate matching observation context at selection 2026-10-24 02:21:58 +08:00
morluto 5e8f8b3163 fix(paths): honor physical caller selection and refuse extraction overlap 2026-10-24 02:21:58 +08: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 <[email protected]>
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 <[email protected]>

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 <[email protected]>
Co-authored-by: morluto <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>

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

Fixes #6482

Co-authored-by: Abelo9996 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>
Co-authored-by: Hifza Zafar <[email protected]>
Co-authored-by: Can <[email protected]>
Co-authored-by: eco-null <[email protected]>
Co-authored-by: Cassie_Fox <[email protected]>
Co-authored-by: Claude Sonnet 5.5 <[email protected]>
Co-authored-by: Wade <[email protected]>
Co-authored-by: RRFZ <[email protected]>
Co-authored-by: pablodiazjorge <[email protected]>
Co-authored-by: Ezra Brilliant <[email protected]>
Co-authored-by: gensokyo-dev <[email protected]>
Co-authored-by: GamerDubz <[email protected]>
Co-authored-by: raidnouredden-ux <[email protected]>
Co-authored-by: 吴佳浩Alben <[email protected]>
Co-authored-by: Abel Yagubyan <[email protected]>
Co-authored-by: Abelo9996 <[email protected]>
Co-authored-by: ShuaixinHuang <[email protected]>
Co-authored-by: Paritosh Lahre <[email protected]>
Co-authored-by: Dery Ferdika <[email protected]>
Co-authored-by: gongfpp <[email protected]>
Co-authored-by: gongfpp <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
Co-authored-by: morluto <[email protected]>
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 <[email protected]>
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 <[email protected]>

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

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
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 <[email protected]>
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 <[email protected]>
2026-10-23 13:02:31 +08:00
Canandeco-null 3e85da9195 fix(screenshot): validate complete PNG IHDR (#6412)
Co-authored-by: eco-null <[email protected]>
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 <[email protected]>
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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
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 <[email protected]>
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 <[email protected]>
Co-authored-by: morluto <[email protected]>
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 <[email protected]>

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

---------

Co-authored-by: Bhaaraadwaj <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: morluto <[email protected]>
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 <[email protected]>
2026-10-23 05:58:16 +08:00
Dayvisonanddayvison2b b5442df02d fix(mcp): describe function comparisons in behavior tool (#5251)
Co-authored-by: dayvison2b <[email protected]>
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 <[email protected]>

* 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 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
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 <[email protected]>
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 <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
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 <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: morluto <[email protected]>
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: 李永祺 <[email protected]>
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 <[email protected]>

* 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 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

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

Co-authored-by: morluto <[email protected]>

* 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 <[email protected]>

* docs: link repository experiments outside the built site

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
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 <[email protected]>

---------

Co-authored-by: morluto <[email protected]>
Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
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 <[email protected]>
Co-authored-by: morluto <[email protected]>
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 <[email protected]>

* 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 <[email protected]>
Co-authored-by: morluto <[email protected]>
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 <[email protected]>
Co-authored-by: Kaoru0822 <[email protected]>
2026-10-23 02:39:05 +08:00
Canandeco-null 536f5ce38b fix(browser): preserve source-map media types (#4032)
Co-authored-by: eco-null <[email protected]>
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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

---------

Co-authored-by: Claude Opus 5 <[email protected]>
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 <[email protected]>
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 <[email protected]>

* 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 <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: morluto <[email protected]>
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 <[email protected]>
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 <[email protected]>
Co-authored-by: morluto <[email protected]>
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 <[email protected]>
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 <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
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 <[email protected]>
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 <[email protected]>

---------

Co-authored-by: Kaoru0822 <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
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 <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
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 <[email protected]>
Co-authored-by: Kaoru0822 <[email protected]>
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 <[email protected]>
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 <[email protected]>

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

Signed-off-by: Vinícius DS Andrade <[email protected]>

---------

Signed-off-by: Vinícius DS Andrade <[email protected]>
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 <[email protected]>
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 <[email protected]>

* 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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

---------

Co-authored-by: Claude Fable 5.1 <[email protected]>
Co-authored-by: morluto <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
Co-authored-by: morluto <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>

* 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 <[email protected]>

* style(pcap): format the capture ceiling test

oxfmt rejected the object literal wrapping in PcapNativeBound.test.ts.

Co-authored-by: morluto <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
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 <[email protected]>

* refactor(errors): drop capability imports superseded by the shared helper

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-23 01:41:25 +08:00
gongfppandgongfpp 77c72b96aa fix(process): propagate comparison contradictions (#6298)
Co-authored-by: gongfpp <[email protected]>
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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
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 <[email protected]>
Co-authored-by: morluto <[email protected]>
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 <[email protected]>
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 <[email protected]>

* 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 <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
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 <[email protected]>

* 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 <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: morluto <[email protected]>
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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

---------

Co-authored-by: Matt Van Horn <[email protected]>
Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>

* 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 <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
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 <[email protected]>
Co-authored-by: deuxdoom <[email protected]>
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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

---------

Co-authored-by: Matt Van Horn <[email protected]>
Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
Co-authored-by: morluto <[email protected]>
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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
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 <[email protected]>

* 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 <[email protected]>

* docs(native): describe portable PE and ELF export inventories

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* 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 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>

* 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 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
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 <[email protected]>

* 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 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

---------

Co-authored-by: Matt Van Horn <[email protected]>
Co-authored-by: morluto <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: Kaoru0822 <[email protected]>
Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>
(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 <[email protected]>

* docs(environment): state how list reads weigh unmodelled writes, added update fields and quoted members

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
Co-authored-by: morluto <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 [email protected] 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 <[email protected]>

* 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 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
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 <[email protected]>

* fix(cli): describe --max-input-bytes in inspect-native-dispatch-metadata help

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* test(native): cover max_input_bytes forwarding through CLI and MCP

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
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 <[email protected]>

* 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 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
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 <[email protected]>
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 <[email protected]>

* test(process): define caller credentials on hosts without geteuid

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
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 <[email protected]>

* 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 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
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 <[email protected]>

* 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 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>

* 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 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
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 <[email protected]>
Co-authored-by: morluto <[email protected]>
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 <[email protected]>

* 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 <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
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 <[email protected]>
Co-authored-by: morluto <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>

---------

Co-authored-by: Kaoru0822 <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* docs(environment): record verification after merging the state view's review fixes

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: Kaoru0822 <[email protected]>
Co-authored-by: gensokyo-dev <[email protected]>
Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* docs(environment): record verification after merging the state view's review fixes

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: Kaoru0822 <[email protected]>
Co-authored-by: gensokyo-dev <[email protected]>
Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
Co-authored-by: morluto <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
Copilot-Session: 35b247e9-c793-4b8f-979a-807c27112cf5

---------

Co-authored-by: Copilot <[email protected]>
Co-authored-by: morluto <[email protected]>
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 <[email protected]>
Copilot-Session: 35b247e9-c793-4b8f-979a-807c27112cf5

---------

Co-authored-by: Copilot <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
2026-10-22 04:08:00 +08:00
keeponlightandkeeponlight 6bc4ec3a74 fix(electron): block moveToApplicationsFolder during active capture (#3471)
Co-authored-by: keeponlight <[email protected]>
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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
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 <[email protected]>

* 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 <[email protected]>

---------

Signed-off-by: Vinícius DS Andrade <[email protected]>
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 <[email protected]>

* 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 <[email protected]>

* test(ida): align analysis profile provider with IDA adapter identity

Signed-off-by: Vinícius DS Andrade <[email protected]>

---------

Signed-off-by: Vinícius DS Andrade <[email protected]>
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 <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
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 <[email protected]>

* docs(go): expose cumulative build-info scan budget

* chore: retain upstream MCP test formatting

---------

Co-authored-by: morluto <[email protected]>
Co-authored-by: Lucien <[email protected]>
Co-authored-by: Kaoru0822 <[email protected]>
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 <[email protected]>
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 <[email protected]>

* 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 <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
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 <[email protected]>
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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
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 <[email protected]>
Co-authored-by: morluto <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>

* 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 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
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 <[email protected]>

* 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 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
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 <[email protected]>

* 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 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>

* 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 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
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 <[email protected]>
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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* test(javascript): guard optional call paths in closure handler test

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
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 <[email protected]>

* 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 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>

* 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 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* style: format worker-reuse close and launch helpers

Co-authored-by: morluto <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
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 <[email protected]>
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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* docs(specs): record the prefix-variance review round

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* 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 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
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 <[email protected]>
Co-authored-by: morluto <[email protected]>
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 <[email protected]>
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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>

* style(hopper): format launcher cancellation return

oxfmt rejects the wrapped abort return in HopperApplicationLauncher.

Co-authored-by: morluto <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
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 <[email protected]>
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 <[email protected]>

* style(test): format the warm CLI session

oxfmt --check rejected the session and its worker.

Co-authored-by: morluto <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
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 <[email protected]>

* 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 <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
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 <[email protected]>

* 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 <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
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 <[email protected]>
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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* style(doctor): format the Hopper timeout regression

Co-authored-by: morluto <[email protected]>

* 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 <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* style: format the turbo cache policy test

Co-authored-by: morluto <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
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
6367 changed files with 1044773 additions and 228139 deletions

No files matched your search

+21
View File
@@ -0,0 +1,21 @@
{
"name": "rea",
"interface": {
"displayName": "REA"
},
"plugins": [
{
"name": "rea",
"source": {
"source": "npm",
"package": "rea-agents",
"version": "6.8.0"
},
"policy": {
"installation": "AVAILABLE",
"authentication": "ON_INSTALL"
},
"category": "Developer Tools"
}
]
}
@@ -15,6 +15,11 @@ Follow the checkout's [performance priorities](../../../AGENTS.md#performance),
workflow, not additional permission to execute targets, publish, or merge.
Use the current contracts, provider prerequisites, and user authorization.
Reserve performance measurements for changes that target performance or have a
concrete regression concern. Routine cleanup uses correctness checks and does
not require this optimization workflow or synthetic benchmarks merely because
helper calls, imports, or representations change.
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.
+4 -3
View File
@@ -68,9 +68,10 @@ closing-issue reporters. Grouping preserves all those credits in one line.
When Highlights, detailed entries or migrations reference the same work,
put its thanks in one of those entries, not all of them. Include issue links.
Direct-commit bullets carry the commit link and known human credit.
Exclude GitHub bots and known agent accounts. REA's observed Anthropic, Cursor
and Codex co-author trailers resolve to `@claude`, `@cursoragent` and `@codex`
with GitHub type `User`; the credit policy excludes them while retaining their metadata.
Exclude GitHub bots and known agent accounts. REA's observed Anthropic, Cursor,
Codex, and Copilot co-author trailers resolve to `@claude`, `@cursoragent`,
`@codex`, and `@Copilot` with GitHub type `User`; the credit policy excludes
them while retaining their metadata.
At the REA maintainer's request, omit `@morluto` self-thanks while preserving
authorship in the inventory. Other human contributors remain eligible.
Use explicit GitHub links for cross-repository references to avoid ambiguous
+17 -1
View File
@@ -1,6 +1,6 @@
---
name: rea-tool-design
description: Design or change REA investigation tools, CLI/MCP contracts, provider capabilities, and Evidence semantics.
description: Audit, investigate, design, or change REA investigation tools, CLI/MCP contracts, provider capabilities, and Evidence semantics.
---
# REA tool design
@@ -10,6 +10,22 @@ contract semantics, provider ownership, and discoverability. Inspect the current
contract and nearest existing tool relevant to the analyst question; source,
documentation, and live catalogs may describe different versions.
For provider-backed work, exercise the analyst's task through REA and compare
relevant native provider and independent artifact observations where available.
Check applicable partial results, recovery and session boundaries; report what
was not exercised. For failure guidance, follow [design recovery around the
failure](../../../docs/tool-design.md#design-recovery-around-the-failure).
Use [workflow verification](../../../docs/testing.md#provider-backed-workflow-verification)
to select evidence for the proposed contract instead of seeking provider API
parity.
For an audit, follow [audit existing tools](../../../docs/tool-design.md#audit-existing-tools):
scope representative tasks, exercise the live workflow, and distinguish
reproduced bugs, supported limitations, design opportunities and unverified
claims. Check relevant rendered MCP prompts, guided workflows and packaged skill
advice against the live contracts and exercise their suggested handoffs. Report
working alternatives and evidence limits as well as findings.
For a design request, return the proposed boundary, input/result shape, Evidence
semantics, nearest alternative, and verification approach. Implement when
requested, preserving the user's scope and existing authorization.
+224 -55
View File
@@ -1,14 +1,14 @@
---
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.
description: Reverse engineer native, managed, Electron/JavaScript, packaged, firmware, and browser targets with REA, and build agent environments (AgentEnv bundles) of web applications from a live instance or a capture. 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: "44"
version: "60"
---
# REA
Use REA when a claim depends on a shipped binary or package, decompilation,
passive application runtime evidence, or comparison with
requested runtime evidence, or comparison with
behavior not established by available source. For ordinary analysis of a
complete source repository, use normal repository tools and do not run REA
readiness or provider commands.
@@ -17,6 +17,9 @@ readiness or provider commands.
Installing this skill supplies instructions; it does not register the REA MCP
server, install analysis engines, or add tools to an already-running agent.
The exception is a Claude Code or Codex plugin install: the plugin bundles
this skill and registers its own `rea` server, outside the state that
`rea setup` and `rea doctor` manage.
If REA tools are available and their registration is not known to be stale,
proceed directly to the target. Do not run diagnostics or setup before every
@@ -32,39 +35,28 @@ 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. For plugin installs, follow
[plugin recovery](references/connection-and-recovery.md#rea-installed-as-a-client-plugin).
For setup-owned registrations, 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
`npx -y rea-agents@latest doctor --client codex --json`. Substitute the current
supported client: `claude_code`, `claude_desktop`, `codex`, `cursor`,
`gemini_cli`, `windsurf`, `devin`, `opencode`, `antigravity`, `copilot_cli`,
`commandcode`, `vscode`, `grok_build`, `omp`, or `grok_bot`. `grok_bot` has no local
file registration: doctor reports the Grok Bot chat step, and setup does not
write `mcp.json`. If the client is unknown, use `doctor --json` and
inspect its registration results before choosing a setup scope.
2. Distinguish the reason. Missing, malformed, or stale registration needs a
scoped configuration repair. An aligned registration with no tools in the
active session needs a restart/reconnection; doctor cannot prove that the
current agent has connected. A missing provider affects only tasks requiring
that provider: static JavaScript inspection needs neither Hopper nor Ghidra,
and Android inspection has separate bring-your-own JADX/Java prerequisites.
3. For a configuration repair, prepare the read-only plan:
`npx -y rea-agents@latest setup --client codex --dry-run --json`.
Use the current client's ID, show its exact proposed paths, backups, and
changes, and obtain approval before setup writes configuration or installs
Hopper. Setup normally installs the matching bundled skill too; include that
replacement in the reviewed plan. After approval, apply the same scope with
`npx -y rea-agents@latest setup --client codex --yes`. Add `--install-hopper`
only if that separate installation was needed and explicitly approved.
4. Restart/reconnect the affected agent. Verify that REA tools actually appear
in the session, then resume the original investigation. If they remain
absent, inspect the client's MCP launch error rather than repeating setup.
Choose the installation owner before repairing registration. Plugin installs use
[plugin recovery](references/connection-and-recovery.md#rea-installed-as-a-client-plugin);
only setup-owned registrations use the guide's
[registration repair plan](references/connection-and-recovery.md#repair-registration-when-needed).
A plugin-only doctor result of `missing` does not call for `rea setup`.
REA setup never installs or upgrades Node.js, npm, Homebrew, Java, Ghidra, IDA,
JADX, Kuna, Binwalk, or Unblob. Use existing prerequisites; do not install unrelated
software to repair
MCP registration. For an unsupported client, use manual stdio registration
with a version-pinned `rea-agents` package or continue through the CLI.
software to repair MCP registration. For an unsupported client, use manual stdio
registration with an existing installation’s absolute runtime and entry script, or continue through the CLI.
A concrete CLI fallback for an operator-supplied JavaScript tree or ASAR is:
@@ -77,11 +69,13 @@ graph, limitations, and unknowns with the same care as an MCP result; the CLI
returns the Evidence record directly. This fallback does not establish that
MCP is configured. Native CLI tasks still require their selected engine.
Prefer an existing `rea` CLI. When none is installed and REA package acquisition
is within the authorized scope, use `npx -y rea-agents@latest` in its place.
Configuration repair starts with a read-only setup plan; show paths, backups and
is within the authorized scope, use `npx -y rea-agents@latest` in its place from a
trusted directory outside the target project, with absolute target paths. Managed
setup binds the current installation directly; retain its runtime and package
cache entry, or restore that installation before repairing its registration.
For setup-owned registration, configuration repair starts with a read-only setup plan; show paths, backups and
skill replacement before applying authorized changes. Preserve existing approval
and target-execution boundaries. REA setup never installs or upgrades unrelated
software, including Node.js, npm, Homebrew, Java, Ghidra, IDA, JADX, Kuna, Binwalk or Unblob.
and target-execution boundaries.
## Route the target first
@@ -91,9 +85,22 @@ 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).
- Bun `--compile` executable: `open_binary`, then `inspect_artifact`; the Bun
module graph is the application layer. Extract stored modules and analyze the
JavaScript. See [Bun standalone executables](https://github.com/morluto/rea/blob/main/docs/bun-standalone.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.
@@ -102,21 +109,47 @@ explicit path or endpoint and do not need it.
`extract_firmware` when extraction is requested, with a caller-selected new
absolute output directory. These tools use caller-supplied Binwalk/Unblob on
Linux; see the [firmware guide](https://github.com/morluto/rea/blob/main/docs/firmware-analysis.md).
- ESP32-family app or bootloader image (`0xE9` magic): `inspect_artifact`
without a provider; see the ESP section of `references/native-and-artifacts.md`.
- .NET NativeAOT PE/ELF: native Ghidra analysis. Native code can yield pseudocode;
consult the NativeAOT workflow in `references/native-and-artifacts.md` for
optional metadata recovery and its supported host/layout boundary.
- Native program behavior, live calls, instrumentation, or command-run effects:
choose the matching [native runtime workflow](references/runtime-observation.md#native-runtime-questions).
`open_binary` and `trace_native_values` establish static facts; they do not
observe execution. Runtime tools have distinct launch, attach, mutation and
cleanup effects, and do not require a preliminary static session unless their
own contract says so.
- User-owned browser page already open: `list_browser_targets`.
- Passive traffic capture of a SPA: the default scope is the page's own origin,
and data APIs usually live on another origin. Discover API origins from
absolute URLs in loaded bundles or script text via JavaScript analysis tools
(another passive capture without those origins still scopes to the page
origin, and `completeness.excluded` counts lack URLs). Pass both the page
origin and those API origins in `allowed_origins` — the list replaces the
default scope — and read `completeness.excluded` before reporting that an
API is absent. For long captures with body shapes, use
`export_evidence_bundle` instead of expecting an inline result.
- Browser action the agent must perform (click, type, navigate):
`capture_browser_scenario` with declared actions. See
[runtime observation](references/runtime-observation.md) for connect/launch
and the focused follow-up browser tools.
- Agent environment (AgentEnv bundle) of a web application: read
[environments](references/environments.md) before recording; it records with
`capture_browser_scenario` and writes the bundle with `export_environment_bundle`.
- Task for a desktop application's exported environment, from a
`capture-desktop-application` folder: read
[desktop application tasks](references/environments.md#tasks-for-a-desktop-application-environment).
- Fiddler Everywhere traffic or a native `.saz` capture: read the
[Fiddler workflow](references/runtime-observation.md#fiddler-everywhere-traffic-debugging).
Inspect saved SAZ with `inspect_web_network_capture` and `format: "saz"`;
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
@@ -129,7 +162,9 @@ explicit path or endpoint and do not need it.
and field names/types before choosing fields for `inspect_packet_capture`.
Query/page the relevant records; registrations do not establish observed traffic.
- Recorded packet captures: `inspect_packet_capture` with selected TShark fields,
display filters and decode-as rules. Requires Linux, util-linux prlimit and `REA_TSHARK_COMMAND`; see
display filters and decode-as rules. Requires Linux, util-linux prlimit and
TShark 4.x on an absolute `PATH` entry or via the optional absolute
`REA_TSHARK_COMMAND` override; see
[capture guide](https://github.com/morluto/rea/blob/main/docs/packet-captures.md).
Packet limits count input packets before filtering. Output defaults to 50 matching
frames; use `match_offset` / `match_limit` for later pages, or explicit null
@@ -156,8 +191,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
@@ -171,6 +216,8 @@ 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
@@ -197,8 +244,86 @@ explicit path or endpoint and do not need it.
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).
- PE import and delay-load tables: `inspect_pe_imports` with the explicit
local PE32/PE32+ file. This built-in target-free inspection requires no
Windows SDK. Preserve DLL names, named and ordinal functions, ILT/IAT RVAs,
delay-load descriptors, and the pefile import hash of the standard import
table. A missing import directory is observed absence of that table, not
proof the image never imported APIs through another table. Bound snapshots
and loaded IAT contents remain unobserved. See the
[PE import guide](https://github.com/morluto/rea/blob/main/docs/pe-imports.md).
- PE TLS callbacks and load configuration: `inspect_pe_load_config` with the
explicit local PE32/PE32+ file. This built-in target-free inspection requires
no Windows SDK. Preserve callback VAs as strings with their RVA, file offset
and section, and the size-covered security cookie, SafeSEH, CFG,
EH-continuation and XFG fields. Guard-flag mitigation claims are inferred
linker declarations, not proof of enforcement. A missing directory is observed
absence of that declaration. See the
[PE load configuration guide](https://github.com/morluto/rea/blob/main/docs/pe-load-config.md).
- ELF, PE, and Mach-O hardening: `inspect_binary_hardening` with the explicit
local file. This built-in target-free inspection requires no pwntools or other
host tool. One image per Mach-O slice. Each property is `enabled`, `disabled`,
`partial`, `unknown`, or `not_applicable`, with the header field or import
behind it. A missing stack-protector or fortify import is `unknown`, not
proof the protection was omitted. Authenticode and Mach-O code signatures
report presence, not validity. Hardened runtime stays unknown and names
`inspect_signature`. The image is not executed. See the
[binary hardening guide](https://github.com/morluto/rea/blob/main/docs/binary-hardening.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).
- Apple `.ips` crash reports: `inspect_apple_ips` with the explicit local
file. This built-in target-free inspection requires no Xcode, atos, or
pwntools. Preserve first-line metadata, `bug_type` as a string, process and
exception facts, lossless integer registers, usedImages UUIDs, and
documented classification matches. Only `bug_type` `"309"` yields a crash
body; other types, including `"288"` stackshots, return no crash result.
Opened Mach-O and dSYM UUID joins remain unobserved. See the
[Apple IPS guide](https://github.com/morluto/rea/blob/main/docs/apple-ips.md).
- Linux kernel module metadata: `inspect_kernel_module` with the explicit local
ELF32/ELF64 relocatable file. This built-in target-free inspection requires
no Linux host or `modinfo`. Preserve `.modinfo` key order and duplicate keys,
`__versions` CRC hex, section file ranges, and the appended signature
trailer's PKCS#7 SHA-256. A missing section is observed absence when section
names are known, not proof the module never carried that metadata.
`struct module` is not decoded. The signature is not validated. gzip, xz, and
zstd containers are unsupported; decompress into a separate file with an
external codec utility, then inspect the decompressed `ET_REL` image. See the
[kernel module guide](https://github.com/morluto/rea/blob/main/docs/kernel-modules.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. For selected facts, prefer `detail: "summary"` plus a
`beam` view (collection, names/indices, fields and paging), retaining the full
Evidence. CLI summary requires `--evidence_output`. Missing table counts stay
null; decoded empty tables count zero. 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
@@ -221,6 +346,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.
@@ -232,6 +359,11 @@ leaves its database open. The modern headless profile creates and closes an
owned private database without saving. Installation and registration reuse
[mrexodia/ida-pro-mcp](https://github.com/mrexodia/ida-pro-mcp); see the
[IDA provider guide](https://github.com/morluto/rea/blob/main/docs/ida-provider.md).
For migration to the separate native IDA MCP plugin, follow the guide's
[legacy attached migration](https://github.com/morluto/rea/blob/main/docs/ida-provider.md#migrate-a-legacy-attached-registration).
Replace the upstream registration explicitly; modern mrexodia tools are not the
native profile. Native attachment preserves the database but does not provide
direct callers; check session availability and the guide's verification limits.
Proceed directly to `analyze_function`, pseudocode, or function/string searches
after opening; IDA does not supply `binary_overview`. Consult session availability
when composing broader workflows. Modern direct callers and unsupported dossier
@@ -262,18 +394,28 @@ native target whose analysis tool operates on the active target. Do not call an
unadvertised tool; inspect `binary_session` with `{}` and its
`result.tool_availability` for availability reasons and remediation. Expand each
group's `names` into entries, inheriting the inventory-level client requirements
unless the group overrides them. In its closed effect and annotation vocabularies,
listed flags are true and omitted known flags are false. The grouped name union
is the complete tool inventory; use `tools/list` for canonical tool schemas.
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.
unless the group overrides them. The grouped name union is the complete tool
inventory; use `tools/list` for canonical tool schemas. The default summary keeps
each provider candidate's availability and target-support status, code and
reason; `omitted_fields` lists what it leaves out. Request `{"detail": "complete"}`
only when you need an unavailable candidate's remediation, provider
diagnostics, candidate capability sets, or tool surface, effect and annotation
flags; in those closed flag vocabularies, listed
flags are true and omitted known flags are false. 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 editable Ghidra analysis that must survive a later agent process, select
the [owned Program checkpoint workflow](references/native-and-artifacts.md#continue-editable-ghidra-analysis-across-processes)
before making metadata edits. It is separate from Evidence/Unknown persistence.
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.
330 seconds can be changed for new startup attempts through
`update_analysis_settings` using `ghidra.startup_timeout_ms`. Changing the
server's launch environment instead requires a new REA process.
## Work summary-first
@@ -281,7 +423,22 @@ Start with the default result and use its inline Evidence and graph context.
For a JavaScript application or ELF layout whose size is unknown or large,
call `analyze_javascript_application` or `inspect_binary_layout` with
`"detail": "summary"`, then inspect the retained `parent_evidence_id` with
`inspect_analysis_view`.
`inspect_analysis_view`. Over MCP, `analyze_function` is summary-first without
a flag: its default reply is the summary, and the complete dossier stays
retained under `parent_evidence_id` for facet paging. Pass that ID as a
`retained-evidence` reference to `compare_functions`, or inside
`match_native_functions` `left_observations`/`right_observations` or
`build_call_path.functions`; request `"detail": "complete"` only when the
dossier must travel inline.
Use the same exact-reference form in other advertised logical Evidence inputs,
including optional sources, arrays and nested sources across domains. Each
source can independently be inline or retained; preserve its source identity,
coverage, partial facts and unknowns. A summary or inspection page has its own
Evidence ID: use `normalized_result.parent_evidence_id` when the consumer needs
the complete source. For portable CLI follow-ups, commands advertising
`--evidence-bundle` resolve references from that selected exported bundle.
See [Evidence composition](references/evidence-workflows.md) for scope and
recovery; file selectors are supported only where explicitly advertised.
Page module identities and select the returned `node_id` before requesting an
item; a single file path can represent several graph roles. Do not repeat an
identical analysis or rejected call without a changed input or resolved blocker.
@@ -300,7 +457,10 @@ output also streams without a single MCP frame.
When the retained parent ID is unknown, use `get_evidence_bundle` with
`detail: "summary"` and exact filters for operation, target SHA-256,
analysis-profile digest or canonical procedure address. Select its returned
Evidence ID for the next view instead of requesting the complete bundle.
Evidence ID for the next view instead of requesting the complete bundle. When
that record has no supported view (for example an annotation), read it with
`detail: "records"` and an `evidence_id` filter; `missing_evidence_links`
names linked sources this session does not retain.
`retention: "complete-record"` means the whole record is stored; native
value-flow truncation and unknown metadata remain separate. For an exported
portable bundle, use `rea inspect-evidence-bundle` with JSON containing `path`,
@@ -369,15 +529,24 @@ as complete while required questions remain open.
[references/javascript-applications.md](references/javascript-applications.md)
- Android APK declarations, classes, methods, and static references:
[references/android-applications.md](references/android-applications.md)
- Browser/Electron observation (passive and scenario) and static/runtime
reconciliation:
- Native instrumentation, debugger and process capture; browser/Electron
observation; static/runtime comparison:
[references/runtime-observation.md](references/runtime-observation.md)
- Evidence paging, comparisons, residual unknowns, and verification:
[references/evidence-workflows.md](references/evidence-workflows.md)
- Agent environments (AgentEnv bundles) of a web application, recorded from
a live instance or a HAR:
[references/environments.md](references/environments.md)
- Tasks for a desktop application's exported environment:
[references/environments.md](references/environments.md#tasks-for-a-desktop-application-environment)
## Finish the task
Explain findings in plain language and tie them to returned evidence. When the
user asks to build something, use normal coding tools and separate observed
behavior from design choices. Close an opened native session with
`close_binary` when the investigation is complete.
`close_binary` when its engine is no longer needed. Evidence references and Unknown
history remain in memory on the same MCP connection. End the investigation with
`reset_investigation`; supply `save_path` to checkpoint the complete bundle before
clearing. Import the saved bundle to restore original references and Unknown
history in a new process. Retention is bounded and is not automatic persistence.
@@ -13,11 +13,34 @@ own APK path and explicit selectors; do not open a native session first.
Choose an `overload_index` from the class inventory when methods are ambiguous
instead of guessing.
For JNI implementations, discover `inspect_android_jni_bindings`. Select raw
DEX class/method identities and optional descriptors, ABI or logical library
paths. Prefer `detail: "summary"`; complete Evidence remains retained and
`android-jni` views page declarations, libraries, candidates or binding rows
with exact fields. Global native-pair ambiguity survives class selection.
Preserve slot/relocation provenance and source coverage: matched means observed
candidate presence; partial scans do not establish uniqueness or absence.
This reads DEX/ELF bytes without loading the APK into the decompiler or executing
target code. Runtime registration and loading remain unknown. See the
[JNI guide](https://github.com/morluto/rea/blob/main/docs/android-analysis.md#link-native-declarations-to-jni-candidates).
For an R8/ProGuard release build with its `mapping.txt`, pass the absolute
`mapping_path` to `inspect_android_class` or `inspect_android_method`. Prefer
`detail: "summary"` to receive normalized symbols or the selected body without
duplicate raw class metadata. Complete Evidence stays retained under the linked
parent ID and can be exported; CLI summary delivery requires `--evidence_output`.
Omitted or complete detail preserves the full inline response. Keep
selecting by observed names; the separate `mapping` block reports each symbol
as `mapped`, `ambiguous` or `unmapped` after checking DEX types and prototypes.
REA does not verify that the mapping belongs to the APK build.
The provider requires a separately supplied headless JADX MCP JAR 0.7.1 and
Java 17 or newer. `REA_JADX_MCP_JAR` selects the absolute JAR path; `JAVA_HOME`
can select Java. REA setup does not install or configure that JAR, and doctor
provider scopes cover Hopper/Ghidra rather than JADX. Use existing tools and
report missing prerequisites for the Android task specifically.
can select Java. Use `rea doctor --provider jadx --json` to check the JADX
configuration and obtain remediation for missing prerequisites. Java and the
headless JADX MCP JAR remain caller-supplied; REA setup does not install or
configure them. Configuration readiness does not establish successful analysis
of a particular APK.
Linux and macOS use the POSIX owned-process boundary. Windows x64 requires the
matching bundled native controls and uses an owned Job Object with bidirectional
@@ -25,8 +48,12 @@ 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 and `REA_JADX_OPERATION_TIMEOUT_MS`
the per-operation deadline (default 120000 ms; the MCP client's tool timeout
must also allow it). The APK is never launched,
the per-operation deadline (integer milliseconds from 1 to 2147483647, default
120000). To change that deadline on a running MCP server, set
`jadx.operation_timeout_ms` through `update_analysis_settings`, then retry after
work and cleanup settle. Newly accepted operations use the override; accepted
work keeps its budget. Changing the launch environment requires a new process.
The MCP client's tool timeout must also allow the selected deadline. The APK is never launched,
and no emulator is required.
Manifest entries and decompiled text are static observations, not execution or
@@ -7,18 +7,23 @@ operation and the next useful step; after repair, continue the investigation.
Commands below use an installed `rea`. If none is available, use the package
invocation in `SKILL.md` when acquiring REA is authorized. Follow the connected
server's schemas and the installed CLI's help rather than assuming repository
main and an older release expose the same arguments.
main and an older release expose the same arguments. Tool-name prefixes belong
to the client: find the canonical REA operation in its actual tool listing and
server association before concluding that it is absent. A shared prefix alone
does not establish which server an approval wildcard covers. See
[client tool names and discovery](https://github.com/morluto/rea/blob/main/docs/client-configuration.md#client-tool-names-and-discovery).
## 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 | If REA was installed as a client plugin, follow the plugin section below. Otherwise 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. Request `{"detail": "complete"}` only for provider diagnostics. |
| `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
@@ -56,8 +61,13 @@ 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. Raising only the
client deadline does not change this server budget. For static JavaScript,
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
@@ -75,6 +85,63 @@ 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.
## REA installed as a client plugin
A Claude Code or Codex plugin owns its `rea` server and this skill. Doctor's
`--client` and `--skill` checks and `rea setup` read and write only
setup-owned state, so for a plugin-only install they report `missing`, and
applying setup would add a second `rea` server. If this skill was loaded from
a plugin, or the client lists a `rea` plugin, do not prepare a setup plan for
missing tools. Instead:
- Check that the plugin is installed and enabled with the client's own
listing: `claude plugin list` and `/mcp` in Claude Code, `codex plugin list`
in Codex.
- Restart or reconnect the client, then read its launch error for the `rea`
server if tools are still absent. The plugin starts its bundled runtime; a missing build or version mismatch
requires a reinstall, or a fresh build when developing from source.
- Check analysis engines through the connected server's
`binary_session.result.tool_availability`. `rea doctor --provider <id> --json`
from a shell reports that shell's environment, which can differ from the
environment the client gave the plugin server. If you still run it through
the package runner, use the version pinned in the plugin manifest's server
entry (`rea-agents@<version>`), not `@latest`, so it matches the server the
plugin starts.
Use the setup repair below only when the user wants a setup-owned registration
instead of the plugin, and remove or disable the plugin first.
## Repair registration when needed
For Codex:
@@ -87,9 +154,12 @@ rea setup --client codex --dry-run --json
Use the current supported client's ID: `claude_code`, `claude_desktop`, `codex`,
`cursor`, `gemini_cli`, `windsurf`, `devin`, `opencode`, `antigravity`,
`copilot_cli`, `commandcode`, `qwen_code`, `vscode`, `trae`, `grok_build`, `omp`, `pi`,
`hermes`, `qoder`, `droid`, `zcode`, `dsh`, or `grok_bot`. If unknown, inspect `doctor --json` before choosing a
`hermes`, `qoder`, `droid`, `zcode`, `dsh`, `freebuff`, or `grok_bot`. If unknown, inspect `doctor --json` before choosing a
scope. `grok_bot` uses a chat registration step rather than local `mcp.json`.
For unsupported clients, use version-pinned manual stdio registration or CLI.
For unsupported clients, use manual stdio registration with the selected
installation’s absolute runtime and entry script, or continue through the CLI.
Acquire packages from a trusted directory outside the target project. Keep a
package-runner installation and its runtime available while registrations use it.
Show the plan's exact paths, backups, changes and bundled-skill replacement.
Obtain approval for writes when that concrete scope is not already authorized,
@@ -134,6 +204,17 @@ writing. Managed policy can constrain client settings. Consult the
for the installed client's supported settings. This is an optional recovery
choice, not a prerequisite for every REA task.
## Repair an editable REA limit
If `details.limit` names an editable setting and its bounds, call
`update_analysis_settings` directly with a `changes` entry containing `setting`,
`action: "set"` and `value`. `inspect_analysis_settings` is optional when those
facts are already present. Retry the same input after work and cleanup settle.
Runtime defaults affect new work only; explicit per-call arguments take
precedence and must be corrected directly when selected. Reset restores launch
values. Updates survive target close/open but disappear on REA restart. They do
not edit host deadlines, response buffers, registrations or fixed limits.
## Continue through an installed CLI
Check the command environment only if the launcher fails:
@@ -0,0 +1,376 @@
# Agent environments from a live application
Use this when the user asks for an agent environment (an AgentEnv bundle) of a
web application: a folder `agent-env` deploys, whose MCP tools answer from what
one recording of the application observed and serve the writes that recording
confirmed. The workflow records the application once with
`capture_browser_scenario`, checks what the environment would serve, records
again if a hint names a gap, then writes the bundle with
`export_environment_bundle`. For a HAR the user already has, skip to
[export](#export-and-hand-off) through `inspect_web_network_capture`, or use
`rea environment-build <capture.har> <dir>`. For a task in a desktop
application's exported environment, read
[its section](#tasks-for-a-desktop-application-environment).
## Before recording
- Record only against an instance the user says is disposable or seeded for
this purpose: every write step creates, edits or deletes its data. If the
user has not said so, ask before any scenario that writes. REA does not
start, seed or reset applications.
- Credentials come from environment variables the user names. Declare each as a
scenario secret, `{secret_id, environment_variable}`; the variable must be set
in the environment REA's MCP server started with. Never ask for a password in
chat. Declared secrets are redacted as the capture records.
- Map the application first with `inspect_web_page` (and `capture_web_screenshot`
when layout matters): the login form's locators, and the API calls behind the
pages that list and edit the entities the user asked about.
- `inspect_web_page` cannot sign in. To see pages behind sign-in, run a short
`capture_browser_scenario` that signs in and opens them, with
`"capture": {"after_each_step": ["accessibility"]}` (`"dom"` for markup).
Read a step's tree with `inspect_analysis_view`, `view: {kind:
"browser-scenario", collection: "text", step_index, artifact:
"accessibility", offset: 0, limit: 2048}`, paging with `offset`. Capture
options are only `after_each_step`, `at_end`, `events`, `network` and
`limits`.
## Record a session an environment can learn writes from
One `capture_browser_scenario` records the whole session. Start from this
shape, with the application's own locators, paths and headers:
```json
{
"browser": { "mode": "launch", "executable_path": "/usr/bin/chromium" },
"start_url": { "url": "http://127.0.0.1:3000/login" },
"secrets": [
{ "secret_id": "admin_password", "environment_variable": "APP_PASSWORD" }
],
"capture": {
"network": {
"request_body": true,
"response_body": true,
"header_values": true
}
},
"actions": [
{
"step_id": "user",
"action": "fill",
"timeout_ms": 15000,
"locator": { "kind": "css", "selector": "input[name=username]" },
"value": { "source": "literal", "value": "admin" }
},
{
"step_id": "password",
"action": "fill",
"timeout_ms": 15000,
"locator": { "kind": "css", "selector": "input[name=password]" },
"value": { "source": "secret", "secret_id": "admin_password" }
},
{
"step_id": "sign-in",
"action": "press",
"timeout_ms": 15000,
"locator": { "kind": "css", "selector": "input[name=password]" },
"key": "Enter"
},
{
"step_id": "signed-in",
"action": "wait_for",
"timeout_ms": 15000,
"locator": { "kind": "css", "selector": "nav a[href='/settings']" },
"state": "visible"
},
{
"step_id": "folders-1",
"action": "request",
"timeout_ms": 15000,
"method": "GET",
"path": "/api/folders",
"expected_status": [200]
},
{
"step_id": "new-folder-1",
"action": "click",
"timeout_ms": 15000,
"locator": { "kind": "role", "role": "button", "name": "New folder" }
},
{
"step_id": "folder-title-1",
"action": "fill",
"timeout_ms": 15000,
"locator": { "kind": "css", "selector": "input[name=title]" },
"value": { "source": "literal", "value": "Scratch folder 1" }
},
{
"step_id": "create-folder-1",
"action": "click",
"timeout_ms": 15000,
"locator": { "kind": "role", "role": "button", "name": "Create" },
"save_response": {
"method": "POST",
"path": "/api/folders",
"save": [{ "name": "folder_1", "json_pointer": "/id" }]
}
},
{
"step_id": "read-folder-1",
"action": "request",
"timeout_ms": 15000,
"method": "GET",
"path": "/api/folders/{saved:folder_1}",
"expected_status": [200]
}
]
}
```
Give each step that accepts `timeout_ms` a deadline. Without one, a step waits
until it completes or the scenario is cancelled, so a locator that never
matches hangs the capture and the client with it. The `wait_for_timeout` pause
instead uses `duration_ms` and does not accept `timeout_ms`. Match `wait_for`
locators to exactly one element.
1. Sign in through the UI: `fill` the user and password fields (the password
as a secret reference), submit, then `wait_for` an element only a signed-in
user sees.
2. Read with `request` steps: each collection the environment needs, twice
before any write, so fields that change on their own show up; then each
object a list shows by id, up to ten per collection. An object known only
from a list that leaves out fields its own read shows cannot be read by id
or updated in the environment.
3. Write the way the application's UI does: fill its forms and click its
buttons, so each request carries the fields the UI sends, including ones
the server would otherwise derive. Give the click or key press that submits
a create `save_response` to keep the new object's ID, then `goto` its page
(`/items/{saved:item_1}`) or read it with a `request` step. Never guess an
ID, and never create a probe object to learn the next one. Write with
`request` steps instead only when the UI cannot reach the operation (an
API-only resource, or a UI that does not call the JSON API), or when coverage
withholds the UI-shaped write as `unexplained-change` or `contradicted`;
that counts as one of the two re-recordings. A `request` step write that
coverage withholds that way usually lacks fields the UI sends: record it
through the UI instead. If a journey cannot be scripted, leave the write
unserved and report the limitation.
Record two numbered scratch objects per write type,
with varied free text. After each write, and before the next one, read the
object back by id and then its list: create 1, read 1, list, create 2,
read 2, list; never several writes before their reads.
4. Leave more than a second between two creates and between a create and its
first edit (`wait_for_timeout` with `duration_ms: 1100`), so lists sorted
by creation time show them in order.
5. Update both objects and read each back, changing the fields a person
would change (a name, an email address, a status, a due date) rather than
only a description: a served update accepts only the members a recorded
update stored. Run an action that turns something on or off (star,
bookmark, archive) twice in a row on one object, reading it back after
each: the second call shows whether it sets or flips the field, and the
environment serves the action only once a call has shown it. Delete both
and read each back (expect 404, or the object marked deleted) and the list.
6. Vary what the two creates send, however you write them: the first every
field the application takes, the second only what a person must give (its
title or name), with the other fields left out or empty. A served create
requires each member both creates filled in and lets a caller leave out one
a create left out or sent empty, so an agent that names only a title is not
refused for fields it had no reason to set.
About `request` steps:
- `path` is on the start URL's origin; the page must be on that origin.
- Header values are literals, declared secrets, a cookie the browser holds
(`{source: "cookie", name}`), a local storage entry
(`{source: "local_storage", key, json_pointer}`), a saved value
(`{source: "saved", name}`), or Basic credentials
(`{source: "basic_credentials", parts: [username, password]}`, sent as the
base64 of `username:password` and redacted), with an optional `prefix` such as
`"Bearer "` or `"Basic "`. Each credential part is a literal, secret, saved
value, cookie or local storage entry, so a header the frontend builds from a
login token (the username and the token) can be copied too; the password is
a secret or saved value, never a literal. Copy the
headers the application's own requests send (a CSRF header such as
`X-CSRFToken` from the `csrftoken` cookie, a bearer token). Values read from the page are redacted from records made after they
are read, not from the login response that returned them, and values
shorter than 8 characters are not redacted at all.
- `save: [{name, json_pointer, secret}]` keeps a string or number from the
step's JSON response, such as the created object's `/id`, for later steps;
`secret: true` redacts it, as for a token the API returns, so mark every
saved token, key or session value secret. This also protects the selected
source response before its body or headers are retained. Compressed or
unknown-size UI responses cannot supply saved values. On a `click` or `press`,
`save_response: {method, path, save}` keeps values from the response to the
new request observed while the step waits (`{*}` in `path` matches one segment).
Matching does not prove UI causality: choose a path distinct from polling;
multiple observed matches fail as ambiguous. The step
fails unless a 2xx JSON response arrives before its `timeout_ms`. Later
steps send a saved value as `{saved:name}` in a request `path` (and its
query) or a `goto` URL; in the `json` body, where a string that is only
`{saved:name}` keeps the saved JSON type; or as `{source: "saved", name}` in
a header or credential part.
- Reading a page value makes later bodies costlier to keep: after a CSRF
token, bodies over about 47 KiB are kept as `unavailable`. When the API
returns larger responses, raise `capture.limits`
(`max_network_body_allocation_bytes`, `max_total_event_allocation_bytes`).
- Request steps follow no redirects: a redirect fails the step, and
`expected_status` cannot name a 3xx status. Use the path the redirect names,
such as the one with the trailing slash.
- Set `expected_status` on every step, so a refused call fails its step and
the later steps report not having run, instead of recording read-backs of
nothing.
Keep it to about 60 to 160 requests. A scenario with no `request` step is
recorded exactly as before.
The capture returns its whole Evidence, every recorded body included, which is
often more than a client shows inline. Before checking coverage, confirm that
every entry of `normalized_result.steps` has `status: "completed"`; if the
client saved the result to a file, search it for `"status":"failed"` and read
that step's `error`. A failed sign-in or write cancels every step after it.
Use the result's `evidence_id` for coverage and export; do not paste the
Evidence back into another tool.
## Check coverage
`inspect_analysis_view` with `source: {kind: "retained-evidence",
evidence_id}` (the capture's id) and `view: {kind: "environment-coverage",
template_rules: ["after-uid-literal", "opaque-token", "sibling-variance",
"prefix-variance"]}`. It reports read tools
answered from the state or replayed (`reads.recorded_reasons`), the served write
tools, and each withheld write with its `reason` and `hint`. It does not apply
the serve-time catalog and state size checks; the export reports those.
Re-record a withheld write at most twice, and only for a recording gap. Stop at
once when the reason is application behaviour, and report the write as
withheld with its hint:
| Reason | What to do |
| --------------------- | --------------------------------------------------------------------------------------------------------------------------- |
| `no-state-target` | Check the path template and recorded views; record missing reads. The route form may not be supported as a mutation target. |
| `no-observation` | Re-record: read the entity back by id after the write; read it first for an update or delete. |
| `unconfirmed` | Re-record: read the entity and its list after the write. |
| `incomplete-entities` | Re-record: read the entity through every view that lists it. |
| `unexplained-change` | Re-record once with a second, different call of the same write. |
| `no-call` | Re-record once with a write that succeeds. |
| `side-effect` | Stop: the application changes other entities. |
| `contradicted` | Stop: a later read disagrees with the modelled effect. |
| `body-unusable` | Re-record a UI write once with `request` steps (its body was not exposed); else stop. |
Use new scratch names on each attempt, and delete what an earlier attempt
created before recording again (through the UI, or a `request` step with the
id that capture shows): the environment starts from what the recording first
reads, so a leftover object becomes seeded data that write tasks then find.
New browser scenario captures include request times, so a fully timed capture
can learn a write clock and serve updates that stamp modification times. Older
captures or ones missing a request start keep record order and learn no clock.
When coverage reports that limitation, record again with the current version
before concluding that the timestamp change cannot be served.
## Export and hand off
`export_environment_bundle` with `source: {kind: "retained-evidence",
evidence_id}` (or `{kind: "evidence-file", path}`), an absolute
`output_directory` that does not exist yet and whose parent does, and a `name`
(required when the application runs on `localhost` or an IP address). It returns
the coverage again, `tool_count`, the written files and warnings; `dry_run:
true` reports without writing. The bundle installs `rea-agents` of this REA's
version from the registry; when the user runs an unreleased REA build, pass the
`package_tarball` they give you (a packed `rea-agents` tarball) instead.
Tell the user how many tools answer from the state, which writes are served and
which are withheld and why, and the run command (`agent-env run <folder>`). Say
that the bundle ships the recorded responses and the capture's local paths
(for a browser scenario, the browser executable's path), and
list any `credential-named-members` warning pointers for review. Never publish
or share the bundle yourself.
See
[agent environments](https://github.com/morluto/rea/blob/main/docs/agent-environments.md)
for what the state, replay and the bundle establish, and
[evidence workflows](evidence-workflows.md) for the manifest and state views.
## Tasks for a desktop application environment
Use this when the user has a folder from `rea capture-desktop-application` and
wants a task for the environment `rea export-desktop-application` makes from
it: a prompt for a computer-use agent and checks that grade what the agent
left in the app's files. You write the prompt and the checks; REA validates
the task against the capture and exports it. These commands are CLI only.
### Describe the change
Run `rea describe-desktop-application-task CAPTURE --format json`. Read:
- `owned_paths`: the only paths whose files the app serves, so the only files
a check can read.
- `files`: each file there whose state differs between the capture's first
and last version, with its `role`, the `baseline` and `final` versions and
the net `change`. Text changes list each added and removed line once, and
`line_counts` gives lines found at both ends whose count changed.
- `task`: a draft whose `checks` are suggestions from the net change of the
documents the user worked on, or of the app's own state when no document
changed.
### Write the prompt
Say what the user wants done, in their words and the app's terms: "Add buy
milk to my to-do list and save it". Never say how the file stores it: no
paths the user would not name, line formats, XML, keys or SQL, and never the
checks. Replace the draft's placeholder; the export refuses it.
### Pick the checks
Checks must express only the observed change:
- Keep a suggestion only if it says what the user did. Drop those that follow
from how the app saves: a format rewrite, renumbered lines, counters. A
LibreCAD drawing gaining four holes was rewritten from DXF R12 to 2007, and
the draft suggests counts of `SECTION`, `ENDSEC` and `CIRCLE`; only
`CIRCLE` equals 9 is the user's change.
- Never check unchanged content, whole files, or values the app stamps:
times, ids it assigns, window sizes. REA refuses ISO 8601 and `asctime`
date-times (`Thu Nov 5 09:20:31 2026`); the
rest is your judgment.
- Prefer a few robust checks: whole lines over positions, rows by meaningful
columns over generated ids, `at_least` when the user may add more.
Kinds: `file_present` and `file_absent`; `lines_present` and `lines_absent`
(`lines`); `line_count` (`exact` or `contains`, and `equals`, `at_least` or
`at_most`); `value_equals` and `value_absent` (a JSON `pointer` into the view,
such as `/groups/GROUP/KEY` for a key file); `text_equals` and
`text_contains` (an XML element's whole text at a `pointer`, optional
`normalize_whitespace`); `row_present` and `row_absent` (`table`, `where` by
column) and `row_count`. Each names its `file` as a path under the home. A
document saved as a zip archive, such as FreeCAD's `.FCStd`, reads as its
entries: point into an entry's view, and name array members by selector
rather than position, as
`/entries/[name=Document.xml]/view/root/children/[name=Object][@name=Hole]`,
so the check survives an app version that writes them in another order. Give
`value_equals` a `tolerance` to compare a number the app writes as text, such
as `"5.0000000000000000"`, with a number. Check what the user wrote in a
document with `text_equals` on its paragraph element, not `value_equals` on a
text node: apps such as AbiWord and Writer split a paragraph into runs as they
save, so its first text node holds only part of it; use `text_contains` for a
value within a sentence, such as a time. The
[desktop application tasks](https://github.com/morluto/rea/blob/main/docs/desktop-application-tasks.md)
guide has the exact rules.
### Export the task
Write the task as JSON with a `name`, the `prompt` and the `checks`, then run
`rea export-desktop-application CAPTURE DIRECTORY --task TASK.json`. REA keeps
the task only when its checks fail on the seed and all pass on the captured
end state. A refusal names the check: one that fails on the end state does not
match what the user left; "every check already passes on the seed" means no
check captures the change.
When the export refuses an application that no apt repository installs, ask
the user for the `.deb` file they installed it from and capture with
`--package-file`, then ask where the computer may download that file and pass
it as `--package-url`. Never fetch a vendor's download page yourself: such
sites often refuse automated downloads, and the copy and its licence are the
user's to choose.
Tell the user the task's name, prompt and checks, and how to measure a solve
rate: run `agent-env run DIRECTORY --task NAME --agent claude-code` with
`--model MODEL` three times and count the runs reported `passed`. Do not run
agents unless asked. The bundle's seed is the user's real data: keep it local.
@@ -11,6 +11,44 @@ returned inline. When complete `inspect_binary_layout` or
`inspect_analysis_view` for a summary, one section/symbol/module, or a stable
page instead of repeating the producer.
For any logical Evidence source that advertises retained references, pass
`{kind: "retained-evidence", evidence_id}` instead of copying the complete
record back into a call. The same contract covers individual, optional, array
and nested sources across native, process, JavaScript, managed, mobile and WASM
workflows. Sources can independently mix inline records and exact references.
Resolution preserves the source's identity, provenance, coverage, partial facts
and unknowns; it neither reruns analysis nor makes incomplete facts complete.
Keep domain role and identity requirements: a native dossier cannot substitute
for a managed member observation, for example.
When starting from a summary or inspection page, use its
`normalized_result.parent_evidence_id` to reference the complete source.
The view's own `evidence_id` names
only that view. `compare_artifacts` explicitly supports artifact summary inputs
and resolves their parents; do not assume every consumer does. When a workflow
follows source links, keep those linked records available too.
MCP references belong to the current connection's ledger. If the ID is known,
pass it directly; no discovery call or provider setup is needed. If it is missing,
import the exported bundle or supply complete inline Evidence. Do not retry an
unchanged reference or rerun a producer merely to move an existing record.
Export before closing the session when the source must survive it. For CLI JSON
workflows that advertise `--evidence-bundle`, the same reference JSON resolves
against one explicitly selected authenticated bundle:
```bash
rea trace-application-feature request.json --evidence-bundle /absolute/path/evidence.json --json
```
This includes JavaScript traces/comparisons and runtime reconciliation, managed
reconstruction/native verification/graph projection, Android/Apple/Harmony graph
projection, WASM interface comparison and retained analysis views. The managed
member comparison CLI still takes two assembly paths; process comparison takes
two capture Evidence JSON paths. Check command help for the installed version.
An input bundle grants no implicit file lookup: `evidence-file` and historical
graph file selectors are accepted only by contracts advertising them. See the
[CLI reuse guide](https://github.com/morluto/rea/blob/main/docs/cli.md#import-export-and-compare-evidence).
Completed `inspect_artifact` inventory also supports a summary and
`view: {kind: "inventory", collection: "occurrences", logical_paths: ["package.json", "main.js"]}`.
Omit `logical_paths` to page all available occurrences; `[]` selects none.
@@ -35,6 +73,14 @@ 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.
`batch_decompile` returns every body unless you select `detail: "summary"` when
the connected tool advertises it. Use its parent Evidence ID with
`view: {kind: "batch-decompile", collection: "items"}` for body-free rows and
exact `where.indices`, `addresses`, `procedure_addresses` or `statuses`. Add
`include_pseudocode: true` for several chosen bodies, or use
`collection: "pseudocode"` with one row's `index` to page a large body. Batch
limitations aggregate every item, so do not attribute them to one successful row.
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.
@@ -46,16 +92,22 @@ Use comparisons with complete, compatible page sets when claiming equivalence
or absence:
- `compare_artifacts` compares inventory pages by occurrence path, content,
metadata, and graph relations.
metadata, and graph relations. Name each side as
`{kind: "retained-evidence", evidence_id}` (an `inspect_artifact` result,
its nested inventory or its summary) or
`{kind: "evidence-file", path, evidence_id?}` instead of resending Evidence.
- `compare_functions` compares explicit function Evidence, not fuzzy
whole-binary matches.
whole-binary matches. It rejects an `analyze_function` summary reply; pass
the summary's `parent_evidence_id` as a `retained-evidence` reference or
the complete dossier.
- `compare_bundles` compares canonical bundle membership and unknown history.
- `find_changed_behavior` combines existing comparisons; static differences
remain candidates, not causal proof.
remain candidates, not causal proof. Its `comparisons` accept exact references.
- `build_call_path` needs explicit function dossiers from one artifact/provider;
an incomplete frontier makes absence unknown.
pass complete parent IDs in `functions`. An incomplete frontier makes absence
unknown.
- `correlate_static_and_runtime` uses explicit mappings; matching patterns are
hypotheses, not causality.
hypotheses, not causality. Both comparison arrays accept exact references.
- `verify_reconstruction` evaluates a finite typed specification. A pass covers
only declared comparable claims, not global equivalence.
@@ -93,4 +145,32 @@ 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.
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.
Use `view: {kind: "environment-coverage"}` with the same rules to see what an
environment built from a capture would serve: read tools answered from the
state or replayed with their reason, and each write tool served or withheld
with a hint about what to record next; it does not apply the serve-time
catalog and state size checks. `rea environment-build <capture.har> <dir>`
inspects, projects and exports in one step and prints the same coverage, or
`null` when the bundle serves replay.
For a PE/ELF export question, `inspect_native_exports` accepts `detail: "summary"`
and retains the complete table. Query `inspect_analysis_view` with
`view: {kind: "native-exports", collection: "exports", names: ["selected_name"],
fields: ["name"], offset: 0, limit: 32}` and the reported parent Evidence ID.
All same-name versions and aliases survive; use `source_index` with the parent ID
for stable identity. `collection: "issues"` pages interpretation failures. Page
coverage describes selected retained rows; `summary.coverage` or `exports.coverage`
still describes the source table, so an empty partial page does not prove absence.
CLI `inspect-native-exports --detail summary --evidence_output PATH` saves the
complete bundle for later `inspect-analysis-view --evidence-bundle PATH` requests.
@@ -17,11 +17,21 @@ the complete artifact inventory and identity but reads source text and analyzes
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; `source_paths: []` is invalid, not an
unfiltered request. `detail: "summary"` reduces delivery
on either static application CLI route, or `--source-paths-json` with a JSON
file for selections too long for one command line. Omit the selection when the
question 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;
an admission error names `asar.max_header_bytes` and its bounds (at most
256 MiB). Call `update_analysis_settings` with that setting, `action: "set"`
and enough bytes, then retry the same full-archive request. No inspection or
REA restart is required. `REA_ASAR_MAX_HEADER_BYTES` seeds future REA processes.
`source_paths`, `detail`, and `max_heap_mb` do not bypass or raise header admission.
Runtime JavaScript timeout/heap defaults are also editable; explicit per-call
`analysis_timeout_ms` and `max_heap_mb` take precedence.
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
@@ -54,6 +64,29 @@ 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).
For captured endpoint observations and inference facts, `analyze_web_bundle`
also accepts `view: {kind: "web-bundle", collection: "endpoints", limit: 32}`
with `detail: "summary"`. Read later `endpoints`, `inferences`, or `summary`
pages from the retained parent. Filter before requesting detail; retain
`source_index` together with parent ID and collection, and request `location`
or `basis` when tracing sources. Page exhaustion does not mean producer
completeness: check `completeness`, structured `source_unknowns`, and limitations.
Endpoint candidates remain inferences rather than confirmed network behavior.
## Read a module without repeating analysis
Use the actual returned parent ID, on the same connection, as `source.evidence_id`.
@@ -136,7 +169,10 @@ leaving it, and `both` when the question needs both. Trace traversal includes
containment relationships, so even a narrow seed can expand many nodes; a
bidirectional trace can reach unrelated sibling modules. A module item or page
is preferable when its observations already answer the question. Do not treat
all reached nodes as participants in the feature.
all reached nodes as participants in the feature. For a broad trace, pass
`view: {"kind": "feature-trace", "collection": "paths"}` (or `summary`) to
retain the complete trace and read terminal paths first; page `nodes` or `edges`
with exact `where.node_ids` through `inspect_analysis_view` as needed.
Reuse retained parent Evidence when the server advertises it, or supply complete
Evidence inline. Include Hopper or Ghidra Evidence only when its artifact digest
@@ -203,10 +239,19 @@ 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
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.
analysis or select a provider; findings remain inline. For summary or inspection
replies, select the complete `normalized_result.parent_evidence_id`. Native
observation arrays and runtime reconciliation's `static_layers[].analysis` and
`runtime_observations[]` also accept exact references where advertised. Each
source can independently be inline or retained, preserving identity, coverage
and unknowns. CLI workflows advertising `--evidence-bundle` resolve these inputs
from the explicitly selected bundle. `close_binary` releases the engine while
retaining references and complete Unknown history on the same MCP connection.
`reset_investigation` explicitly clears them; optional `save_path` checkpoints the
complete bundle before clearing. Import that bundle in another connection to
restore its original references. Retention is bounded, in-memory and never silently
evicted; `binary_session.investigation` reports the budget. 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}`.
@@ -1,16 +1,51 @@
# 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,
For release comparisons on one MCP connection, retain `list_procedures` Evidence
from each target and reuse its exact IDs after target switches or `close_binary`.
For a handoff or new process, export the complete bundle and import it there,
including its source closure. `reset_investigation` explicitly clears retained
records; optional `save_path` checkpoints them before clearing.
`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
`procedure_info`, `read_bytes` and existing `analyze_function` dossiers as
`left_observations`/`right_observations`; an MCP `analyze_function` summary
reply contributes its `parent_evidence_id` as a `retained-evidence` reference,
not the summary itself. 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.
Aggregate completed comparisons with `find_changed_behavior`, selecting each
comparison's exact `retained-evidence` ID instead of resending full records.
Inline comparison Evidence remains accepted. References belong to the current
server: after handoff, import the original bundle including its source closure.
Do not substitute dossier IDs or summary views. Static differences remain
behavior candidates, with runtime behavior unknown unless separately observed.
For a matching representation refusal, inspect `details.limit` and raise
`native.matching_representation_bytes` with `update_analysis_settings` before
retrying the unchanged MCP request. CLI launch seed:
`REA_NATIVE_MATCHING_REPRESENTATION_BYTES`. The default is 96 MiB; the policy
range is 1 byte to 1 GiB and does not bound process RSS. Keep the complete
inventories: splitting them changes global name/body uniqueness.
## 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
@@ -29,7 +64,8 @@ See [desktop installations](https://github.com/morluto/rea/blob/main/docs/deskto
## 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`.
@@ -68,7 +104,8 @@ See [IL2CPP metadata](https://github.com/morluto/rea/blob/main/docs/il2cpp-metad
## 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`.
@@ -78,6 +115,124 @@ 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).
## PE import and delay-load tables
Use `inspect_pe_imports` for an explicit local PE32/PE32+ image when the
question concerns imported DLL and function names, ordinals, delay-load
descriptors, or the pefile import hash. No `open_binary`, Windows SDK, or
target execution is required. The CLI equivalent is `inspect-pe-imports PATH`.
Preserve artifact SHA-256, directory file ranges, original DLL spellings, and
ILT/IAT RVAs. A missing `IMAGE_DIRECTORY_ENTRY_IMPORT` directory is observed
absence of that table, not proof the image never imported APIs through another
table or overlay. Bound snapshots, loaded IAT contents, and export-forward
resolution remain unobserved. See
[PE import and delay-load tables](https://github.com/morluto/rea/blob/main/docs/pe-imports.md).
## PE TLS callbacks and load configuration
Use `inspect_pe_load_config` for an explicit local PE32/PE32+ image when the
question concerns code that runs before the entry point through TLS callbacks,
or the security cookie, SafeSEH, CFG, EH-continuation and XFG declarations in
the load configuration. No `open_binary`, Windows SDK, or target execution is
required. The CLI equivalent is `inspect-pe-load-config PATH`. Preserve artifact
SHA-256, directory and callback-array file ranges, and callback VAs as
hexadecimal strings. Fields outside the declared `Size` are `null`. Guard-flag
mitigations carry `evidence_kind: "inferred"`; enforcement remains unobserved.
See
[PE TLS callbacks and load configuration](https://github.com/morluto/rea/blob/main/docs/pe-load-config.md).
## Binary hardening
Use `inspect_binary_hardening` for an explicit local ELF, PE, or Mach-O file
when the question concerns PIE or ASLR, NX, RELRO, stack-protector or FORTIFY
signs, control-flow flags, or stripped state. No `open_binary`, pwntools, or
target execution is required. The CLI equivalent is
`inspect-binary-hardening PATH`. Universal Mach-O files return one image per
slice. A missing import is `unknown`, not proof the protection was omitted.
Authenticode and Mach-O code-signature rows report presence, not validity.
`hardened_runtime` stays unknown and names `inspect_signature`. See
[binary hardening](https://github.com/morluto/rea/blob/main/docs/binary-hardening.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).
## Apple IPS crash reports
Use `inspect_apple_ips` for an explicit local Apple `.ips` file when the
question concerns crash metadata, exception and termination facts, threads,
usedImages, lossless registers, or documented crash classes. No `open_binary`,
Xcode, atos, or target execution is required. The CLI equivalent is
`inspect-apple-ips PATH`. Preserve artifact SHA-256, `bug_type` as a string,
uppercase image UUIDs, and integer literals that exceed IEEE-754 range. Only
`bug_type` `"309"` yields a crash body; other DiagnosticReports types return
no crash result. Opened Mach-O and dSYM UUID joins remain unobserved. See
[Apple IPS crash reports](https://github.com/morluto/rea/blob/main/docs/apple-ips.md).
## Linux kernel modules
Use `inspect_kernel_module` for an explicit local ELF32/ELF64 relocatable
object when the question concerns `.modinfo` license, vermagic, alias,
depends, or parm strings, the `__versions` symbol CRCs, or an appended module
signature trailer. No `open_binary`, Linux host, or `modinfo` program is
required. The CLI equivalent is `inspect-kernel-module PATH`. Preserve
artifact SHA-256, key order, duplicate keys, CRC hex, and section file ranges.
A missing section is observed absence when section names are known, not proof
the module never carried that metadata. `.gnu.linkonce.this_module` is reported
as present or absent; `struct module` stays unknown because its layout changes
with the kernel version. `__ksymtab` records are not decoded. The PKCS#7 range
and its SHA-256 are observed; certificate, signer, and digest coverage remain
unvalidated. An absent trailer is not proof the module is unsigned in another
container. gzip, xz, and zstd whole-file containers are unsupported and name
`external_decompression` as the next probe: use an external codec utility to
decompress into a separate file, then call `inspect_kernel_module` on that file. See
[Linux kernel modules](https://github.com/morluto/rea/blob/main/docs/kernel-modules.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. For a few facts, use `detail: "summary"` with
a `beam` view selecting a collection, exact names/indices and fields; read later
pages from `parent_evidence_id` or the complete Evidence bundle. Same-name
exports retain every arity and original atom index. CLI summary delivery requires
`--evidence_output`. Null table counts remain distinct from decoded empty tables. 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
@@ -132,6 +287,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.
@@ -141,9 +320,23 @@ cross-references. Addresses and recovered pseudocode are analysis observations,
not original source. Provider unavailability and unsupported metadata remain
unknown rather than false.
For large pseudocode, request `procedure_pseudo_code` with `detail: "summary"`
on the first MCP call. It retains the complete observation and returns the
original selection, text availability and length without the body. Use
`normalized_result.parent_evidence_id` with `inspect_analysis_view` and
`view: {kind: "native", facet: "pseudocode", offset: 0, limit: 20000}`;
follow `coverage.next_offset` for later pages. Offsets and limits count UTF-16
code units, not lines or tokens. Pages reuse the same observation without
repeating decompilation. Null and empty text remain distinct. Omitted or
`complete` detail preserves full inline delivery. Summary delivery requires a
retaining session and does not reduce the work or memory of decompilation.
For a completed `analyze_function` dossier, use `inspect_analysis_view` with
`view: {kind: "native", facet: "value_flow_operations", offset: 0, limit: 32}`
and the exact retained Evidence ID to read selected facts without reanalysis.
The default MCP reply is already a summary of that dossier: its
`normalized_result.parent_evidence_id` is the retained ID to page, and its
null counts mark facets the provider did not record or declared unavailable.
Native facets also include procedure, pseudocode, assembly, basic blocks,
comments, callers/callees, references, unresolved calls, referenced strings and
names, the native API record, and value-flow summaries. Pseudocode offsets and limits count UTF-16
@@ -163,36 +356,84 @@ run, use `observe_native_calls` with explicit breakpoints and a bounded
`duration_ms`/`max_events`. It launches a new process under LLDB, so the target
runs and may change files or use the network. Hardened-runtime targets need the
`get-task-allow` entitlement.
Function breakpoints use exact LLDB runtime names: pass an `inspect_macho` export
or import `runtime_name` (`main`), not its symbol-table `name` (`_main`). REA does
not translate breakpoint names. Set `module` to the image
file name when selecting that image only; otherwise loaded system images also
match. Inspect retained locations and module paths before attributing a call.
Use `binary_session` with no arguments to check the open target, selected
provider, and alignment. Its default `result.tool_availability` carries a
complete inventory grouped by shared facts. Expand each group's `names` and
inherit the inventory-level client requirements unless a group overrides them;
availability, reasons, remediation, modes, and flags remain inline. The
`effects` and `annotations` arrays name true flags from fixed vocabularies, so an
omitted known flag is false. `tools/list` remains the canonical source for full
tool schemas when target or provider state changes.
provider, and alignment. Its default summary `result.tool_availability` carries
the complete tool inventory grouped by current status. Expand each group's
`names` and inherit the inventory-level client requirements unless a group
overrides them; availability, reasons, remediation, and modes remain inline.
Provider candidates keep availability and target-support status, code, and
reason. `omitted_fields` names what the summary leaves out; `{"detail":
"complete"}` adds each unavailable candidate's `remediation`, provider
diagnostics, candidate capability sets, and tool surface, `effects`, and
`annotations`. Those arrays name true flags from fixed
vocabularies, so an omitted known flag is false. `tools/list` remains the
canonical source for full tool schemas when target or provider state changes.
Provider capability groups carry provider identity and ordered limitations.
Prepend each set's `shared_limitations` to a group's `limitations` to recover the
operation's full ordered list. Candidate capabilities describe possible
providers. Top-level session capabilities describe the current route, and
operation's full ordered list. Candidate capabilities, returned with complete
detail, describe possible providers. Top-level session capabilities describe the current route, and
`analysis_provider_binding` identifies the selected provider.
## Continue editable Ghidra analysis across processes
For a task spanning agent sessions, check `binary_session` after selecting
Ghidra: the current provider's `capabilities[].shared_limitations` states whether
Program checkpoints are enabled or the session is ephemeral. Successful native
queries distinguish initial analysis from an actually restored packed Program;
do not infer that restoration happened merely because checkpoint mode is enabled.
In ephemeral mode, choose a private absolute directory and set
`REA_GHIDRA_CHECKPOINT_DIR` in the environment starting REA. Restart the MCP server
after changing that launch configuration; no current tool call or runtime
analysis-setting update enables it. Stock ELF/PE/Mach-O imports on Linux/macOS
are supported; see the [checkpoint setup and limitations](https://github.com/morluto/rea/blob/main/docs/installation.md#automatically-checkpoint-an-owned-ghidra-program).
With the setting enabled, normal opens restore compatible saved Programs
automatically. Initial completed analysis is saved, and `close_binary` saves
later metadata edits; no separate save/restore tool is needed. Repair a reported
save failure and retry close. Cancellation or process failure can lose edits
after the last successful save; unfinished initial analysis cannot resume.
Keep the same checkpoint configuration for later processes. Evidence/Unknown
history and immutable REA query snapshots have separate persistence workflows.
## Reuse caller-selected Ghidra analysis
When the caller supplies an expanded project, configure `REA_GHIDRA_PROJECT_PATH`
(the `.gpr` marker) and optionally `REA_GHIDRA_PROGRAM_NAME` (one root program),
then restart/reconnect before `open_binary`. CLI uses the same configuration.
REA opens only a fingerprint-bound owned copy with `-process -noanalysis -readOnly`;
original project files are not opened by the engine or saved. Database edits,
import overrides and repair scripts are unavailable in this mode. Treat original
analysis completeness/settings as unknown and prior edits as caller-project state.
The program's recorded import digest must match the target; changed project content
requires reopening for a fresh profile. See the
[setup and preparation guide](https://github.com/morluto/rea/blob/main/docs/installation.md#reuse-an-existing-ghidra-project).
## 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.
When the error names `ghidra.startup_timeout_ms`, call
`update_analysis_settings` with `action: "set"` and a larger value within the
reported bounds. Resolve retained cleanup before the next startup attempt.
No REA restart is needed; an already accepted startup keeps its original
budget. Follow the error's session-specific restart guidance, and preserve useful Evidence before closing
and reopening the target when remediation requires that lifecycle step.
For a per-function timeout, update `ghidra.decompile_timeout_seconds` and retry
the same function on the existing Program. Explicit `null` removes that
per-function deadline. Inspection is optional when the error supplies the
setting and bounds. Runtime updates disappear when REA exits; environment
changes only seed future REA processes.
The client's tool-call deadline must allow startup plus the requested query;
server settings and progress notifications cannot override it. CLI analysis
@@ -209,6 +450,50 @@ 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.
## Selected native facts and call graphs
For a large `get_call_graph`, or repeated `procedure_address`,
`procedure_callees`, `procedure_callers` and `resolve_containing_procedure`
queries, select `detail: "summary"` when the first 64 recorded observations
suffice. Read further `native-observation` pages from the returned parent Evidence
ID. Graph message IDs index that page's exact-text dictionary; keep level,
source index, failed nodes, and partial/unknown relationship coverage.
Read referenced source context through `native-context` facets `overview`,
`analysis-profile`, `limitations` and `result-limitations`. Reuse inspected
context/profile values only for the same digest; until caveats are read,
interpretation beyond the returned facts remains unknown. Complete Evidence
remains retained and exportable. Small complete replies can cost fewer tokens
than additional context/page calls; count the completed task and discovery.
## Static memory and original file bytes
For Ghidra, `read_bytes` reads initialized analysis memory, including loader
@@ -236,6 +521,14 @@ loaded bytes, file mappings, relocations and the entry against the immutable
snapshot. Keep `mismatch` and `unsupported` explicit. Verified import covers the
reported static image, not runtime DOS or PC-98 hardware behavior.
For a large load-image report, select `detail: "summary"` and query its
`parent_evidence_id` with `inspect_analysis_view` kind `native-load-image`.
Choose a collection such as `relocations` or `mappings`, select only needed
fields, and page with offset/limit. Original indices preserve same-address
alternatives. Null collections remain unrecorded or inapplicable; paging does
not upgrade unsupported verification. CLI requires `--evidence_output` for
summary delivery so complete source Evidence remains accessible.
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.
Complete function body ranges use inclusive ends and can contain gaps. Do not
@@ -279,6 +572,11 @@ names), coverage and per-collection counts. Page rows with
`view: {kind: "managed-members", collection: "call_edges", offset: 0, limit: 64}`
over `types`, `exported_types`, `fields`, `methods`, `member_refs`, `call_edges`,
`field_accesses` or `user_strings`.
Use exact `names` and `declaring_types` filters before paging supported
collections. For a compact method lookup, request `fields: ["name",
"declaring_type", "signature"]`; omit `body` to avoid anchors. Identity tokens,
original `source_index`, module/MVID and source coverage remain. Check
`unresolved_filter_rows` before interpreting an empty match as absence.
### .NET NativeAOT
@@ -329,24 +627,39 @@ 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
destination. No separate permission grant is needed.
For classic iNES cartridges, `open_binary` recognizes the byte signature without
an executable architecture. `inspect_artifact` retains header facts and each
PRG/CHR file unit's offset, length and SHA-256; its nested inventory's
`ines.reconstruction` verifies the actual ordered region bytes against the
whole-file digest. Extraction includes the original header, optional trainer
and uninterpreted trailing bytes. These file units are not mapper/CPU bank
maps. NES 2.0, UNIF, dirty legacy headers and truncated payloads are explicitly
refused. No emulator or 6502 analysis is performed. See the
[iNES guide](https://github.com/morluto/rea/blob/main/docs/ines-containers.md).
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`, `6502`, or `m68k`), and `entry_point` (`0x` hex). File offset 0 is mapped at
@@ -359,9 +672,27 @@ 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).
Intel HEX, S-record, UF2 and DfuSe files are containers, not raw images:
`inspect_artifact` lists address regions (UF2 per familyID, DfuSe per element
with its alternate setting), and `extract_artifact` writes each region. Open an
extracted region as `raw-binary` with its start address as `entry_point`. UF2
numbering gaps leave coverage unknown; a DfuSe suffix CRC mismatch follows
`integrity_policy`. See [UF2 and DfuSe](https://github.com/morluto/rea/blob/main/docs/uf2-dfuse.md).
### Function annotations in Ghidra
Use `annotate_native_function` for one function name and/or entry comments, with at least one explicit change. Review its annotation readback and refreshed dossier inline. Changes are atomic and session-scoped; empty comments clear them, omitted fields preserve them. Later MCP calls observe edits until close. CLI `annotate-native-function` returns the updated analysis before discarding the session. Original executable bytes are unchanged; edits invalidate immutable snapshots. Windows P0 does not admit database mutations. For naming only, `set_address_name` / `set_addresses_names` also work with Ghidra: they name function entries or data addresses (primary labels) and return compact per-address success booleans instead of a dossier.
Fresh-import sessions admit annotations; existing-project reuse is read-only.
Use `annotate_native_function` for one function name and/or entry comments, with at least one explicit change. Review its annotation readback and refreshed dossier inline. Changes are atomic; empty comments clear them, omitted fields preserve them. Later MCP calls observe edits. Normal close saves them when owned Program checkpoint mode is enabled; ephemeral mode discards them. CLI `annotate-native-function` follows the same setting when closing after its updated result. Select the [checkpoint workflow](#continue-editable-ghidra-analysis-across-processes) before edits that must survive a later process. Original executable bytes are unchanged; edits invalidate immutable snapshots. Windows P0 does not admit database mutations. For naming only, `set_address_name` / `set_addresses_names` also work with Ghidra: they name function entries or data addresses (primary labels) and return compact per-address success booleans instead of a dossier. Ghidra rejects batch addresses that request the same name or a name another selected address holds; `raw_result.failures` gives each failure's reason.
For `compare_functions`, obtain `analyze_function` Evidence before and after the
edit while the MCP session is open; annotation Evidence is not a comparison
input. Export the needed Evidence before close if comparison will happen later.
Authenticated same-artifact coordinates preserve renamed entries as
`matched / same-entry`, with the canonical `entry_address` inline; name and
exact-text changes remain separate dimensions. This requires compatible
architecture, provider-owned mapping and selected-image commitments. Equal
numeric addresses alone cannot establish correspondence; inspect retained
source Evidence and coverage when the match remains ambiguous.
## Offline PE resource inventory
@@ -375,11 +706,23 @@ 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`.
Evidence in `rea compare-wasm-interfaces input.json --json`, or exact references
with `--evidence-bundle /absolute/path/evidence.json`. Use the complete parent
ID when the inspection returned a summary.
## Godot PCK
@@ -399,3 +742,91 @@ 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).
UnityFS AssetBundle containers (`.bundle`, `.ab`, YooAsset packages and other
byte-identified files) list `CAB-*` SerializedFiles and `.resS` members with
header and node facts; none/LZ4/LZ4HC member blocks stream on demand. Bundles
declaring asset-bundle encryption keep header facts with unknown directory
coverage; LZMA member bytes retain named occurrences with unavailable hashes.
Virtual member offsets are not physical carrier locations. Format 6–8 readers
cache one decoded block and support retained filtered/paged views. No
decryption or SerializedFile object decoding is provided. See
[UnityFS](https://github.com/morluto/rea/blob/main/docs/unityfs.md).
WeChat mini-program packages (`.wxapkg` files with `0xBE`/`0xED` marks) list
members with declared names and absolute carrier offsets; member bytes are
stored verbatim. `V1MMWX` PC-client packages report `encrypted` directory
status with unknown coverage and are refused on extraction; no decryption is
attempted. See
[wxapkg](https://github.com/morluto/rea/blob/main/docs/wxapkg.md).
U-Boot legacy uImages use the same inspection/extraction workflows. Prefer a
retained summary and selected inventory views for header facts or component
IDs. Header addresses/CPU/OS are declarations; payloads remain as stored.
Multi-file components retain their aligned physical ranges; the full payload
is also materialized. Unknown name text retains its original 32 bytes. See
[U-Boot uImage](https://github.com/morluto/rea/blob/main/docs/uimage.md).
## ESP images
For an ESP32-family app or bootloader image, `open_binary` then
`inspect_artifact` with `detail: "summary"`. `root_format_metadata` holds the
observed `header` and `descriptor` (its `kind` is the app or bootloader role)
and the derived `chip`, `checksum`, `appended_sha256` and
`undescribed_trailing` ranges. Segment rows (`segment-NN.bin`) carry
`load_addr`, `classification` (`mapped`, `loaded`, `not-loaded` or `unknown`,
per ESP-IDF `should_map`/`should_load`; RAM ranges are not checked) and
`source_location`; select them with `inspect_analysis_view` and
`logical_paths`. Signature blocks are `unverified`; `public_key_sha256`
matches eFuse key digests.
Unknown `source_coverage` means truncation or undescribed trailing bytes, such
as the flash segments of a `--ram-only-header` image; do not infer absence
from it. Erased flash is described. Integrity mismatches are facts, not
refusals. ESP8266 images are refused, and flash-encrypted images are not
recognized. Segments are not disassembled, and RISC-V or Xtensa segments
cannot be opened as raw-binary. See
[ESP images](https://github.com/morluto/rea/blob/main/docs/esp-image.md).
## ESP flash images
A whole-flash dump opens as `esp-flash-image`. `root_format_metadata` holds
`partition_table` (its `method`: `default`, `caller` or a derived
`scan`), `bootloader` and `apps` ESP image facts, the `otadata` records,
`partitions_outside_dump` and `boot_selection`. Every table has a
`boot_selection`: its `basis` names the bootloader's branch (`no-otadata`
without otadata, `none` when the bootloader rejects the table, `unknown`
when a record is missing), and it lists its `assumptions`;
`running_partition` is always `unknown`. Each image names its entry
`path`. With `detail: "summary"` the root kind is `esp-flash-image-summary`
and each image is a compact `summary` (chip, entry point, segment count,
classified load ranges, signature block count, descriptor identity,
integrity); complete detail keeps every segment. Partition rows
(`partitions/<label>.bin`) carry the declared `type`, `subtype`, names,
`flash_address`, `size` and flags; `encrypted` is a storage flag, not
proof of ciphertext. `written_offset`/`written_end` bound the bytes that
are not erased: an app slot whose facts say `erased` can still hold an older
image, and a limitation says so. For a dump read from another flash address, call
`open_binary` with `format: "esp-flash-image"` and `flash_offset`; an
`esp-image` whose `flash_dump` is set names that offset in a next probe; a
relocated table can be named with `partition_table_offset`. No table found
means unknown, often flash encryption. Extraction writes `nvs_keys` as
ordinary data. See
[ESP flash images](https://github.com/morluto/rea/blob/main/docs/esp-flash-image.md).
## Bun standalone executables
A `bun build --compile` executable stores its module graph in Mach-O
`__BUN,__bun`, a PE or ELF `.bun` section, or an appended trailer.
`open_binary` then `inspect_artifact` reports root format `bun-standalone` with
the carrier, structural layout, module count, entry point, raw flags and
compile exec-argv; each module keeps its `/$bunfs/...` source name, loader,
encoding, sizes and SHA-256, with sourcemap, bytecode and module-info regions as
separate children. Use summary root metadata `entry_point_logical_path` as the
exact inventory `logical_paths` selector, then read the selected content node by
its artifact ID; the reader owns virtual-name normalization and indexed fallback. `extract-artifact` writes stored contents through the safe
output tree; route extracted JavaScript to `analyze_javascript_application`.
Layout labels describe record shapes, not a detected compiler version; unknown
or ambiguous layouts are unsupported. Bytecode is retained, not decoded, and the
executable is never run. See
[Bun standalone executables](https://github.com/morluto/rea/blob/main/docs/bun-standalone.md).
@@ -1,4 +1,52 @@
# Browser and Electron runtime observation
# Runtime observation
## Native runtime questions
Start from the requested observation. State the selected target, input, expected
result, failure signal and stopping condition before a run. Reuse static Evidence
to choose a function or hypothesis; `trace_native_values` follows recovered
static value dependencies and does not measure runtime values. Prefer static
inspection or existing capture comparison when it already answers the question.
| Question | Existing workflow | Host and effects |
| -------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Which selected native entry is reached, with which integer argument registers? | `observe_native_calls` on the active Mach-O, with exact function/Objective-C breakpoints and a duration/event bound | macOS with LLDB and required debugger access. Launches a new owned process, stops at entries, records and continues; target execution may write files, show UI or use the network. It never attaches to an existing process. Inspect termination and partial coverage after the run. |
| What does caller-authored instrumentation observe? | `list_frida_devices` / `list_frida_processes` when discovery is needed; `start_frida_session`, `load_frida_script`, then status/unload/close | Requires compatible installed Frida bindings and selected device access; the real REA verifier covers Linux. Attach selects an existing PID; spawn selects a program and starts it paused until `resume_frida_session`. Scripts execute in the target and can change memory, files, network activity and behavior. This is not passive inspection. |
| Which debugger state or controlled execution explains the behavior? | `start_gdb_session` and `gdb_console`, or `start_rizin_debug_session` and `rizin_debug_command` | Persistent debugger workflows are Linux-only. GDB starts a debugger; console commands choose/load/attach/run targets. Rizin debug start launches the selected target and requires a durable `inferior_tty`. Both consoles are unrestricted and can change target memory/registers, run shell commands, write files and use the network. |
| How do terminal output, process lifetime and selected files differ between runs? | `capture_process_scenario`, then `compare_process_captures` with compatible recorded inputs | New captures require Linux or macOS and a working native PTY backend; macOS also needs `xcrun`/Swift for ownership inspection. Capture launches an owned command with ordinary user permissions. Observation paths select snapshots, not a filesystem sandbox. Comparing retained captures does not execute targets and is also available on Windows. |
| What does an existing desktop window show, or what changes after a declared UI action? | `observe_native_ui`; use `capture_native_ui_scenario` only for requested actions | macOS 14+ with required Screen Recording/Accessibility access. Select an existing PID/window. Observation does not launch or own the app; scenario actions can change its data or trigger network activity, and leave its state as-is. |
Close each persistent session with its matching tool. `close_frida_session`
unloads scripts and detaches; it does not kill the target. A still-paused spawn
is resumed only after instrumentation cleanup is confirmed. GDB and Rizin close
can detach a live inferior and let it continue only after verifying its durable
output terminal. When disposition or cleanup is unresolved, preserve the owned
session and reported recovery; do not assume that closing the debugger terminated
the target. Process capture and LLDB call observation instead own their launched
processes and report their termination/cleanup outcome. Cancellation is not proof
that cleanup completed.
Unrestricted GDB/Rizin commands and relevant start/close operations retire an
active binary-analysis session in the same MCP server. Close an active Ghidra
session explicitly first; after target effects settle, reopen the artifact before
new static analysis. A Cutter bridge command is likewise unrestricted and may
affect its GUI/target; it is not a passive native observation substitute.
To test a static hypothesis across two builds, retain compatible static
comparison Evidence and `compare_process_captures` Evidence. Supply explicit
finding/dimension mappings to `correlate_static_and_runtime`. It does not ingest
arbitrary Frida, LLDB or debugger traces, auto-match names, or prove causality
from cochange. Preserve those traces as their own Evidence and leave unobserved
behavior unknown. Record truncation, unmatched breakpoints, changed target
identity and cleanup uncertainty as limitations, rather than treating no retained
event as proof that the behavior never occurs.
Detailed contracts and prerequisites: [Frida](https://github.com/morluto/rea/blob/main/docs/frida-instrumentation.md),
[GDB/Rizin/Cutter](https://github.com/morluto/rea/blob/main/docs/reverse-engineering-tools.md),
[process capture](https://github.com/morluto/rea/blob/main/docs/process-capture.md),
and [native calls/UI](https://github.com/morluto/rea/blob/main/docs/native-investigation.md#native-desktop-observation).
## Browser and Electron
Supply the existing browser or Electron instance's literal-port loopback CDP
endpoint in the request. No separate permission configuration or per-call
@@ -38,6 +86,8 @@ Page-declared WebMCP tools are untrusted inventory and are never invoked by REA.
browser and profile. Check `browser.cleanup` for the outcome.
Request only the DOM, accessibility, or network details needed for the question.
The default final capture contains the URL; step outcomes are reported separately.
For multi-step flows with trees or events, select `detail: "summary"` and page
exact steps, transactions, text slices or events with `inspect_analysis_view` kind `browser-scenario`.
Add a wait action for the expected page state before capturing it. Report that
REA performed the declared actions.
+21
View File
@@ -0,0 +1,21 @@
{
"$schema": "https://anthropic.com/claude-code/marketplace.schema.json",
"name": "rea",
"description": "Reverse engineer anything with agents, from app behavior down to native binaries.",
"owner": {
"name": "morluto",
"url": "https://github.com/morluto"
},
"plugins": [
{
"name": "rea",
"description": "Reverse engineer anything with agents, from app behavior down to native binaries.",
"source": {
"source": "npm",
"package": "rea-agents",
"version": "6.8.0"
},
"category": "development"
}
]
}
+29
View File
@@ -0,0 +1,29 @@
{
"name": "rea",
"version": "6.8.0",
"description": "Reverse engineer anything with agents, from app behavior down to native binaries.",
"author": {
"name": "morluto",
"url": "https://github.com/morluto"
},
"homepage": "https://rea.tools/",
"repository": "https://github.com/morluto/rea",
"license": "MIT",
"keywords": [
"reverse-engineering",
"binary-analysis",
"decompiler",
"mcp-server"
],
"skills": "./skills",
"mcpServers": {
"rea": {
"command": "node",
"args": [
"${CLAUDE_PLUGIN_ROOT}/scripts/plugin-mcp-launcher.mjs",
"6.8.0"
]
}
},
"icon": "./assets/rea-plugin.png"
}
+13
View File
@@ -0,0 +1,13 @@
{
"mcpServers": {
"rea": {
"command": "node",
"args": [
"./scripts/plugin-mcp-launcher.mjs",
"6.8.0"
],
"cwd": ".",
"startup_timeout_sec": 30
}
}
}
+40
View File
@@ -0,0 +1,40 @@
{
"name": "rea",
"version": "6.8.0",
"description": "Reverse engineer anything with agents, from app behavior down to native binaries.",
"author": {
"name": "morluto",
"url": "https://github.com/morluto"
},
"homepage": "https://rea.tools/",
"repository": "https://github.com/morluto/rea",
"license": "MIT",
"keywords": [
"reverse-engineering",
"binary-analysis",
"decompiler",
"mcp-server"
],
"skills": "./skills",
"mcpServers": "./.codex-plugin/mcp.json",
"interface": {
"displayName": "REA",
"shortDescription": "Reverse engineer anything",
"longDescription": "Reverse engineer anything with agents, from app behavior down to native binaries.",
"developerName": "morluto",
"category": "Developer Tools",
"capabilities": [
"Interactive",
"Read",
"Write"
],
"websiteURL": "https://rea.tools/",
"defaultPrompt": [
"Use REA to explain how this binary implements a feature.",
"Use REA to compare two versions of this application.",
"Use REA to recover the sources from this JavaScript bundle."
],
"composerIcon": "./assets/rea-plugin.png",
"logo": "./assets/rea-plugin.png"
}
}
+12
View File
@@ -0,0 +1,12 @@
# Review changes to security-sensitive automation and dependency inputs.
/.github/CODEOWNERS @morluto @kaoru0822-kitauji @N0zoM1z0 @gensokyo-dev
/.github/dependabot.yml @morluto @kaoru0822-kitauji @N0zoM1z0 @gensokyo-dev
/.github/workflows/ @morluto @kaoru0822-kitauji @N0zoM1z0 @gensokyo-dev
/.github/actions/ @morluto @kaoru0822-kitauji @N0zoM1z0 @gensokyo-dev
/package.json @morluto @kaoru0822-kitauji @N0zoM1z0 @gensokyo-dev
/package-lock.json @morluto @kaoru0822-kitauji @N0zoM1z0 @gensokyo-dev
/scripts/verify-tools/skills/package.json @morluto @kaoru0822-kitauji @N0zoM1z0 @gensokyo-dev
/scripts/verify-tools/skills/package-lock.json @morluto @kaoru0822-kitauji @N0zoM1z0 @gensokyo-dev
/scripts/fixtures/http3-lab-requirements.txt @morluto @kaoru0822-kitauji @N0zoM1z0 @gensokyo-dev
/website/requirements.txt @morluto @kaoru0822-kitauji @N0zoM1z0 @gensokyo-dev
/third_party/ @morluto @kaoru0822-kitauji @N0zoM1z0 @gensokyo-dev
+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 }}
+21
View File
@@ -0,0 +1,21 @@
name: REA production code
# Match the existing default-setup scan's local-input threat model.
threat-models: [local]
# Retain root traversal, including future production roots. Hidden directories
# need their own root; use a directory so Python can share this configuration.
paths:
- .
- docs/.vitepress
# Keep the default query suite and all production roots, including bridge/,
# install.sh, runtime scripts, release controllers, and GitHub Actions.
# Exclude test inputs at extraction time so they cannot taint production callers.
paths-ignore:
- tests
- scripts/fixtures
- "**/*.test.*"
- "**/*.fixture.*"
- "**/test_*.py"
- "**/*_test.py"
+33
View File
@@ -0,0 +1,33 @@
version: 2
updates:
- package-ecosystem: npm
directories:
- /
- /scripts/verify-tools/skills
schedule:
interval: weekly
cooldown:
default-days: 7
open-pull-requests-limit: 5
groups:
npm-minor-and-patch:
update-types:
- minor
- patch
- package-ecosystem: pip
directory: /website
schedule:
interval: weekly
open-pull-requests-limit: 5
- package-ecosystem: github-actions
directory: /
schedule:
interval: weekly
open-pull-requests-limit: 5
groups:
actions-minor-and-patch:
update-types:
- minor
- patch
+13 -2
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
@@ -33,6 +36,9 @@ unchecked. Maintainers can help resolve open questions.
Use "none" or "not applicable" only when established. -->
- Semantic owner and earliest changed stage:
<!-- Name the producer of parsed facts or resources and its affected consumers.
Summarize superseded paths removed and any distinct policy, lifecycle,
lazy-loading, or compatibility responsibility retained. -->
- Contribution scope fit and linked maintainer decision:
- CLI and MCP/tool-catalog contract:
- Provider, bridge, target-format, or platform compatibility:
@@ -65,6 +71,10 @@ source, link, or rendered-document checks. -->
- User-visible CLI/MCP output (if applicable):
- Remaining proof gaps:
For boundary fixes, identify the applicable [boundary regressions](https://github.com/morluto/rea/blob/main/docs/testing.md#boundary-regressions)
and [lifecycle regressions](https://github.com/morluto/rea/blob/main/docs/testing.md#lifecycle-regressions)
covered by the evidence above, and explain relevant gaps.
For evidence-bearing changes:
- [ ] Observed, derived, and inferred claims remain distinguishable.
@@ -96,7 +106,8 @@ limits, security/privacy/containment implications, and meaningful proof gaps. --
## Author readiness
<!-- The human owner completes these declarations before requesting review.
<!-- The human owner completes these declarations before requesting review,
unless the PR author is exempt as listed in CONTRIBUTING.md.
The PR author is the owner by default. A different owner must explicitly accept
ownership on this PR.
AI agents must leave these declarations to a human to dictate to them or there could be catastrophic consequences for many users of REA.
+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
+269 -35
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:
@@ -43,6 +61,9 @@ jobs:
qualification_run_id: ${{ steps.qualification.outputs.run_id }}
code: ${{ steps.plan.outputs.code }}
reverse_tools: ${{ steps.plan.outputs.reverse_tools }}
python: ${{ steps.plan.outputs.python }}
packet_capture: ${{ steps.plan.outputs.packet_capture }}
signal_recording: ${{ steps.plan.outputs.signal_recording }}
sqlite: ${{ steps.plan.outputs.sqlite }}
runtime: ${{ steps.plan.outputs.runtime }}
package: ${{ steps.plan.outputs.package }}
@@ -54,6 +75,7 @@ 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 }}
@@ -125,9 +147,15 @@ jobs:
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
- name: Validate source architecture before dependency installation
if: needs.changes.outputs.code == 'true'
run: node scripts/verify/architecture-guards.mjs
- 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'
@@ -151,14 +179,10 @@ jobs:
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
@@ -180,6 +204,93 @@ 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
python:
name: Python producer tests
needs: changes
if: needs.changes.outputs.python == 'true' && needs.changes.outputs.tier == 'fast'
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
- name: Cache npm package downloads
id: npm-cache
uses: ./.github/actions/npm-cache
- run: npm ci
# Analysis-efficiency checks import compiled process modules.
- name: Reuse portable build outputs across runs
uses: ./.github/actions/portable-build-cache
- run: npm run test:prepare
- run: npm run test:python
test-source:
name: Source tests
needs: changes
@@ -219,9 +330,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
@@ -299,12 +412,15 @@ 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.agentenv == 'true'
needs.changes.outputs.agentenv == 'true' ||
needs.changes.outputs.packet_capture == 'true' ||
needs.changes.outputs.signal_recording == 'true'
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
@@ -324,7 +440,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:
@@ -332,13 +450,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
@@ -373,8 +492,9 @@ jobs:
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
@@ -469,11 +589,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
@@ -577,6 +708,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 }}
@@ -645,9 +779,23 @@ jobs:
npm run verify:legacy-simulator
npm run verify:apple-dispatch
npm run verify:interface-builder
- name: Verify portable iNES CLI, MCP and source boundaries
run: >-
npm run test:focused --
tests/acceptance/inesContainer.test.ts
tests/boundary/filesystem/inesArtifact.test.ts
tests/boundary/filesystem/inesCancellation.test.ts
- name: Run native Apple and process boundary regressions
run: >-
npm run test:focused --
tests/boundary/providers/hopper/hopperMacLaunchArguments.test.ts
tests/acceptance/providers/hopperStartupRecovery.test.ts
tests/boundary/providers/hopper/hopperClient.helperFailure.test.ts
tests/boundary/providers/hopper/bridgeBootstrapCleanup.test.ts
tests/boundary/providers/hopper/bridgeTargetLease.integration.test.ts
tests/acceptance/artifactDmgAttachmentRecoveryCli.test.ts
tests/boundary/providers/native/nativeDmgArtifactReader.test.ts
tests/process-global/dmgCancellationReason.test.ts
tests/boundary/filesystem/installScript.test.ts
tests/boundary/filesystem/nibNativeHierarchy.test.ts
tests/boundary/filesystem/interfaceBuilderBundle.test.ts
@@ -657,8 +805,13 @@ jobs:
tests/boundary/cli/nativeDemangleCli.test.ts
tests/boundary/cli/nativeDispatchPropertyAttributes.test.ts
tests/boundary/mcp/nativeDemangleMcp.test.ts
tests/boundary/mcp/nativeMachoSummaryMcp.test.ts
tests/boundary/providers/native/nativeSignatureTargetBinding.test.ts
tests/boundary/providers/native/nativeUiOffscreenWindow.test.ts
tests/boundary/process/nativeUiVerifierLane.test.ts
tests/boundary/cli/nativeSignatureCli.test.ts
tests/boundary/cli/nativeMachoArm64_32Cli.test.ts
tests/boundary/cli/nativeMachoEncryptionCli.test.ts
tests/boundary/mcp/nativeSignatureTargetBindingMcp.test.ts
src/process/capture/FilesystemSnapshot.test.ts
tests/process-global/filesystemSnapshotFallbackCancellation.test.ts
@@ -683,7 +836,7 @@ jobs:
id: npm-cache
uses: ./.github/actions/npm-cache
- run: npm ci
- name: Run the public CLI/MCP reconstruction journey
- name: Verify static CLI/MCP journeys and synthetic report conformance
env:
REA_READINESS_REPORT_PATH: readiness-report.json
run: |
@@ -713,7 +866,14 @@ 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
@@ -739,17 +899,41 @@ jobs:
# The matching native artifact establishes real Job Object and private
# runtime boundaries for these fixtures. Ghidra and Java are not needed;
# real engine claims remain in the separate controlled-provider workflow.
# Run native proofs before the broad curated suite so an unrelated
# fixture failure cannot hide the real control and recovery checks.
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: windows-process-boundary-fixture
path: .cache/windows-process-boundary/
# Passing the fixture adds the breakaway, caller/child token, environment
# semantics and ancestor reparse observations, which the lane skips
# entirely when it is absent.
- run: >-
npm run verify:windows-native -- .
"${{ github.workspace }}\.cache\windows-process-boundary\process-boundary.exe"
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: windows-native-inspection-fixture
path: .cache/windows-native-inspection-fixture/
- name: Verify native inspection failure ownership and recovery
run: node scripts/verify-windows-native-inspection.mjs
- name: Run curated Windows capability tests
run: >-
npx vitest run --coverage.enabled=false
src/process/capture/FilesystemSnapshot.test.ts
tests/process-global/filesystemSnapshotFallbackCancellation.test.ts
tests/boundary/filesystem/typescriptCompiler.test.ts
tests/boundary/filesystem/releaseProposal.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/claudeDesktopSkillUninstallWindows.test.ts
tests/boundary/filesystem/clientConfigurationEnvironment.test.ts
tests/boundary/filesystem/clientConfigurationLayeredEnvironment.test.ts
tests/acceptance/setup/registrationRuntimeEnvironmentCli.test.ts
@@ -761,6 +945,7 @@ jobs:
tests/boundary/process/packageNpmInvocation.test.ts
tests/boundary/process/packedBridgeVerification.test.ts
tests/boundary/process/windowsOutputCapture.test.ts
tests/boundary/process/windowsExecutableSelection.test.ts
tests/acceptance/webModuleCli.test.ts
tests/acceptance/webSourceLocationCli.test.ts
tests/boundary/cli/referenceSourceImportStatus.test.ts
@@ -785,22 +970,6 @@ jobs:
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:
name: windows-process-boundary-fixture
path: .cache/windows-process-boundary/
# Passing the fixture adds the breakaway, caller/child token, environment
# semantics and ancestor reparse observations, which the lane skips
# entirely when it is absent.
- run: >-
npm run verify:windows-native -- .
"${{ github.workspace }}\.cache\windows-process-boundary\process-boundary.exe"
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: windows-native-inspection-fixture
path: .cache/windows-native-inspection-fixture/
- name: Verify native inspection failure ownership and recovery
run: node scripts/verify-windows-native-inspection.mjs
- name: Verify real Node Inspector discovery and observation
timeout-minutes: 10
env:
@@ -816,7 +985,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
@@ -832,7 +1001,7 @@ jobs:
retention-days: 30
docs:
name: Docs Generation
name: Documentation
needs: changes
if: needs.changes.outputs.docs == 'true'
runs-on: ubuntu-latest
@@ -847,6 +1016,8 @@ jobs:
uses: ./.github/actions/npm-cache
- run: npm ci
- run: npm run docs:check
- name: Render documentation and validate page links
run: npm run docs:build
- name: Generation leaves authored files unchanged
run: git diff --exit-code
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
@@ -858,68 +1029,126 @@ 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')) }}
packet_capture:
needs: [changes, static, build]
if: needs.changes.outputs.packet_capture == 'true'
secrets:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
uses: ./.github/workflows/real-packet-capture.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')) }}
signal_recording:
needs: [changes, static, build]
if: needs.changes.outputs.signal_recording == 'true'
secrets:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
uses: ./.github/workflows/real-signal-recording.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]
@@ -944,8 +1173,12 @@ jobs:
[
changes,
static,
static-deep,
build,
reverse-tools-quality,
python,
packet_capture,
signal_recording,
test-shard,
test-source,
sqlite-runtime,
@@ -960,6 +1193,7 @@ jobs:
browser,
binary_layout,
evm,
apk_signature,
javascript_recovery,
recorded_crash,
web_network,
+61
View File
@@ -0,0 +1,61 @@
name: CodeQL production
on:
push:
branches: [main]
pull_request:
branches: [main]
schedule:
- cron: "37 5 * * 1"
workflow_dispatch:
permissions:
contents: read
concurrency:
group: codeql-production-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
analyze:
# A repository administrator must disable default setup before enabling
# this workflow; default setup rejects advanced CodeQL result uploads.
if: vars.REA_CODEQL_ADVANCED == 'true'
name: Analyze production (${{ matrix.language }})
runs-on: ubuntu-latest
timeout-minutes: 45
permissions:
contents: read
security-events: write
strategy:
fail-fast: false
max-parallel: 1
matrix:
language: [javascript-typescript, python, actions]
steps:
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
persist-credentials: false
- name: Initialize production analysis
uses: github/codeql-action/init@9b3254085494b55a0f7c4c2b3d2d52cc712ce370 # v4
with:
languages: ${{ matrix.language }}
build-mode: none
config-file: .github/codeql/production.yml
threads: 2
ram: 4096
- name: Analyze production code
uses: github/codeql-action/analyze@9b3254085494b55a0f7c4c2b3d2d52cc712ce370 # v4
with:
category: /language:${{ matrix.language }}
output: ${{ runner.temp }}/codeql-production-results
threads: 2
ram: 4096
- name: Retain complete analysis evidence
if: always()
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: codeql-production-${{ matrix.language }}
path: ${{ runner.temp }}/codeql-production-results
if-no-files-found: error
retention-days: 7
+53 -4
View File
@@ -25,16 +25,16 @@ jobs:
node-version-file: .nvmrc
- name: Validate the SDK pin against the provider's verified release
run: node scripts/verify-ghidra-bridge-sources.mjs --check-sdk-pin >> "$GITHUB_ENV"
# Only the jars and application.properties are extracted: the check
# compiles against them and never launches Ghidra or imports a target.
- name: Acquire checksum-verified Ghidra jars in isolated runner storage
# 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" '*/Ghidra/*.jar' '*/Ghidra/application.properties' -d "${ghidra_root}"
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}"
@@ -46,3 +46,52 @@ jobs:
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"
# Each probe imports source-owned bytes at 0x1000, away from the Cortex-M
# vector symbols that ARMCortex.pspec defines at 0, and decodes them from
# the language's default context, so M-profile Thumb state must hold at
# the first byte. The bytes are never executed.
- name: Decode M-profile ARM and Xtensa bytes in their default context
shell: bash
run: |
java_home_variable="JAVA_HOME_${GHIDRA_JAVA_MAJOR}_X64"
export JAVA_HOME="${!java_home_variable}"
probe_root="${RUNNER_TEMP}/rea-decode"
mkdir -p "${probe_root}"
# mrs r0, msp; bx lr
printf '\357\363\010\200\160\107' > "${probe_root}/cortex.bin"
# mrs r0, msplim (ARMv8-M only); bx lr
printf '\357\363\012\200\160\107' > "${probe_root}/v8m.bin"
# entry a1, 32; retw.n
printf '\066\101\000\035\360' > "${probe_root}/xtensa.bin"
decode() {
local name="$1" language="$2"
shift 2
mkdir -p "${probe_root}/${name}-project"
timeout 180s "${GHIDRA_INSTALL_DIR}/support/analyzeHeadless" "${probe_root}/${name}-project" Decode \
-import "${probe_root}/${name}.bin" -loader BinaryLoader -loader-baseAddr 0x1000 \
-processor "${language}" -noanalysis -readOnly -deleteProject \
-scriptPath "${GITHUB_WORKSPACE}/tests/conformance/ghidra" \
-postScript ReaDecodeProbe.java "${language}" "$@" \
> "${probe_root}/${name}.log" 2>&1 || { cat "${probe_root}/${name}.log"; exit 1; }
cat "${probe_root}/${name}.log"
grep -F "REA_DECODE_OK ${language} $*" "${probe_root}/${name}.log"
}
decode cortex ARM:LE:32:Cortex mrs/4/msp bx/2/lr
decode v8m ARM:LE:32:v8-m mrs/4/msplim bx/2/lr
decode xtensa Xtensa:LE:32:default entry/3/a1 retw.n/2
+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:
+24 -7
View File
@@ -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
PIP_DISABLE_PIP_VERSION_CHECK: "1"
@@ -38,10 +54,11 @@ jobs:
- 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.
# The pinned framework builds its gateway and service-db images only when pulling them from
# ghcr.io fails, from public ECR base images; public ECR caps anonymous pulls by data per source
# IP and hosted runners share IPs, so such a build can fail here for hours. mirror.gcr.io serves
# the same Docker official images under the same digests, so each base image those Dockerfiles
# name 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
@@ -56,16 +73,16 @@ jobs:
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/python" -m pip install --require-virtualenv --no-cache-dir 'agentenv-framework==0.9.1431'
"${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
- name: Pull the gateway and service-db 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 |
grep -rhoE '^FROM +public\.ecr\.aws/docker/library/[^ ]+' --include='Dockerfile*' "${package}/env/gateway" "${package}/env/envs/service_db" | awk '{print $2}' | sort -u |
while read -r image; do
name=${image#public.ecr.aws/docker/library/}
source=mirror.gcr.io/library/$name
@@ -0,0 +1,63 @@
name: Real Android metadata bridge
on:
push:
branches: ["**"]
paths:
- .github/workflows/real-android-metadata.yml
- bridge/android/**
- src/android/JadxRelease.ts
- scripts/fixtures/android/**
- tests/conformance/android/**
- tests/fixtures/android/static-initializers/**
- tests/fixtures/android/direct-invokes/**
- tests/fixtures/android/external-invokes/**
- .nvmrc
workflow_dispatch:
workflow_call:
permissions:
contents: read
concurrency:
group: android-metadata-${{ github.ref }}
cancel-in-progress: true
jobs:
android-metadata:
runs-on: ubuntu-24.04
timeout-minutes: 10
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
- name: Fetch checksum-pinned engine and public APK fixture
run: node scripts/fixtures/android/fetch.mjs "$RUNNER_TEMP/rea-android"
- name: Compile and exercise the real metadata bridge
shell: bash
run: |
export JAVA_HOME="$JAVA_HOME_21_X64"
android_root="$RUNNER_TEMP/rea-android"
classes="$android_root/classes"
jar="$android_root/jadx-headless-mcp-0.7.1-all.jar"
mkdir -p "$classes"
"$JAVA_HOME/bin/javac" -J-Xmx512m -J-XX:ActiveProcessorCount=2 \
-classpath "$jar" -d "$classes" \
bridge/android/ReaJadxBridge.java \
tests/conformance/android/ReaJadxMetadataRegression.java \
tests/conformance/android/ReaJadxExternalReferencesRegression.java
"$JAVA_HOME/bin/java" -Xmx512m -XX:ActiveProcessorCount=2 \
-classpath "$classes:$jar" ReaJadxMetadataRegression \
"$android_root/ApiDemos-debug.apk" \
"$GITHUB_WORKSPACE/tests/fixtures/android/static-initializers" \
"$classes" "$GITHUB_WORKSPACE/tests/fixtures/android/direct-invokes" \
| tee "$android_root/metadata.log"
grep -F 'RAW_CLASS_SELECTOR_REGRESSION_PASS nested=true metadata=true references=true' \
"$android_root/metadata.log"
"$JAVA_HOME/bin/java" -Xmx512m -XX:ActiveProcessorCount=2 \
-classpath "$classes:$jar" ReaJadxExternalReferencesRegression \
"$GITHUB_WORKSPACE/tests/fixtures/android/external-invokes" "$classes" \
| tee "$android_root/external-references.log"
grep -F 'EXTERNAL_REFERENCE_REGRESSION_PASS prototypes=2 using_nodes=3 sites=4' \
"$android_root/external-references.log"
+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
+22
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:
@@ -39,6 +55,12 @@ jobs:
- uses: ./.github/actions/portable-build-cache
with:
build_cache_artifact: ${{ inputs.build_cache_artifact }}
- name: Install AArch64 and ELF32 cross toolchains for source-owned fixtures
run: |
sudo apt-get update
sudo apt-get install --yes --no-install-recommends \
gcc-aarch64-linux-gnu gcc-i686-linux-gnu gcc-arm-linux-gnueabi gcc-mips-linux-gnu gcc-mipsel-linux-gnu \
libc6-dev-arm64-cross libc6-dev-i386-cross libc6-dev-armel-cross libc6-dev-mips-cross libc6-dev-mipsel-cross
- name: Acquire exact upstream libraries in an isolated runner environment
shell: bash
run: |
+23 -1
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
@@ -32,7 +48,13 @@ jobs:
- uses: ./.github/actions/portable-build-cache
with:
build_cache_artifact: ${{ inputs.build_cache_artifact }}
- run: npm run verify:browser
# Own a fresh bus so Chromium never depends on an inherited session address.
- run: dbus-run-session -- npm run verify:browser
env:
REA_BROWSER_EXECUTABLE: /usr/bin/google-chrome
REA_BROWSER_NO_SANDBOX: "true"
# The preceding verifier builds the runtime used by this request journey.
- run: dbus-run-session -- node scripts/verify-browser-request.mjs
env:
REA_BROWSER_EXECUTABLE: /usr/bin/google-chrome
REA_BROWSER_NO_SANDBOX: "true"
+8
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
+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"
+38 -36
View File
@@ -1,43 +1,35 @@
name: Real packet capture
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:
pull_request:
paths:
- "tests/boundary/captures/**"
- "tests/boundary/cli/captureCleanup.test.ts"
- "tests/boundary/mcp/packetCaptureMcp.test.ts"
- "src/captures/**"
- "src/pcap/**"
- "src/domain/pcap/**"
- "src/domain/analysisView/pcapCaptureView.ts"
- "src/application/pcap/**"
- "src/contracts/pcap/**"
- "src/server/registerPcapTools.ts"
- "tests/boundary/pcap/**"
- "src/domain/captures/**"
- "src/application/captures/**"
- "src/contracts/captures/**"
- "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"
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
concurrency:
group: real-packet-capture-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
tshark:
runs-on: ubuntu-24.04
@@ -52,8 +44,18 @@ jobs:
uses: ./.github/actions/npm-cache
- run: sudo apt-get update && sudo DEBIAN_FRONTEND=noninteractive apt-get install -y tshark util-linux python3
- run: npm ci
- uses: ./.github/actions/portable-build-cache
with:
build_cache_artifact: ${{ inputs.build_cache_artifact }}
- 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/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
- name: Run focused tests and require Linux resource and cleanup cases
run: |
report=".cache/rea-packet-required-tests.json"
mkdir -p .cache
rm -f "$report"
node scripts/run-exact-tests.mjs run --reporter=default --reporter=json --outputFile.json="$report" \
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 tests/acceptance/pcapVerifier.test.ts
node scripts/ci/require-provider-tests.mjs packet "$report"
- run: node scripts/verify-packet-capture.mjs
env:
REA_TSHARK_COMMAND: /usr/bin/tshark
+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' }}"
UV_CONCURRENT_BUILDS: "1"
UV_CONCURRENT_INSTALLS: "1"
UV_PYTHON_DOWNLOADS: never
+38 -29
View File
@@ -1,36 +1,35 @@
name: Real signal recording
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:
pull_request:
paths:
- "tests/boundary/signals/**"
- "tests/boundary/cli/signalCleanup.test.ts"
- "tests/boundary/mcp/signalRecordingMcp.test.ts"
- "src/signals/**"
- "src/domain/signals/**"
- "src/application/signals/**"
- "src/contracts/signals/**"
- "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"
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
concurrency:
group: real-signal-recording-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
sigrok:
runs-on: ubuntu-24.04
@@ -45,8 +44,18 @@ jobs:
uses: ./.github/actions/npm-cache
- run: sudo apt-get update && sudo apt-get install -y sigrok-cli util-linux python3
- run: npm ci
- uses: ./.github/actions/portable-build-cache
with:
build_cache_artifact: ${{ inputs.build_cache_artifact }}
- run: npm run build:cached
- run: npm run test:focused -- tests/boundary/signals/decoderDiscovery.test.ts src/process/LinuxResourceLimits.test.ts src/domain/signals/annotationTextWindow.test.ts tests/boundary/signals/recordingProvider.test.ts tests/boundary/signals/recordingDescriptorCleanup.test.ts tests/boundary/signals/resourceLimits.test.ts tests/boundary/mcp/signalRecordingMcp.test.ts tests/boundary/cli/signalCleanup.test.ts
- name: Run focused tests and require Linux resource and cleanup cases
run: |
report=".cache/rea-signal-required-tests.json"
mkdir -p .cache
rm -f "$report"
node scripts/run-exact-tests.mjs run --reporter=default --reporter=json --outputFile.json="$report" \
tests/boundary/signals/decoderDiscovery.test.ts src/process/LinuxResourceLimits.test.ts src/domain/signals/annotationTextWindow.test.ts tests/boundary/signals/recordingProvider.test.ts tests/boundary/signals/recordingDescriptorCleanup.test.ts tests/boundary/signals/resourceLimits.test.ts tests/boundary/mcp/signalRecordingMcp.test.ts tests/boundary/cli/signalCleanup.test.ts
node scripts/ci/require-provider-tests.mjs signal "$report"
- run: node scripts/verify-signal-recording.mjs
env:
REA_SIGROK_COMMAND: /usr/bin/sigrok-cli
+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:
+18 -1
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
@@ -28,7 +44,8 @@ jobs:
- uses: ./.github/actions/portable-build-cache
with:
build_cache_artifact: ${{ inputs.build_cache_artifact }}
- run: env -u DBUS_SESSION_BUS_ADDRESS npm run verify:browser:runtime
# Own a fresh bus so Chromium never depends on an inherited session address.
- run: dbus-run-session -- npm run verify:browser:runtime
env:
REA_BROWSER_EXECUTABLE: /usr/bin/google-chrome
REA_BROWSER_NO_SANDBOX: "true"
+21 -1
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
@@ -30,8 +46,12 @@ jobs:
build_cache_artifact: ${{ inputs.build_cache_artifact }}
- name: Install the pinned compiler into an isolated fixture directory
run: npm install --prefix "$RUNNER_TEMP/rea-web-source-map-compiler" --no-audit --no-fund --package-lock=false [email protected]
- run: npm run verify:browser:source-maps
# Own a fresh bus so Chromium never depends on an inherited session address.
- run: dbus-run-session -- npm run verify:browser:source-maps
env:
# Retain browser launch argv/PID/stderr in the verifier's captured
# CLI failure diagnostics without changing its deadline or retrying.
DEBUG: pw:browser
REA_BROWSER_EXECUTABLE: /usr/bin/google-chrome
REA_BROWSER_NO_SANDBOX: "true"
REA_WEB_SOURCE_MAP_COMPILER: ${{ runner.temp }}/rea-web-source-map-compiler/node_modules/esbuild/lib/main.js
+128 -41
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 }}
@@ -41,19 +47,63 @@ jobs:
GH_TOKEN: ${{ secrets.RELEASE_PLEASE_TOKEN || secrets.GITHUB_TOKEN }}
GH_REPO: ${{ github.repository }}
run: |
finalized=$(gh api --paginate "repos/${GH_REPO}/pulls?state=open&base=main&per_page=100" \
--jq '.[] | select(.head.repo.full_name == .base.repo.full_name) | select(.head.ref | startswith("release-please--branches--main--")) | select(any(.labels[]; .name == "rea:release-notes-finalized")) | .number')
# Filter at the repository connection, without a search-index dependency.
pages=$(gh api graphql --paginate --slurp \
-f owner="${GH_REPO%/*}" -f name="${GH_REPO#*/}" \
-f query='query($owner:String!,$name:String!,$endCursor:String) {
repository(owner:$owner,name:$name) {
nameWithOwner
pullRequests(first:100,after:$endCursor,states:OPEN,baseRefName:"main",labels:["rea:release-notes-finalized"]) {
nodes { number headRefName headRepository { nameWithOwner } }
pageInfo { hasNextPage endCursor }
}
}
}')
finalized=$(jq -er '
if type != "array" or length == 0 then error("Missing GitHub pages") else . end |
if .[-1].data.repository.pullRequests.pageInfo.hasNextPage != false
then error("Incomplete GitHub pagination") else . end |
map(
if (.errors // [] | length) != 0 then error("GitHub query failed") else . end |
.data.repository |
if (.nameWithOwner | type) != "string" or (.pullRequests.nodes | type) != "array" or
(.pullRequests.pageInfo.hasNextPage | type) != "boolean"
then error("Invalid GitHub repository connection") else . end |
.nameWithOwner as $repo | .pullRequests.nodes[] |
if (.number | type) != "number" or (.headRefName | type) != "string" or (has("headRepository") | not) or
(.headRepository != null and (.headRepository.nameWithOwner | type) != "string")
then error("Invalid GitHub pull request") else . end |
select(.headRepository.nameWithOwner == $repo) |
select(.headRefName | startswith("release-please--branches--main--")) | .number
) | map(tostring) | join(" ")' <<< "$pages")
if [[ -n "${finalized}" ]]; then
echo "Finalized release notes on PR ${finalized}; automatic proposal refresh is paused." >> "$GITHUB_STEP_SUMMARY"
echo "frozen=true" >> "$GITHUB_OUTPUT"
fi
- uses: googleapis/release-please-action@5c625bfb5d1ff62eadeeb3772007f7f66fdcf071 # v4.4.1
- name: Check out proposal controller and complete Git history
if: steps.editorial.outputs.frozen != 'true'
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
fetch-depth: 0
persist-credentials: false
- uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6.5.0
if: steps.editorial.outputs.frozen != 'true'
with:
token: ${{ secrets.RELEASE_PLEASE_TOKEN || secrets.GITHUB_TOKEN }}
target-branch: main
skip-github-release: true
node-version-file: .nvmrc
package-manager-cache: false
- name: Install proposal controller dependencies
if: steps.editorial.outputs.frozen != 'true'
run: npm ci
- name: Update release proposal
if: steps.editorial.outputs.frozen != 'true'
env:
GH_TOKEN: ${{ secrets.RELEASE_PLEASE_TOKEN || secrets.GITHUB_TOKEN }}
SOURCE_SHA: ${{ github.sha }}
run: node scripts/release-proposal.mjs --target-branch main --source-sha "$SOURCE_SHA"
merged-release:
name: Release reviewed main merge
@@ -226,15 +276,25 @@ jobs:
node scripts/release-notes.mjs publication --repository "$GH_REPO" \
--source "$SOURCE_SHA"
- uses: googleapis/release-please-action@5c625bfb5d1ff62eadeeb3772007f7f66fdcf071 # v4.4.1
id: release
with:
- name: Prepare checkpoint release proposal
if: inputs.phase == 'prepare'
id: proposal
env:
# A dedicated credential starts PR CI automatically; the built-in
# token creates approval-required PR runs for maintainers to approve.
GH_TOKEN: ${{ secrets.RELEASE_PLEASE_TOKEN || secrets.GITHUB_TOKEN }}
RELEASE_BRANCH: ${{ inputs.release_branch }}
SOURCE_SHA: ${{ steps.branch.outputs.sha }}
run: node scripts/release-proposal.mjs --target-branch "$RELEASE_BRANCH" --source-sha "$SOURCE_SHA"
- uses: googleapis/release-please-action@5c625bfb5d1ff62eadeeb3772007f7f66fdcf071 # v4.4.1
if: inputs.phase == 'publish'
id: release
with:
token: ${{ secrets.RELEASE_PLEASE_TOKEN || secrets.GITHUB_TOKEN }}
target-branch: ${{ inputs.release_branch }}
skip-github-release: ${{ inputs.phase == 'prepare' }}
skip-github-pull-request: ${{ inputs.phase == 'publish' }}
skip-github-release: false
skip-github-pull-request: true
- name: Require a merged release PR for publication
if: inputs.phase == 'publish' && steps.release.outputs.release_created != 'true'
@@ -254,26 +314,26 @@ jobs:
fi
- name: Check out release pull request
if: inputs.phase == 'prepare' && steps.release.outputs.prs_created == 'true'
if: inputs.phase == 'prepare' && steps.proposal.outputs.prs_created == 'true'
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
ref: ${{ fromJSON(steps.release.outputs.pr).headBranchName }}
ref: ${{ fromJSON(steps.proposal.outputs.pr).headBranchName }}
fetch-depth: 0
persist-credentials: false
- name: Set up Node.js for generated documentation
if: inputs.phase == 'prepare' && steps.release.outputs.prs_created == 'true'
if: inputs.phase == 'prepare' && steps.proposal.outputs.prs_created == 'true'
uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6.5.0
with:
node-version-file: .nvmrc
package-manager-cache: false
- name: Install dependencies
if: inputs.phase == 'prepare' && steps.release.outputs.prs_created == 'true'
if: inputs.phase == 'prepare' && steps.proposal.outputs.prs_created == 'true'
run: npm ci
- name: Validate prepared release version and migration notes
if: inputs.phase == 'prepare' && steps.release.outputs.prs_created == 'true'
if: inputs.phase == 'prepare' && steps.proposal.outputs.prs_created == 'true'
env:
RELEASE_BRANCH: ${{ inputs.release_branch }}
SOURCE_SHA: ${{ steps.branch.outputs.sha }}
@@ -283,11 +343,11 @@ jobs:
--source-sha "$SOURCE_SHA"
- name: Regenerate release documentation
if: inputs.phase == 'prepare' && steps.release.outputs.prs_created == 'true'
if: inputs.phase == 'prepare' && steps.proposal.outputs.prs_created == 'true'
run: npm run docs:generate
- name: Validate generated release documentation
if: inputs.phase == 'prepare' && steps.release.outputs.prs_created == 'true'
if: inputs.phase == 'prepare' && steps.proposal.outputs.prs_created == 'true'
run: npm run docs:check && git diff --exit-code
qualification:
@@ -301,11 +361,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 +405,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 +416,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 +473,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
+3 -3
View File
@@ -17,10 +17,10 @@ jobs:
strategy:
fail-fast: false
matrix:
# Exercise unavailable-runtime diagnostics, the first supported release,
# and new patches on both supported lines without hand-editing versions.
# Exercise unavailable-runtime diagnostics and rolling supported releases.
# Security floors prevent fallback to an older hosted-toolcache patch.
# The repository-pinned Node 24 version also runs the complete test suite.
node: ["22.x", "24.15.0", "24.x", "26.x"]
node: ["^22.23.4", "24.22.1", "^24.22.1", "^26.12.1"]
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
+4
View File
@@ -21,6 +21,10 @@ jobs:
python-version: "3.12"
cache: pip
cache-dependency-path: website/requirements.txt
- name: Set up Node.js for the website Worker checks
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
- name: Install sharing-image dependencies
run: |
sudo apt-get update
+50 -8
View File
@@ -54,6 +54,10 @@ jobs:
python-version: "3.12"
cache: pip
cache-dependency-path: website/requirements.txt
- name: Set up Node.js for the website Worker checks
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
- name: Install sharing-image dependencies
run: |
sudo apt-get update
@@ -75,10 +79,21 @@ jobs:
PY
- name: Configure Pages
uses: actions/configure-pages@983d7736d9b0ae728b81ab479565c72886d7745b # v5.0.0
# upload-pages-artifact excludes dot directories, including .well-known.
- name: Package the website including agent discovery
run: |
tar --dereference --hard-dereference --directory website/_site \
--exclude=.git --exclude=.github -cf "$RUNNER_TEMP/artifact.tar" .
tar -tf "$RUNNER_TEMP/artifact.tar" \
./.well-known/ard.json ./.well-known/ai-catalog.json \
./.well-known/agent-skills/index.json > /dev/null
- name: Upload the website
uses: actions/upload-pages-artifact@7b1f4a764d45c48632c6b24a0339c27f5614fb0b # v4.0.0
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
path: website/_site
name: github-pages
path: ${{ runner.temp }}/artifact.tar
retention-days: 1
if-no-files-found: error
deploy:
needs: prepare
@@ -130,14 +145,24 @@ jobs:
PUBLISH_TARGET: ${{ inputs.target }}
run: |
python3 -u - <<'PY'
import hashlib
import json
import os
import sys
import time
from urllib.error import URLError
from urllib.parse import urljoin
from urllib.parse import urljoin, urlsplit
from urllib.request import Request, urlopen
def open_public(url, accept=None):
headers = {
"Cache-Control": "no-cache",
"User-Agent": "REA-Website-Verifier/1.0 (+https://github.com/morluto/rea)",
}
if accept:
headers["Accept"] = accept
return urlopen(Request(url, headers=headers), timeout=10)
expected = os.environ["GITHUB_SHA"]
pages_url = os.environ["PAGES_URL"]
if not pages_url:
@@ -153,11 +178,7 @@ jobs:
for name, base in list(pending.items()):
url = urljoin(base, "deployment.json") + "?commit=" + expected
try:
request = Request(url, headers={
"Cache-Control": "no-cache",
"User-Agent": "REA-Website-Verifier/1.0 (+https://github.com/morluto/rea)",
})
with urlopen(request, timeout=10) as response:
with open_public(url) as response:
actual = json.load(response).get("commit")
if actual == expected:
print(f"{name} serves {expected}: {base}")
@@ -172,6 +193,27 @@ jobs:
time.sleep(5)
if pending:
sys.exit("Could not verify published revision on: " + ", ".join(pending))
for name, base in hosts.items():
with open_public(urljoin(base, ".well-known/agent-skills/index.json")) as response:
skill, = json.load(response)["skills"]
# Fetch from each selected host, including the /rea/ mirror.
artifact_url = urljoin(base, urlsplit(skill["url"]).path.lstrip("/"))
with open_public(artifact_url) as response:
digest = "sha256:" + hashlib.sha256(response.read()).hexdigest()
if digest != skill["digest"]:
sys.exit(name + " serves a skill archive with the wrong digest")
for manifest in ("ard.json", "ai-catalog.json"):
with open_public(urljoin(base, ".well-known/" + manifest)) as response:
if not json.load(response).get("entries"):
sys.exit(name + " serves an empty discovery manifest")
if name == "Cloudflare":
with open_public(base, accept="text/markdown") as response:
vary = {value.strip().lower() for value in response.headers.get("Vary", "").split(",")}
if response.headers.get_content_type() != "text/markdown" or "accept" not in vary:
sys.exit("Cloudflare did not negotiate Markdown with Vary: Accept")
if 'rel="ard"' not in response.headers.get("Link", ""):
sys.exit("Cloudflare is missing its discovery Link header")
print(name + " serves the discovery manifests and verified skill archive")
with open(os.environ["GITHUB_STEP_SUMMARY"], "a", encoding="utf-8") as summary:
links = " and ".join(f"[{name}]({url})" for name, url in hosts.items())
summary.write(f"Published commit `{expected}` to {links}.\n")
+1 -1
View File
@@ -50,7 +50,7 @@ Thumbs.db
# Local reference sources and analysis artifacts
/_reference/
/experiments/analysis-efficiency/results/
/experiments/analysis-efficiency/
# Generated website download
/website/public/examples/notes-example.zip
+1 -1
View File
@@ -1,5 +1,5 @@
{
"threshold": 3.5,
"threshold": 4,
"reporters": ["consoleFull"],
"ignore": [
"node_modules/**",
+1 -1
View File
@@ -1 +1 @@
24.18.0
24.22.1
+4 -1
View File
@@ -10,7 +10,10 @@
"node_modules/**",
"CHANGELOG.md",
"package-lock.json",
".claude-plugin/plugin.json",
".codex-plugin/plugin.json",
"_reference/**",
"third_party/**"
"third_party/**",
".codex-plugin/mcp.json"
]
}
+95 -3
View File
@@ -1,7 +1,13 @@
{
"$schema": "./node_modules/oxlint/configuration_schema.json",
"plugins": ["typescript", "import", "unicorn", "oxc"],
"plugins": ["typescript", "import", "unicorn", "oxc", "vitest"],
"rules": {
"rea/no-buffer-deep-equality": "error",
"rea/cancellation-cause": "error",
"no-async-promise-executor": "error",
"no-unreachable": "error",
"no-unsafe-optional-chaining": "error",
"no-useless-escape": ["warn", { "allowRegexCharacters": ["-"] }],
"complexity": ["warn", 20],
"max-depth": ["warn", 4],
"max-lines": [
@@ -23,7 +29,38 @@
"max-params": ["warn", 4],
"no-var": "warn",
"no-var-requires": "error",
"typescript/no-import-type-side-effects": "error"
"typescript/no-import-type-side-effects": "error",
"vitest/no-standalone-expect": "off",
"vitest/no-conditional-expect": "off",
"vitest/expect-expect": "off",
"vitest/no-disabled-tests": "off",
"vitest/require-mock-type-parameters": "off",
"vitest/require-to-throw-message": "off",
"import/no-duplicates": [
"warn",
{
"preferInline": false
}
],
"vitest/valid-expect": [
"warn",
{
"maxArgs": 2
}
],
"rea/no-evidence-json-preadmission": "error",
"rea/no-positional-tool-contract": "error",
"rea/no-fixture-root-cleanup": "error",
"typescript/no-floating-promises": "error",
"typescript/no-misused-promises": "warn",
"typescript/return-await": ["error", "error-handling-correctness-only"],
"typescript/switch-exhaustiveness-check": [
"error",
{
"considerDefaultExhaustiveForUnions": true
}
],
"rea/no-shared-module-mock": "error"
},
"overrides": [
{
@@ -36,6 +73,12 @@
"no-var-requires": "off"
}
},
{
"files": ["src/**/*.ts"],
"rules": {
"typescript/no-misused-promises": "error"
}
},
{
"files": ["tests/**/*.ts", "src/**/*.test.ts", "src/**/*.fixture.ts"],
"rules": {
@@ -47,7 +90,8 @@
"skipBlankLines": true,
"skipComments": true
}
]
],
"typescript/no-misused-promises": "warn"
}
},
{
@@ -91,6 +135,45 @@
}
]
}
},
{
"files": ["tests/**/*.test.ts", "src/**/*.test.ts"],
"rules": {
"vitest/valid-expect": [
"warn",
{
"maxArgs": 2
}
],
"vitest/valid-expect-in-promise": "error",
"vitest/no-focused-tests": "error"
}
},
{
"files": [
"scripts/lib/http3-capture-assertions.test.mjs",
"scripts/lib/http3-verifier-lifecycle.test.mjs"
],
"rules": {
"typescript/no-floating-promises": [
"error",
{
"allowForKnownSafeCalls": [
{
"from": "package",
"package": "@types/node",
"name": "test"
}
]
}
]
}
},
{
"files": ["scripts/fixtures/javascript-recovery/code-split/entry.mjs"],
"rules": {
"typescript/no-floating-promises": "off"
}
}
],
"env": {
@@ -109,5 +192,14 @@
".devin",
".factory",
"third_party"
],
"options": {
"reportUnusedDisableDirectives": "error"
},
"jsPlugins": [
{
"name": "rea",
"specifier": "./scripts/lint/index.mjs"
}
]
}
+1 -1
View File
@@ -1,3 +1,3 @@
{
".": "6.7.0"
".": "6.8.0"
}
+31 -10
View File
@@ -22,10 +22,16 @@ Keep proof of ownership separate from validity, editability, and readiness. Malf
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.
After confirming a bug, search sibling adapters, consumers and similar workflows
for the same failure. Verify suspected matches, record working comparisons, and
group findings by root cause instead of filing duplicate reports.
When addressing a review thread, inspect the producing boundary and affected consumers for brittleness. Complete directly related refactoring in the same change. Before resolving the thread, explain the root cause, structural fix, validation, and any remaining cleanup.
Parse unknown boundary values and model expected failures with the tagged error algebra and `Result`. Preserve partial facts and meaningful failure reasons. Keep identity and lookup values separate from display formatting; preserve source values and explicit unknowns when normalization loses information. Absolute paths, file URLs, and HTTP paths have different semantics.
@@ -51,15 +57,18 @@ Optimize in this order:
3. CPU and memory. Avoid unnecessary work and copies; bound concurrency,
retained state and serialization growth.
For changes affecting these costs, measure representative completed tasks and
compare equivalent workflows before and after where available. Report token
usage, elapsed time, CPU time and peak process-family memory. Separate cold
Reserve performance measurements for changes that target performance or have a
concrete regression concern. Routine cleanup uses correctness checks; changes
to helper calls, imports, or representations alone do not require synthetic
benchmarks. When measurement is warranted, measure representative completed
tasks and compare equivalent workflows before and after where available. Report
token usage, elapsed time, CPU time and peak process-family memory. Separate cold
and warm runs, label estimates, and state unmeasured costs. Fewer bytes alone
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.
@@ -70,13 +79,24 @@ 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.
When changing installers or setup recovery, follow
[Checking installers and publisher downloads](docs/testing.md#checking-installers-and-publisher-downloads).
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
For tool design, follow [docs/tool-design.md](docs/tool-design.md): start from the analyst question, reuse existing contracts, preserve caller choices, and return useful evidence inline. Limits must follow real format, protocol, authority, or resource constraints. Account for representation expansion before allocation; derive completeness from examined coverage.
For test changes or provider claims, use [docs/testing.md](docs/testing.md). Prefer real public CLI/MCP workflows, then production-boundary integration, then producer goldens. Keep module tests for distinct semantics stronger workflows cannot reliably reproduce. Preserve malformed-input, cancellation, cleanup, permission, capacity, and target-identity coverage when pruning. Report unverified coverage explicitly; simulated providers do not establish real-provider behavior.
For user- or agent-facing tool problems, prioritize fixing the owning behavior,
contract, and actionable result or recovery at the point of use. Document the
result in product docs when explanation is needed. Add skill guidance only for
workflow judgment or context that the tool cannot reasonably convey; do not use
a skill as the primary repair for a discoverability or contract defect.
For test changes or provider claims, use [docs/testing.md](docs/testing.md). Select applicable [boundary regressions](docs/testing.md#boundary-regressions) for parser, inventory, snapshot, and projection fixes. Prefer real public CLI/MCP workflows, then production-boundary integration, then producer goldens. Keep module tests for distinct semantics stronger workflows cannot reliably reproduce. Preserve malformed-input, cancellation, cleanup, permission, capacity, and target-identity coverage when pruning. Report unverified coverage explicitly; simulated providers do not establish real-provider behavior.
Use Conventional Commit subjects and PR titles. Mark breaking changes with `!` or a `BREAKING CHANGE:` footer. Describe behavior changes, relevant validation, and real-provider verification scope.
@@ -104,11 +124,12 @@ distinguish source-derived conclusions, proposed checks, and unavailable coverag
Address actionable automated review findings and explain disagreements.
Leave the template's **Author readiness** declarations unchecked for the human
owner to complete. Never attest to human review on their behalf. Keep the PR in
draft until the human declares the PR initially ready. Later pushes do not
invalidate that declaration or require another human sign-off. Keep the PR body
and affected validation current as work changes. The readiness check validates
the filled declaration; CI and automated review results do not establish that
owner to complete. Never attest to human review on their behalf. Draft status
is optional; preserve the PR's current draft status unless the user asks to change
it. Later pushes do not invalidate the readiness declaration or require another
human sign-off. Keep the PR body and affected validation current as work changes.
The readiness check validates the filled declaration or the author exemption
listed in CONTRIBUTING.md; CI and automated review results do not establish that
human review occurred, maintainer scope approval, or merge permission.
<!-- BEGIN:turborepo-agent-rules -->
+105 -3
View File
@@ -1,5 +1,107 @@
# Changelog
## [6.8.0](https://github.com/morluto/rea/compare/rea-agents-6.7.0...rea-agents-6.8.0) (2026-10-25)
### Highlights
- Build local agent environments from network captures, browser scenarios, or a prompt, then serve derived reads and evidence-gated writes and compare them with fresh application captures.
- Inspect more shipped formats, including GPU code objects, BEAM, Python bytecode, Unity IL2CPP metadata, Godot PCK, PyInstaller, Node.js SEA, devicetree, and additional PE/ELF tables.
- Keep large investigations focused with retained summaries, selected fact queries, configurable recovery limits, and row-oriented CLI exports. Complete Evidence remains available.
### ⚠ BREAKING CHANGES
- **Function analysis:** `analyze_function` now defaults to a retained summary. Set `detail: "complete"` for the previous inline dossier; the CLI retains its complete default and requires `--evidence_output` when selecting a summary. [#5660](https://github.com/morluto/rea/pull/5660). Thanks @CryptoJones, @gensokyo-dev, @RishTheFish21.
- **Accessibility:** Accept JSON numbers and booleans in node `value`, alongside strings and null. Update consumers that previously assumed text-only values. [cc90771](https://github.com/morluto/rea/commit/cc90771af14f045538651615147d8bf86b576549), [#6759](https://github.com/morluto/rea/pull/6759), [#6467](https://github.com/morluto/rea/issues/6467). Thanks @coygeek, @gensokyo-dev, @mvanhorn.
- **JSON Lines:** `--format jsonl` now emits object-collection rows followed by a `$rea: "jsonl/v1"`, `type: "metadata"` record; scalar selections and unsupported reports are refused. Parse control records separately, select a single supported collection when necessary, and use `--format json` for the previous whole-document output. See [JSON Lines framing](https://github.com/morluto/rea/blob/rea-agents-6.8.0/docs/cli.md#json-lines-record-output). [#5629](https://github.com/morluto/rea/pull/5629), [#5487](https://github.com/morluto/rea/issues/5487). Thanks @gensokyo-dev, @jayykernel, @vijaykiran06.
- **Interface Builder:** Keyed-archive graph identities use decimal table UIDs instead of authored `objectID`/`object-id`. Treat authored IDs as attributes and update persisted graph lookups; ibtool and native NIBArchive identities keep their source semantics. [6fbe67d](https://github.com/morluto/rea/commit/6fbe67d66e3d716db69929503a3a232a7ada0860).
- **Frida:** `instrument-with-frida` JSON returns the canonical Evidence directly. Read `normalized_result.cleanup_error` instead of the former top-level wrapper field. [48f700e](https://github.com/morluto/rea/commit/48f700ee35e0988ed973e5486dc9db33f2a5d944).
- **Reconstruction proof:** ID-array proof records no longer establish verified or ready ledgers. Supply implementation/revision bindings and individual claim outcomes; handle inapplicable, unavailable, and failed proof diagnostics. [f8b65e7](https://github.com/morluto/rea/commit/f8b65e78723b1e2169f94dd1226198ea3139707e).
- **Mach-O imports:** Handle a null import `name` when identity is ambiguous; use `raw_name` and `name_status` to distinguish observed expressions from exact symbols. [5d0d243](https://github.com/morluto/rea/commit/5d0d243a45c9132e1aff75d454ab53d86a74cc31), [169512a](https://github.com/morluto/rea/commit/169512a35bb015ff209168fb8bc1c9b31a2c678b).
- **Node.js:** Use a runtime satisfying `^22.23.4 || ^24.22.1 || >=26.12.1`; Node 23 and 25 are unsupported. Upgrade Node before installing or updating REA; the installer does not upgrade it. [#6315](https://github.com/morluto/rea/pull/6315), [#6283](https://github.com/morluto/rea/issues/6283). Thanks @GamerDubz, @gensokyo-dev, @ShikamaruNaaraa.
- **Browser storage:** Historical hashed storage remains readable, but comparisons stay unknown until both captures are recaptured in the new representation. [#6341](https://github.com/morluto/rea/pull/6341), [#6289](https://github.com/morluto/rea/issues/6289). Thanks @coygeek, @gongfpp.
- **Environment serving:** `environment-serve` serves derived state by default, including write tools whose effects are confirmed by recorded evidence. Use `--replay-only` for stateless replay of manifest tools. Read stop-summary `state` instead of `state_evidence_id`. [#5351](https://github.com/morluto/rea/pull/5351), [#6158](https://github.com/morluto/rea/pull/6158). Thanks @earakely-scale, @gensokyo-dev, @kaoru0822-kitauji.
- **MCP schemas:** Input advertisements default to compact. Set `REA_MCP_INPUT_SCHEMA_PROFILE=full` for complete advertised guidance and nested constraints. [#5975](https://github.com/morluto/rea/pull/5975). Thanks @gensokyo-dev.
- **Browser console arguments:** Accept null `argument_types` and `text_capture.status: "unavailable"` when the protocol argument inventory is omitted or malformed; preserve that uncertainty. [#6016](https://github.com/morluto/rea/pull/6016), [#5934](https://github.com/morluto/rea/issues/5934). Thanks @coygeek, @kaoru0822-kitauji.
- **Reconstruction summaries:** `summary.by_evidence_authority` is keyed by Evidence authority; read the former source-state counts from required `summary.by_source_state`. Regenerate ledgers whose closure digests depended on the old projection. [#5875](https://github.com/morluto/rea/pull/5875), [#5838](https://github.com/morluto/rea/issues/5838). Thanks @coygeek, @kaoru0822-kitauji.
- **Swift bindings:** Conformance `type_name` and `protocol_name` may be null; consume typed `type_binding`/`protocol_binding` evidence. Replace the former Objective-C-specific import-binding type/schema with `MachoImportBinding`. [d0067ba](https://github.com/morluto/rea/commit/d0067ba34a39ea47dd7e11bb2e6169bc0938cc3f).
- **WebSocket frames:** Accept null opcodes and preserve unknown frame facts; numeric opcodes are restricted to the four-bit protocol range. [0bc2b42](https://github.com/morluto/rea/commit/0bc2b42001f684beffcf47b70e507443b1a5d320).
- **Resource-only inspection:** Evidence subjects and executable digests may be null. Use `resource_root` and each resource's reported provenance instead of requiring an executable target. [6a6adf7](https://github.com/morluto/rea/commit/6a6adf703fa44100643947d20fbc670f86260b1b).
- **Browser timestamps:** Console and session-timeline timestamps may be null when the protocol supplies no finite/comparable time. Keep unknown time explicit in sorting and comparisons. [690fc51](https://github.com/morluto/rea/commit/690fc518540bae982038a74f47ef7781201785aa), [2a0410f](https://github.com/morluto/rea/commit/2a0410f1717b3126e8a95063d77dea66a597010a).
- **Reference sources:** Read failed root enumeration from `rootFailure` rather than a synthetic failed child at `"."`; preserve the top-level source-graph limitation. [6296fdf](https://github.com/morluto/rea/commit/6296fdf560f1ac4aa55985558edaa98300aacdbc).
- **Filesystem capture:** Accept `entry_unavailable` and `identity_changed` enumeration omission reasons in addition to the previous reason literals. [c85b44e](https://github.com/morluto/rea/commit/c85b44e1b14851c54e65327a4807f79ad5cd5a61). Thanks @rudrakshagarwal4200-ai.
- **APK locales:** Replace numeric legacy selectors such as `es-r419` with `es-419` or `b+es+419`. Two-letter regions remain supported. [29910d1](https://github.com/morluto/rea/commit/29910d16b98fe4b607a7aa15928789a73614b0b9). Thanks @mertkayacs.
- **XML plist scalars:** Recognize `$plist_type: "unknown"` and read `element`, `literal`, and `reason`; unresolved XML scalars use this wrapper instead of the former real/null unknown marker or malformed scalar prefixes. Supported extended UTC years preserve their timestamp. [3537e58](https://github.com/morluto/rea/commit/3537e58c1ab29309f06eabf50bc52adba9817a0c).
### Changes
- **Binary layouts:** Inspect ELF32 i386, little-endian ARM, and little- or big-endian MIPS `EXEC`/`DYN`/`REL` layouts on Linux x64 using a caller-supplied pwntools environment. [#7052](https://github.com/morluto/rea/pull/7052), [#6934](https://github.com/morluto/rea/issues/6934). Thanks @roblennon, @salmanalmutairi1.
- **Agent environments:** Project network captures and Evidence files into manifests, preserve template path pairs and positions, export AgentEnv bundles, and serve manifests as replay environments. [#4954](https://github.com/morluto/rea/pull/4954), [#5293](https://github.com/morluto/rea/pull/5293), [#5664](https://github.com/morluto/rea/pull/5664), [#4999](https://github.com/morluto/rea/pull/4999), [#4975](https://github.com/morluto/rea/pull/4975). Thanks @earakely-scale, @gensokyo-dev, @kaoru0822-kitauji.
- **Capture to environment:** Build environments from browser scenarios or a prompt, extract the initial observed state, and compare the environment with application probe captures. [#5182](https://github.com/morluto/rea/pull/5182), [#6946](https://github.com/morluto/rea/pull/6946), [#5319](https://github.com/morluto/rea/pull/5319), [#5699](https://github.com/morluto/rea/pull/5699). Thanks @earakely-scale, @gensokyo-dev, @kaoru0822-kitauji.
- **State-backed tools:** Render read filters, search, sorting and paging from state; apply confirmed write effects with broader state targets and recording-based write admission. [#5352](https://github.com/morluto/rea/pull/5352), [293832b](https://github.com/morluto/rea/commit/293832bafc9a054914ec514fe906dd4936c7a372), [#5800](https://github.com/morluto/rea/pull/5800), [#5787](https://github.com/morluto/rea/pull/5787), [#5600](https://github.com/morluto/rea/pull/5600), [5375ff0](https://github.com/morluto/rea/commit/5375ff0f9aadb4fd0d46333815aa0431e3df1010), [#6169](https://github.com/morluto/rea/pull/6169), [#6385](https://github.com/morluto/rea/pull/6385), [#6897](https://github.com/morluto/rea/pull/6897). Thanks @earakely-scale, @gensokyo-dev, @kaoru0822-kitauji.
- **State learning:** Model application-assigned values, preserve view-specific clock spellings, learn write clocks from scenario timing, derive fields, and learn creates across sorted arrays and referenced entities. [7ec9b6e](https://github.com/morluto/rea/commit/7ec9b6e56e1b3762285dca881db6d900a389ab9c), [#6136](https://github.com/morluto/rea/pull/6136), [957bcfa](https://github.com/morluto/rea/commit/957bcfa149c1a0593bb0aa2466cc56d1d4f62e7d), [#6138](https://github.com/morluto/rea/pull/6138), [#6967](https://github.com/morluto/rea/pull/6967), [#7025](https://github.com/morluto/rea/pull/7025), [#7034](https://github.com/morluto/rea/pull/7034). Thanks @earakely-scale.
- **Recorded writes:** Serve creates whose compatible read-backs add omitted fields or express timestamps at a different precision, and whole-object updates that regenerate observed slugs or modification times. Conflicting observations still withhold writes. State views and bundles carrying the new read-back data need REA 6.8.0 or later to serve. [#7080](https://github.com/morluto/rea/pull/7080). Thanks @earakely-scale.
- **Native investigations:** Page native inventory summaries, match functions across builds, export pseudocode to resumable directories, and select objdump disassembly by symbol. [#5378](https://github.com/morluto/rea/pull/5378), [#5390](https://github.com/morluto/rea/pull/5390), [#6170](https://github.com/morluto/rea/pull/6170), [#5171](https://github.com/morluto/rea/pull/5171), [#4249](https://github.com/morluto/rea/issues/4249), [#5343](https://github.com/morluto/rea/issues/5343), [#5159](https://github.com/morluto/rea/issues/5159). Thanks @coygeek, @CryptoJones, @gensokyo-dev, @kaoru0822-kitauji.
- **Engine workflows:** Bound a single Ghidra decompilation without closing its session, analyze GameCube/Wii DOL executables, and inspect static CUDA/HIP code objects. [#5662](https://github.com/morluto/rea/pull/5662), [#6240](https://github.com/morluto/rea/pull/6240), [#5813](https://github.com/morluto/rea/pull/5813), [#5340](https://github.com/morluto/rea/issues/5340), [#5345](https://github.com/morluto/rea/issues/5345). Thanks @arielfikru, @coygeek, @gensokyo-dev, @RishTheFish21.
- **PE/ELF:** Inspect export tables without an engine, PE imports/delay-loads, Authenticode certificates, TLS callbacks/load configuration, ELF notes, Linux kernel module metadata, and Zstandard section-name tables. [#6278](https://github.com/morluto/rea/pull/6278), [#6509](https://github.com/morluto/rea/pull/6509), [#4830](https://github.com/morluto/rea/pull/4830), [#7008](https://github.com/morluto/rea/pull/7008), [#4837](https://github.com/morluto/rea/pull/4837), [#7010](https://github.com/morluto/rea/pull/7010), [#6987](https://github.com/morluto/rea/pull/6987), [#4057](https://github.com/morluto/rea/issues/4057). Thanks @gensokyo-dev, @kaoru0822-kitauji, @mvanhorn, @Rinkia, @ShuaixinHuang.
- **Apple/native metadata:** Inspect IPS crash reports, decode Swift nominal declarations, and select universal Mach-O slices in Go/Rust metadata readers. [#6511](https://github.com/morluto/rea/pull/6511), [#6257](https://github.com/morluto/rea/pull/6257), [#6219](https://github.com/morluto/rea/pull/6219), [#5341](https://github.com/morluto/rea/issues/5341), [#4102](https://github.com/morluto/rea/issues/4102). Thanks @coygeek, @gensokyo-dev, @kaoru0822-kitauji, @mvanhorn, @Rinkia.
- **Packaged applications:** Triage desktop installation directories and inspect/extract Godot PCK, PyInstaller archives, and Node.js single-executable application blobs. [#5207](https://github.com/morluto/rea/pull/5207), [#3634](https://github.com/morluto/rea/pull/3634), [afa5da7](https://github.com/morluto/rea/commit/afa5da745e0b139e68029a96b776ae30e48ea2b2), [#2433](https://github.com/morluto/rea/pull/2433), [#7000](https://github.com/morluto/rea/pull/7000), [#4399](https://github.com/morluto/rea/issues/4399). Thanks @0xthearchitect, @coygeek, @gensokyo-dev, @mvanhorn.
- **Unity and managed code:** Inventory IL2CPP images/types/methods and metadata versions 24/27; read member names and literals, link native methods and generic instances, and export Ghidra seeds. Project managed call/field-access edges and page managed member summaries. [#3807](https://github.com/morluto/rea/pull/3807), [#5326](https://github.com/morluto/rea/pull/5326), [52d1a72](https://github.com/morluto/rea/commit/52d1a72af047a73b787b8935b032786a34966036), [#5327](https://github.com/morluto/rea/pull/5327), [#5785](https://github.com/morluto/rea/pull/5785), [#6260](https://github.com/morluto/rea/pull/6260), [#5167](https://github.com/morluto/rea/pull/5167), [#3784](https://github.com/morluto/rea/issues/3784), [#5112](https://github.com/morluto/rea/issues/5112), [#4056](https://github.com/morluto/rea/issues/4056), [#5148](https://github.com/morluto/rea/issues/5148). Thanks @8rulerstar, @alexlanghart, @elricfd, @gensokyo-dev, @jaelliot, @kaoru0822-kitauji, @Rinkia.
- **Android:** Inspect APK signing declarations and structured manifest components; report static-final DEX field values. [e93e3b1](https://github.com/morluto/rea/commit/e93e3b1e7d6e7e4f49c2de24b92452ec29e853f8), [#4158](https://github.com/morluto/rea/pull/4158), [#5025](https://github.com/morluto/rea/pull/5025), [#6981](https://github.com/morluto/rea/pull/6981), [#4537](https://github.com/morluto/rea/issues/4537), [#6397](https://github.com/morluto/rea/issues/6397). Thanks @Abelo9996, @BaaRai, @FerroLx, @gensokyo-dev, @sserrgo12.
- **Bytecode and firmware:** Inspect and query CPython pyc and BEAM facts, inspect flattened devicetrees, and attribute MMIO peripheral regions in native disassembly. [#4826](https://github.com/morluto/rea/pull/4826), [#6928](https://github.com/morluto/rea/pull/6928), [#4833](https://github.com/morluto/rea/pull/4833), [#6943](https://github.com/morluto/rea/pull/6943), [#7009](https://github.com/morluto/rea/pull/7009), [#5348](https://github.com/morluto/rea/pull/5348), [#6922](https://github.com/morluto/rea/issues/6922), [#6938](https://github.com/morluto/rea/issues/6938). Thanks @gensokyo-dev, @liziye627-design, @mvanhorn.
- **JavaScript:** Retain queryable static route inventories, link assignment values to writes, and accept source selections as a JSON array or file. [#6019](https://github.com/morluto/rea/pull/6019), [#6206](https://github.com/morluto/rea/pull/6206), [#5029](https://github.com/morluto/rea/pull/5029), [#4772](https://github.com/morluto/rea/issues/4772), [#6193](https://github.com/morluto/rea/issues/6193). Thanks @gensokyo-dev, @IvenKooLab, @jacobmalibiran, @JoshOsullivan-au, @kaoru0822-kitauji, @KJLavender.
- **Data inspection:** Decode selected protobuf/gRPC bodies, inspect GGUF/safetensors declarations, and produce byte-entropy profiles. [#5031](https://github.com/morluto/rea/pull/5031), [#5603](https://github.com/morluto/rea/pull/5603), [#2331](https://github.com/morluto/rea/pull/2331), [#4986](https://github.com/morluto/rea/issues/4986), [#3323](https://github.com/morluto/rea/issues/3323). Thanks @AayushJB03, @gensokyo-dev, @kaoru0822-kitauji, @KJLavender, @michaelkillgta, @mvanhorn.
- **CLI input/output:** Accept structured request documents on stdin and expose version/runtime output formats. Row-oriented JSONL exports use the migration above. [#7040](https://github.com/morluto/rea/pull/7040), [#5486](https://github.com/morluto/rea/issues/5486), [#6530](https://github.com/morluto/rea/pull/6530), [169512a](https://github.com/morluto/rea/commit/169512a35bb015ff209168fb8bc1c9b31a2c678b), [#5483](https://github.com/morluto/rea/issues/5483). Thanks @Abelo9996, @ahmed-husssain, @CassieFox23, @DeryFerd, @eco-null, @ezrabrilliant, @GamerDubz, @gensokyo-dev, @gongfpp, @hifzabuildsai, @jayykernel, @mhamzasakhi, @pablodiazjorge, @Plastic-time, @raidnouredden-ux, @SandstromPL, @ShuaixinHuang, @Sriram-PR, @XiaoBinGan.
- **MCP and setup:** Add opt-in Streamable HTTP and FreeBuff setup registration. FreeBuff support first appears in 6.8.0; installed native FreeBuff loading and skill discovery remain unverified. [#4955](https://github.com/morluto/rea/pull/4955), [#2736](https://github.com/morluto/rea/pull/2736), [#3243](https://github.com/morluto/rea/issues/3243). Thanks @0xLLVEL, @codebuff-team, @earakely-scale, @gensokyo-dev, @kaoru0822-kitauji.
- **Recovery controls:** Adjust per-call WASM/SQLite/firmware deadlines, MCP startup budgets, regex matching deadlines/worker heap, capture decoder heap, ASAR header admission, Apple dispatch input, and the bridge request ceiling. MCP runtime limit recovery reports actionable settings. [#5798](https://github.com/morluto/rea/pull/5798), [#5818](https://github.com/morluto/rea/pull/5818), [aa53c06](https://github.com/morluto/rea/commit/aa53c065387c58b4189d82c47f1f3445fc505e30), [#5863](https://github.com/morluto/rea/pull/5863), [#6304](https://github.com/morluto/rea/pull/6304), [#6267](https://github.com/morluto/rea/pull/6267), [#6301](https://github.com/morluto/rea/pull/6301), [#6299](https://github.com/morluto/rea/pull/6299), [#6209](https://github.com/morluto/rea/pull/6209), [#6233](https://github.com/morluto/rea/pull/6233), [#6376](https://github.com/morluto/rea/pull/6376), [#7049](https://github.com/morluto/rea/pull/7049), [#5797](https://github.com/morluto/rea/issues/5797), [#5812](https://github.com/morluto/rea/issues/5812), [#5851](https://github.com/morluto/rea/issues/5851), [#6176](https://github.com/morluto/rea/issues/6176), [#3917](https://github.com/morluto/rea/issues/3917). Thanks @gensokyo-dev, @jaychou813, @kaoru0822-kitauji.
- **APK output:** Set `string_tables_only` or CLI `--string-tables-only` to omit duplicate primary strings and avoid their output-capacity charge. [7c458f0](https://github.com/morluto/rea/commit/7c458f04b9155707c8ba406f8b455b10016f1b99), [#4990](https://github.com/morluto/rea/pull/4990), [#4982](https://github.com/morluto/rea/issues/4982). Thanks @gensokyo-dev, @kaoru0822-kitauji, @N0zoM1z0.
- **Retained analysis:** Reuse successful validation of authenticated immutable native/imported-JavaScript parents and captures. Query retained Go build identity, Rust dependencies, JavaScript source-to-bundle mappings, and native export facts without repeating full output. [8f7d12e](https://github.com/morluto/rea/commit/8f7d12e30d63f3d3b4cfe2a90336362841438f56), [#4962](https://github.com/morluto/rea/pull/4962), [#5349](https://github.com/morluto/rea/pull/5349), [#5524](https://github.com/morluto/rea/pull/5524), [#5815](https://github.com/morluto/rea/pull/5815), [#6128](https://github.com/morluto/rea/pull/6128), [#6907](https://github.com/morluto/rea/pull/6907), [#4810](https://github.com/morluto/rea/issues/4810), [#5499](https://github.com/morluto/rea/issues/5499), [#5802](https://github.com/morluto/rea/issues/5802), [#6112](https://github.com/morluto/rea/issues/6112), [#6900](https://github.com/morluto/rea/issues/6900). Thanks @gensokyo-dev, @kaoru0822-kitauji, @N0zoM1z0.
- **Compact results:** Use retained Frida/GDB/objdump summaries, omit redundant Ghidra raw results, and defer the analysis runtime during MCP catalog discovery. [#5661](https://github.com/morluto/rea/pull/5661), [#5614](https://github.com/morluto/rea/pull/5614), [#5355](https://github.com/morluto/rea/pull/5355), [#4424](https://github.com/morluto/rea/pull/4424), [#5640](https://github.com/morluto/rea/issues/5640), [#5584](https://github.com/morluto/rea/issues/5584), [#5337](https://github.com/morluto/rea/issues/5337). Thanks @coygeek, @gensokyo-dev, @kaoru0822-kitauji, @leeguooooo.
- **Analysis work:** Reuse Hopper literal-string records, JavaScript workers and captured prefix selections, Interface Builder validation and plist scalar schemas; bound stable reads to one snapshot buffer and avoid decoding omitted GGUF strings. [#5017](https://github.com/morluto/rea/pull/5017), [#5819](https://github.com/morluto/rea/pull/5819), [31de177](https://github.com/morluto/rea/commit/31de177404461d122761d8243abe4fd24564f4da), [#5885](https://github.com/morluto/rea/pull/5885), [85e38ac](https://github.com/morluto/rea/commit/85e38ac199dff15e8c43343d9170db7e0e46d284), [#6966](https://github.com/morluto/rea/pull/6966), [#5781](https://github.com/morluto/rea/pull/5781), [#5010](https://github.com/morluto/rea/issues/5010), [#5855](https://github.com/morluto/rea/issues/5855), [#6954](https://github.com/morluto/rea/issues/6954), [#5774](https://github.com/morluto/rea/issues/5774). Thanks @gensokyo-dev, @kaoru0822-kitauji.
- **Native scheduling:** Batch modern IDA callee lookups, schedule decompilation reads on demand, index NativeAOT type closure, and retain call-path ancestry once per visited node. [0c6fed9](https://github.com/morluto/rea/commit/0c6fed90b74c75a9fd23d1ec1b510a7274f85887), [06fe7ee](https://github.com/morluto/rea/commit/06fe7eecbc8c387414b4c1aa0d88ad6314f54f0c), [ce61680](https://github.com/morluto/rea/commit/ce61680c04c95a9cdfad5335632a99542af26d37), [5e4ec0f](https://github.com/morluto/rea/commit/5e4ec0fa1b678aa3bf20704ec9110224ba8c1676).
- **Selected processing:** Prepare CLI positional schemas only for the selected command, separate entropy reads from report resolution, prune typed-array reference escapes, index state-write cutoffs, decode only selected replay query values, and skip payload text during WAT framing. [315b234](https://github.com/morluto/rea/commit/315b23403f0819967878cee418c3f9cb4b1a69d9), [#6950](https://github.com/morluto/rea/pull/6950), [#5139](https://github.com/morluto/rea/pull/5139), [#5350](https://github.com/morluto/rea/pull/5350), [96ea32b](https://github.com/morluto/rea/commit/96ea32b7e4e28d3c9cd79a1de4b8e1ba3b4244e9), [70835d3](https://github.com/morluto/rea/commit/70835d3d5f030deadf606e1c79c70a60883e50c9), [#5766](https://github.com/morluto/rea/pull/5766), [#6947](https://github.com/morluto/rea/issues/6947), [#5760](https://github.com/morluto/rea/issues/5760). Thanks @galleonli, @gensokyo-dev, @kaoru0822-kitauji.
### Fixes
- **IPS report fields:** Preserve the recorded terminating process and PID, termination flags, parent PID, and build/platform metadata. [#7079](https://github.com/morluto/rea/pull/7079), [#7078](https://github.com/morluto/rea/issues/7078). Thanks @rodricksz4h5.
- **Ghidra language selection:** Accept stock language IDs containing `LEBE` or slash-separated processor names, and report conflicting DOL language overrides as configuration errors naming `REA_GHIDRA_LANGUAGE_ID`. [#7070](https://github.com/morluto/rea/pull/7070), [#7072](https://github.com/morluto/rea/issues/7072), [#7081](https://github.com/morluto/rea/pull/7081), [#7082](https://github.com/morluto/rea/issues/7082). Thanks @Adolanium.
- **Setup and Hopper:** Preserve the Linux embedding runtime, supported x86_64 host restriction, readiness-based provider choices, orderly demo-helper shutdown, and installed apt dependency versions. Keep cleanup and skill publication ownership through retries. [#5041](https://github.com/morluto/rea/pull/5041), [#3422](https://github.com/morluto/rea/pull/3422), [f5d1012](https://github.com/morluto/rea/commit/f5d1012fabfe99af82523c3078ce9d93eb3780c1), [#4966](https://github.com/morluto/rea/pull/4966), [#6974](https://github.com/morluto/rea/pull/6974), [211e353](https://github.com/morluto/rea/commit/211e3533b34f81ae0910ad4f53f2a3d3c8587d3e), [#6920](https://github.com/morluto/rea/pull/6920), [#5015](https://github.com/morluto/rea/issues/5015), [#3419](https://github.com/morluto/rea/issues/3419), [#4921](https://github.com/morluto/rea/issues/4921), [#6975](https://github.com/morluto/rea/issues/6975). Thanks @Abel-I-Akhigbe, @Adolanium, @DavidFB-creator, @DeryFerd, @gensokyo-dev, @kaoru0822-kitauji, @sx4im.
- **Client setup:** Treat empty APPDATA as unset, distinguish MSIX Claude Desktop process views, preserve aliased Hermes server tables, reuse client dialect/alias bindings, and materialize the pinned package-runner cache before the first start. [#5126](https://github.com/morluto/rea/pull/5126), [#4903](https://github.com/morluto/rea/pull/4903), [#3785](https://github.com/morluto/rea/pull/3785), [b7eeded](https://github.com/morluto/rea/commit/b7eededf5af5061a834f93f57f2c888acf870344), [#7032](https://github.com/morluto/rea/pull/7032), [#5104](https://github.com/morluto/rea/issues/5104), [#4278](https://github.com/morluto/rea/issues/4278), [#3320](https://github.com/morluto/rea/issues/3320), [#7027](https://github.com/morluto/rea/issues/7027). Thanks @CmsChase, @coygeek, @myso-kr, @pablodiazjorge, @vaidehi-snk, @wxj006007.
- **Target admission:** Preserve thin arm64_32 Mach-O support, distinguish JVM identity before FAT admission, validate FAT format/CPU identity, preserve original DOS header padding, and reject NULs in local objdump/rizin paths. [#5788](https://github.com/morluto/rea/pull/5788), [6843dbe](https://github.com/morluto/rea/commit/6843dbea9bce4709ee2e8bf46c38f0bbf640716b), [497037e](https://github.com/morluto/rea/commit/497037e6e3975cbb37cfd89ea0ad300faa2a4769), [#7013](https://github.com/morluto/rea/pull/7013), [#3600](https://github.com/morluto/rea/pull/3600), [#5713](https://github.com/morluto/rea/issues/5713), [#6510](https://github.com/morluto/rea/issues/6510). Thanks @coygeek, @dhruvac29, @kaoru0822-kitauji, @Lew1sWong, @qorvr.
- **Mach-O and dylibs:** Preserve container framing, facts when dyld rejects a missing UUID, verified-signature snapshot identity, unsigned diagnostics, absolute paths inside the selected root, legacy platform rules, lazy-load payloads, and reasons for rejecting present dylibs. [cf3288d](https://github.com/morluto/rea/commit/cf3288d8cf7a09685acf89103b36c32359be56fa), [2b263a7](https://github.com/morluto/rea/commit/2b263a7fe9e9d4c185c7d6af6b4e67b21a73af30), [eb7acc7](https://github.com/morluto/rea/commit/eb7acc70bf06f119e8639e30a78081d5ba82b0bf), [1502cfb](https://github.com/morluto/rea/commit/1502cfbc8ca0d1d1d86eebf334018dfb418bbac0), [90062c0](https://github.com/morluto/rea/commit/90062c092228dea9d33fadc0d7e1b968105b8b2c), [#5650](https://github.com/morluto/rea/pull/5650), [3b67b36](https://github.com/morluto/rea/commit/3b67b362aa2cd79eac5af57d0965e81a7d0b8c2b), [#5551](https://github.com/morluto/rea/issues/5551). Thanks @coygeek.
- **Native facts:** Admit direct dispatch-metadata providers; preserve record-local Objective-C omissions, snapshot profiles, class-import binding identity, stripped class labels, version-like names, and qualified Swift protocol identities. [50b6975](https://github.com/morluto/rea/commit/50b6975d861069e2c6cfa202780621449f863802), [9eec57b](https://github.com/morluto/rea/commit/9eec57b469087fe9bff3d29dcc02f47074159e91), [0ba4d6f](https://github.com/morluto/rea/commit/0ba4d6f43ad610dacc861a1548be1a5c4712cd61), [4d7de00](https://github.com/morluto/rea/commit/4d7de00d4cc9a95ec2b7a3123b04e01db2daafa0), [22c84ce](https://github.com/morluto/rea/commit/22c84ce64aa6c7601aa81af86d4117b5a4c2feac), [4852c83](https://github.com/morluto/rea/commit/4852c830af1e3b9df437235b7a8fa8970a6d1c59), [aa9043f](https://github.com/morluto/rea/commit/aa9043f6b61c453e4735795cfe6a559bf6b6cc16).
- **Apple resources:** Preserve independent plist scalars, processing instructions, nonstandard table widths, unsupported NIB connector coverage, and strict IPS JSON surrogate validation; classify Interface Builder objects by their declared superclass chains. [fd728f2](https://github.com/morluto/rea/commit/fd728f271e7de7bcfa63890b573715720d5f948c), [#5806](https://github.com/morluto/rea/pull/5806), [4526862](https://github.com/morluto/rea/commit/4526862728af01f1135714079c0d8a8c754eb364), [14cd78c](https://github.com/morluto/rea/commit/14cd78c3224edcf79fca152915077112f547b9ed), [#6990](https://github.com/morluto/rea/pull/6990), [#5805](https://github.com/morluto/rea/issues/5805), [#5228](https://github.com/morluto/rea/pull/5228), [#5218](https://github.com/morluto/rea/issues/5218). Thanks @coygeek, @DeryFerd, @kaoru0822-kitauji, @Mangolycheematcha, @mhamzasakhi, @Shreyash021104.
- **Archives and files:** Complete positional ZIP reads, preserve bounded ASAR signatures, report stable symlink loops as resolution failures, retain target applicability, and report unknown traversal coverage. Retain reported integrity contradictions for members present in only one compared artifact. [#4414](https://github.com/morluto/rea/pull/4414), [e8da7e1](https://github.com/morluto/rea/commit/e8da7e1edd075feec0e6beedec93d5645fed8125), [#5571](https://github.com/morluto/rea/pull/5571), [#5763](https://github.com/morluto/rea/pull/5763), [#5867](https://github.com/morluto/rea/pull/5867), [#5521](https://github.com/morluto/rea/issues/5521), [#5731](https://github.com/morluto/rea/issues/5731), [#5837](https://github.com/morluto/rea/issues/5837), [#7076](https://github.com/morluto/rea/pull/7076). Thanks @Afloat16, @coygeek, @jdotc1, @kaoru0822-kitauji, @WenyuChiou.
- **Extraction and file ownership:** Restrict destination-case recovery to owned collisions, retain physical-read failures through late cancellation/cleanup, preserve cleanup capabilities for retry, and retain independent firmware extraction failures. [932fbb7](https://github.com/morluto/rea/commit/932fbb7d84a9a995b9aee3c1be7da05a8c4d7bbb), [9499667](https://github.com/morluto/rea/commit/949966712e68d8857a50a165ecddf04c2d7e6d3b), [56679dd](https://github.com/morluto/rea/commit/56679dd2ae31dda58e181f8bf99775014f7bb82b), [b3c912a](https://github.com/morluto/rea/commit/b3c912ab79ad147a37b6cfbe1552a1a5eecec629), [7298bd0](https://github.com/morluto/rea/commit/7298bd08d134860b84b4073f6703021916a5a4c5).
- **Processes and cancellation:** Retain partial samples and shutdown gaps, unresolved descendants and caller abort reasons; bind cleanup errors to their owners, propagate comparison contradictions, and report no normal exit code for signalled targets. [ba866a2](https://github.com/morluto/rea/commit/ba866a26ecbe179a717e746cbc0d1605ae616ad5), [#5270](https://github.com/morluto/rea/pull/5270), [#6144](https://github.com/morluto/rea/pull/6144), [a721c42](https://github.com/morluto/rea/commit/a721c42c07dc674c32ab939627355b8a1bee97a5), [#6298](https://github.com/morluto/rea/pull/6298), [#6486](https://github.com/morluto/rea/pull/6486), [#5935](https://github.com/morluto/rea/issues/5935), [#5944](https://github.com/morluto/rea/issues/5944), [#6100](https://github.com/morluto/rea/issues/6100), [#6101](https://github.com/morluto/rea/issues/6101), [#6110](https://github.com/morluto/rea/issues/6110), [#6113](https://github.com/morluto/rea/issues/6113), [#6121](https://github.com/morluto/rea/issues/6121), [#6131](https://github.com/morluto/rea/issues/6131), [#6132](https://github.com/morluto/rea/issues/6132), [#6218](https://github.com/morluto/rea/issues/6218), [#6445](https://github.com/morluto/rea/issues/6445). Thanks @agorevski, @coygeek, @gongfpp, @kwakhyun, @mhamzasakhi.
- **Provider cleanup:** Preserve Windows native handles until close is confirmed, native/Inspector/Electron cleanup ownership, Kuna probe identity and cleanup, IDA registration configuration, and Hopper preparation ownership. [#5772](https://github.com/morluto/rea/pull/5772), [87ab054](https://github.com/morluto/rea/commit/87ab054ea384b3fcbae2998b1f94685edcd5f624), [ff78165](https://github.com/morluto/rea/commit/ff78165978de9dbcf9fa16c2acf74bf85a69dc44), [04ae07a](https://github.com/morluto/rea/commit/04ae07ab8c9ae1a6697b6ad15489bbde515da8b5), [dd0015a](https://github.com/morluto/rea/commit/dd0015aaf156c5701ef9598bcc6c885e6b8da803), [b231c28](https://github.com/morluto/rea/commit/b231c28cf64c2f799ada99343f8f21e0e6acb15e), [#4505](https://github.com/morluto/rea/issues/4505), [#5616](https://github.com/morluto/rea/issues/5616).
- **Provider diagnostics:** Give Ghidra restart recovery, stable Homebrew aliases, unsupported-host results for Linux-only integrations, correct IDA regex/readiness handling, and clear diagnostics when Windows refuses a verified native addon. [#5356](https://github.com/morluto/rea/pull/5356), [7c6142e](https://github.com/morluto/rea/commit/7c6142efdc19a42c148d0e8c96b69d18e63c7f87), [#5793](https://github.com/morluto/rea/pull/5793), [#4862](https://github.com/morluto/rea/pull/4862), [99ccfb6](https://github.com/morluto/rea/commit/99ccfb6e709ac4bc0afe176315280abde9ebcb9f), [#6832](https://github.com/morluto/rea/pull/6832), [#5730](https://github.com/morluto/rea/issues/5730), [#4860](https://github.com/morluto/rea/issues/4860), [#5723](https://github.com/morluto/rea/issues/5723). Thanks @coygeek, @dark13b, @gensokyo-dev, @jgt87, @jpmorris41488, @Lew1sWong.
- **MCP results:** Preserve protocol envelopes on oversized errors, enforce and advertise absolute Evidence paths, retain typed output-filter errors and selected JSON paths, and give client/provider timeout recovery. Compact advertisements now normalize reported enum and union shapes for strict gateway translation. [562dab0](https://github.com/morluto/rea/commit/562dab00c5cbf76a9b2695bad93792eccdc9a49d), [3cf6206](https://github.com/morluto/rea/commit/3cf6206614e65b5ebb6284a5709090dfb8f9aaee), [0e83d92](https://github.com/morluto/rea/commit/0e83d92c583ae36afe00b2c23dd2a0f1d025cbca), [4714c7e](https://github.com/morluto/rea/commit/4714c7eaa3d1d8b6a5df0aff586e844b9d9ab06d), [1bfa60a](https://github.com/morluto/rea/commit/1bfa60ac20e1a344bcf3132c08d15b6860b05c61), [#5608](https://github.com/morluto/rea/pull/5608), [#3806](https://github.com/morluto/rea/pull/3806), [#5598](https://github.com/morluto/rea/issues/5598), [#3800](https://github.com/morluto/rea/issues/3800). Thanks @gensokyo-dev, @Hahaa909, @wang1xiang.
- **Browser lifecycles:** Retain historical outcomes through cleanup, stop late navigation before session release, stop expired work, observe aborted fetch settlement, and mark later scenario actions skipped after failure or cancellation. [2e5171e](https://github.com/morluto/rea/commit/2e5171e391b83f8a467d0861fd452c1f78621fd8), [599fdac](https://github.com/morluto/rea/commit/599fdac029912f96c511f859660483405d2de26f), [b2fad36](https://github.com/morluto/rea/commit/b2fad362f2b37f81dc9a6f784f4521e4ca24d41e), [13a8640](https://github.com/morluto/rea/commit/13a864020414577006dce58d7fdaa877ddd0cb52), [#6023](https://github.com/morluto/rea/pull/6023), [#5943](https://github.com/morluto/rea/issues/5943). Thanks @coygeek, @kaoru0822-kitauji.
- **Browser comparisons:** Preserve frame identity, default origin scope, unknown body/shape coverage, correspondence and uncertainty, and independent retained-observation budgets. [#4040](https://github.com/morluto/rea/pull/4040), [#4413](https://github.com/morluto/rea/pull/4413), [#4476](https://github.com/morluto/rea/pull/4476), [a0c22ff](https://github.com/morluto/rea/commit/a0c22ff0125b8af7612db54d3708413e2bfecb25), [a0fcc60](https://github.com/morluto/rea/commit/a0fcc60d89eb0cc09b08181eb9f961acfd07d8e9), [#3878](https://github.com/morluto/rea/issues/3878), [#3910](https://github.com/morluto/rea/issues/3910), [#3941](https://github.com/morluto/rea/issues/3941), [#4385](https://github.com/morluto/rea/issues/4385), [#4477](https://github.com/morluto/rea/issues/4477). Thanks @Adolanium, @coygeek, @eco-null, @gensokyo-dev, @kaoru0822-kitauji, @XY-code1.
- **Electron observations:** Preserve listener callback ownership and count registrations by source callsite; block trash calls in the active hook, retain window handles through cancelled selection, and preserve cancellation during finalization. [dfe8378](https://github.com/morluto/rea/commit/dfe8378cf5a727e3d93d47a5986233175c6ef898), [5064007](https://github.com/morluto/rea/commit/506400748eb1d6c674f0cf51330073929c45f939), [816a9dc](https://github.com/morluto/rea/commit/816a9dc98886e6d824cd8ae88b68731b161d5dda), [1707d17](https://github.com/morluto/rea/commit/1707d171d750955bf6387e028e378183b360ffe3), [4e2a566](https://github.com/morluto/rea/commit/4e2a566d6c7e053ecbbee1b684311ed31ea163bf).
- **JavaScript source maps:** Keep unmapped and missing mappings unknown, preserve partial indexed-map facts and section paths, propagate preparation capability errors, and retain observed worker failures through cancellation. [a4dbb36](https://github.com/morluto/rea/commit/a4dbb36d8ea83e9f94dd289ac1a2d220d2c72c9b), [2b2e362](https://github.com/morluto/rea/commit/2b2e362a00fc5ba2f93fa6c62e2d8780072d1b4b), [498a667](https://github.com/morluto/rea/commit/498a667234b0140c1cfa43c819b35454ab11d14e), [#6149](https://github.com/morluto/rea/pull/6149), [#6214](https://github.com/morluto/rea/pull/6214), [f01dcca](https://github.com/morluto/rea/commit/f01dccab77f712fb4ac936d0a6e92a70d12f0789), [#6082](https://github.com/morluto/rea/issues/6082), [#6061](https://github.com/morluto/rea/issues/6061). Thanks @coygeek, @diyasharma05, @kaoru0822-kitauji.
- **JavaScript semantics:** Preserve overflowing timer uncertainty, unavailable-scope comparisons, directory-form URL/ESM path intent, and artifact confinement. Replaced globals lose built-in effects; erased imports no longer become runtime bindings. Treat a known truthy `fork()` `silent` option as piped stdio unless a string/array `stdio` override takes precedence; unresolved options stay dynamic. [#3456](https://github.com/morluto/rea/pull/3456), [8d198d6](https://github.com/morluto/rea/commit/8d198d6ec707402d75276f516e0090d71767b7e4), [#4646](https://github.com/morluto/rea/pull/4646), [#2700](https://github.com/morluto/rea/pull/2700), [#4565](https://github.com/morluto/rea/pull/4565), [#6994](https://github.com/morluto/rea/pull/6994), [#3400](https://github.com/morluto/rea/issues/3400), [#3882](https://github.com/morluto/rea/issues/3882), [#3367](https://github.com/morluto/rea/issues/3367), [#7083](https://github.com/morluto/rea/pull/7083), [#7084](https://github.com/morluto/rea/issues/7084). Thanks @Adolanium, @amyanger, @coygeek, @gensokyo-dev, @HawkForge, @rohitg00, @viniciusdsandrade.
- **JavaScript routes and imports:** Treat path data as route candidates, preserve route flag and lexical RPC identities, package-main trailing-slash selection, catch aliases, and factory parameter ownership. [#6360](https://github.com/morluto/rea/pull/6360), [38ecc02](https://github.com/morluto/rea/commit/38ecc0248e1993d25a2f7dd350024484cce797bb), [#4219](https://github.com/morluto/rea/pull/4219), [21097b1](https://github.com/morluto/rea/commit/21097b192b1ab94647e3446b1a2a4dd00e924d5e), [f774dc4](https://github.com/morluto/rea/commit/f774dc420b3befe2b8e68971b4116da2d62fc800), [#6349](https://github.com/morluto/rea/issues/6349), [#4189](https://github.com/morluto/rea/issues/4189). Thanks @coygeek, @hemv-857, @kaoru0822-kitauji, @Nikhi00718.
- **Managed/IL2CPP:** Preserve extension-only scope uncertainty and type discovery after member limits, nested names, ReadyToRun/declaration facts, selected type scope, valid shared-suffix strings, and truncated IL2CPP metadata prefixes. [#5674](https://github.com/morluto/rea/pull/5674), [#5312](https://github.com/morluto/rea/pull/5312), [87fa1b6](https://github.com/morluto/rea/commit/87fa1b613d2b6bda1722855a00f1390f27731b64), [bc2d494](https://github.com/morluto/rea/commit/bc2d494030efd1ccabaeaa0bd49226aa87c378a2), [1f0eb22](https://github.com/morluto/rea/commit/1f0eb228e50908ee2a2aaa72d329178ae1a2cdbd), [4c5fae5](https://github.com/morluto/rea/commit/4c5fae5179dd54c4a0d427cd2a115b9c887e3841), [b3aadaf](https://github.com/morluto/rea/commit/b3aadaf474e59b33e5ff465236008a67eccefb87), [#5467](https://github.com/morluto/rea/issues/5467), [#5146](https://github.com/morluto/rea/issues/5146). Thanks @coygeek, @eco-null, @elricfd, @umi008.
- **Format boundaries:** Preserve independent facts across malformed inputs in model, GPU, WASM, Rust, Android, and SQLite inspection; report optional metadata warnings without discarding core results. [eb41f35](https://github.com/morluto/rea/commit/eb41f358cf83d5626f09de68332f9718f9aa6482), [#5789](https://github.com/morluto/rea/pull/5789), [00a9e95](https://github.com/morluto/rea/commit/00a9e95de91a4bd4d2a41f8aee704a53fa575750), [fc63fe8](https://github.com/morluto/rea/commit/fc63fe8682654086261540dcf3c59afc36c7d58f), [39a91d4](https://github.com/morluto/rea/commit/39a91d4db96ca13224c25936330ea84fe78970e5), [6c6e2ad](https://github.com/morluto/rea/commit/6c6e2ad62b20f810b54f5a567e66fc55241514b2), [929a0a7](https://github.com/morluto/rea/commit/929a0a76c870d9991e015dde58308fd9df0280b8), [d137a4c](https://github.com/morluto/rea/commit/d137a4cdde0049de967f1d5cd429ed9e1f7d0a10), [ef475fc](https://github.com/morluto/rea/commit/ef475fcf8b0601c61052b7091568ba574546417d), [#5749](https://github.com/morluto/rea/issues/5749). Thanks @coygeek, @gensokyo-dev.
- **Capture decoding:** Preserve malformed protobuf trailer facts and wire/schema identity, HTTP/2 URI and UTF-8 failure facts, WebMCP property coverage, and offline capture availability without a browser provider. Report invalid record ordinals as selection errors with actual bounds where available, and trim surrounding SP/HTAB from gRPC-web trailer values while preserving internal whitespace. [9cf7a7c](https://github.com/morluto/rea/commit/9cf7a7cb0d3c72a1121fde280cb11bf911af8055), [b3c3b21](https://github.com/morluto/rea/commit/b3c3b21e43cab4e71b460d08829d8c6e4e9b9c21), [4cb1f1d](https://github.com/morluto/rea/commit/4cb1f1d820df4498ee464bcf72a5f47d40cf8dab), [51e5938](https://github.com/morluto/rea/commit/51e593877642ab42fb5bf25812f4a4110ee7ac26), [d23be5c](https://github.com/morluto/rea/commit/d23be5c0898c39c9f0c54e2e0b43ee746d92d316), [#6725](https://github.com/morluto/rea/pull/6725), [#3947](https://github.com/morluto/rea/issues/3947), [#7067](https://github.com/morluto/rea/pull/7067), [#7068](https://github.com/morluto/rea/issues/7068), [#7069](https://github.com/morluto/rea/pull/7069), [#7071](https://github.com/morluto/rea/issues/7071). Thanks @Adolanium, @coygeek, @Sriram-PR.
- **Evidence and errors:** Authenticate ledger Evidence before committing; preserve parent-schema diagnostics, canonical correction details and owned cleanup outcomes; identify the rejected function-Evidence side/kind. Fixed allowances now name their actual limits. [0407bbf](https://github.com/morluto/rea/commit/0407bbfbe65e8fa475e8bb0c78c30170e2c2f55e), [#6361](https://github.com/morluto/rea/pull/6361), [1629266](https://github.com/morluto/rea/commit/1629266e3c08c90fed6bba5383528af7986a12b7), [#6969](https://github.com/morluto/rea/pull/6969), [#5860](https://github.com/morluto/rea/pull/5860), [#6034](https://github.com/morluto/rea/issues/6034), [#5932](https://github.com/morluto/rea/issues/5932), [#5847](https://github.com/morluto/rea/issues/5847). Thanks @AyoubElleuch, @coygeek, @dhruvac29, @kaoru0822-kitauji.
- **ADB and firmware recovery:** Report an unparsed push checksum as unverified, normalize absent installer-package sentinels, describe extraction-relative Unblob links, and avoid recommending a budget increase at its fixed ceiling. [#6010](https://github.com/morluto/rea/pull/6010), [#4073](https://github.com/morluto/rea/pull/4073), [#4573](https://github.com/morluto/rea/pull/4573), [#6971](https://github.com/morluto/rea/pull/6971), [#5924](https://github.com/morluto/rea/issues/5924), [#4175](https://github.com/morluto/rea/issues/4175). Thanks @coygeek, @Ganesh2325, @kaoru0822-kitauji, @Rinkia, @XiaoBinGan, @yankikucuk.
- **CLI formatting:** Share Markdown report formatting, exclude inherited row values, and preserve arrays of empty objects as JSON. Render Markdown table headers and cells literally while keeping intraword underscores readable. [b784912](https://github.com/morluto/rea/commit/b784912811fd10ac9dc425696dc0e9af632afa34), [#6984](https://github.com/morluto/rea/pull/6984), [#7058](https://github.com/morluto/rea/pull/7058), [#7057](https://github.com/morluto/rea/issues/7057), [#7073](https://github.com/morluto/rea/pull/7073). Thanks @Plastic-time, @ShuaixinHuang, @viniciusdsandrade.
- **Format identity:** Preserve Python marshal scalar/decode limits, empty optional BEAM chunks, PE resource branch identities and certificate alignment, unsupported compressed ELF notes, and codec-framed GPU bundle extents. [0603223](https://github.com/morluto/rea/commit/06032237c6755e3dd5f9a8db732c89958b238cc7), [c46064c](https://github.com/morluto/rea/commit/c46064c3f6eaba121c5de7b17390bfdecdd132ef), [66671e8](https://github.com/morluto/rea/commit/66671e874888e98f7e2b27e68a7c6fb320bbb827), [03077d5](https://github.com/morluto/rea/commit/03077d54afc49613db4e38f70c506bc60f5223aa), [#6721](https://github.com/morluto/rea/pull/6721), [9fb8c11](https://github.com/morluto/rea/commit/9fb8c114a03ad60e57c11586967c6df845a3f97e), [f6a1e2c](https://github.com/morluto/rea/commit/f6a1e2c892dc23c7583c3d03af9edb01632ff5a5), [#6694](https://github.com/morluto/rea/issues/6694). Thanks @coygeek, @Deepak-Kambala.
- **WASM and APK metadata:** Preserve ambiguous WASM identity and partial facts, glue URL query/fragment text across line terminators, APK signer identity, and raw issuer identity when comparison rules are unknown. [af8f0ba](https://github.com/morluto/rea/commit/af8f0badf64fd3546392b6aa4625777904b2f051), [#6802](https://github.com/morluto/rea/pull/6802), [f783f0f](https://github.com/morluto/rea/commit/f783f0f1e82472cdddd4de2156d974f729e332a6), [115a78f](https://github.com/morluto/rea/commit/115a78fe241276c34917b496621954c5f29bfc15), [#6639](https://github.com/morluto/rea/issues/6639). Thanks @coygeek, @gensokyo-dev, @gongfpp.
- **Source identity:** Preserve Next route spelling, module grammar and loader request identity, directory package-main behavior, accessor effects, exact source targets, and resolver-discovery versus module-loading distinctions. [ddd7ac7](https://github.com/morluto/rea/commit/ddd7ac7b846b48191ead1f7dcd0e3bd735468fa8), [9974512](https://github.com/morluto/rea/commit/99745126009b15060d71c38809418858a07d5afa), [f2e5fd1](https://github.com/morluto/rea/commit/f2e5fd148a85fa737302dd194a33172c4561093e), [baf1678](https://github.com/morluto/rea/commit/baf16784af3592534687052431c145daef55cfd1), [6e324b2](https://github.com/morluto/rea/commit/6e324b224405f2befd953234f6dc210cf72ad50a), [75daf0c](https://github.com/morluto/rea/commit/75daf0c7df108f04ab41f34aec1a31d4a7ceb322), [f91b7f8](https://github.com/morluto/rea/commit/f91b7f8009c9af27a5e0148e3b07d20bfe3b7a2a).
- **Graphs and selected facts:** Preserve JavaScript graph omissions/version facts, Electron native-loader facts, web JSON identity/coverage, module-path selection, feature-trace qualifications, selected native paths, endpoint context, and mixed Swift demangler outcomes. [30db499](https://github.com/morluto/rea/commit/30db499db310f073d9bf3d3acfd77985e1cb7e2e), [5a7bddb](https://github.com/morluto/rea/commit/5a7bddb3aeb0345854e76b79d529aed1c40e2054), [79805f2](https://github.com/morluto/rea/commit/79805f244a1ec0850f3109929168a9c8ebb1b908), [f719dc4](https://github.com/morluto/rea/commit/f719dc46c51a55b3926eec9659d06e7dfb95132b), [7784fdb](https://github.com/morluto/rea/commit/7784fdbad78f8b384d513f0e7dae8e983cd48ef8), [76500f1](https://github.com/morluto/rea/commit/76500f12b54e07b1ea700a403b2593890d63b42a), [676d29f](https://github.com/morluto/rea/commit/676d29f9eef794cea668f5f09f0ff510c848a513), [edf7b3d](https://github.com/morluto/rea/commit/edf7b3d32c3248f221e27f7ce5a3d34e8590285b), [93de7bc](https://github.com/morluto/rea/commit/93de7bc7e324a761162a31625b838ba0d4e3824d).
- **Managed boundaries:** Honor native platform symbol lookup, consume resolved IL2CPP ELF declarations, and attribute malformed comparison inputs to the supplied argument. [64fa1db](https://github.com/morluto/rea/commit/64fa1db1309218283df19f3ee4dba61f5fbd2a67), [87745af](https://github.com/morluto/rea/commit/87745af52ab0ccd3a172c2534c516d41b0095035), [96ad94f](https://github.com/morluto/rea/commit/96ad94f69eda5d68c7335ff775b6927b1376fd2b).
- **Provider and worker recovery:** Preserve output loss after successful command exit, physical refusal diagnostics, bounded worker control framing, JavaScript worker-phase/budget attribution, admitted ambiguity choices, and selected EVM limiter availability. Scope objdump debug-link limitations to DWARF requests and keep process-capture preparation nonexecuting. [e639edf](https://github.com/morluto/rea/commit/e639edf6204c05285d96840478926592daf95876), [171b33b](https://github.com/morluto/rea/commit/171b33b2c4defc6cbea49545bedf4272b72d1ea2), [34563fc](https://github.com/morluto/rea/commit/34563fcc9bd92af30668820dcac5635e27181094), [3f9357b](https://github.com/morluto/rea/commit/3f9357b5990d166a38440f56f439452266650093), [cec8f1e](https://github.com/morluto/rea/commit/cec8f1e8f305d6332272457a6ee419d3c5fb6359), [a74d859](https://github.com/morluto/rea/commit/a74d859a19122fb8f1282b3632d320997b1caae0), [c284168](https://github.com/morluto/rea/commit/c284168c4b1cd214ce7f504d43da28528d82d603), [4fb936f](https://github.com/morluto/rea/commit/4fb936fe86bb7a07f94f819ae8510301f2ef69c9).
- **Setup and limits:** Retain unknown environment-budget facts, selected-skill/maintenance uncertainty, Hopper URL admission and bookmark verification, and listener failure recovery that matches the system cause. [a718037](https://github.com/morluto/rea/commit/a7180370332350bc0f998fbd643888221759570e), [d1fbebb](https://github.com/morluto/rea/commit/d1fbebb578538dc94fd5e376e6e1959218efa835), [60accad](https://github.com/morluto/rea/commit/60accad2de8f2862fe91901502fe26cc5390fb36), [fd7da77](https://github.com/morluto/rea/commit/fd7da77fc02dcfaf7d1bc9f2fb240d71cdd27c59), [0c440bf](https://github.com/morluto/rea/commit/0c440bfc07f3cb343e2e836d27cccaed539cfe9c).
- **State and Evidence:** Retain export tarball ownership and SQLite primary outcomes through cleanup retry, count emitted SQLite schema names across encodings, scope display refresh to matching snapshot bindings, and report retained-result validation failures consistently. Reconstruction uses one effective completion scope and kind-based labels when names are blank. [ab58edc](https://github.com/morluto/rea/commit/ab58edcdf50b4bd687900ee2dc362ecc5c3dce15), [1006714](https://github.com/morluto/rea/commit/100671427105791a5c5913a918b63a394f6bc83e), [a47788a](https://github.com/morluto/rea/commit/a47788a6a4abb067273f954275a9d0a269adf1e7), [8aa40e9](https://github.com/morluto/rea/commit/8aa40e9a61155cff8b6f3c138c601bf805b20653), [b8c1b45](https://github.com/morluto/rea/commit/b8c1b45316882beeab4a9de49381d94e131fab0f), [a35b933](https://github.com/morluto/rea/commit/a35b9335a0124674ee5e3abe8c075f1fff6b4fa6), [#6137](https://github.com/morluto/rea/pull/6137), [#6126](https://github.com/morluto/rea/issues/6126). Thanks @carlogiovannineri-rgb, @mohithhhh.
- **Capture coverage:** Name browser bundle budgets and deadline recovery, separate domain readiness from worker attribution, attribute rendered-frame omissions to their producing stage, share offline-capture host admission with discovery, preserve selected signal-file semantics, and retain short Flutter GNU build IDs. [#6036](https://github.com/morluto/rea/pull/6036), [ba78133](https://github.com/morluto/rea/commit/ba781330d6d28b22d15498e63c144fb40e086156), [5934387](https://github.com/morluto/rea/commit/5934387f4185129efe9dc8b7338d8bc3fa5b25bd), [ad629b2](https://github.com/morluto/rea/commit/ad629b2e31c3a9495b27179360b508fa021fe86a), [928ba76](https://github.com/morluto/rea/commit/928ba76882bfd216dde8ed79118789b2c3aad4f9), [678c4cf](https://github.com/morluto/rea/commit/678c4cfa68e2fee12f620075e12dc87d9b1195b0).
- **Additional boundary facts:** Retain parser format/query occurrence identity and independent malformed-input facts, unresolved nested Swift identifiers, and completed signing-capture coverage; describe crash documents without assuming a provider. [5f702dd](https://github.com/morluto/rea/commit/5f702dd6ccd9e8e505e224854b91678439c3b41f), [99d2d7a](https://github.com/morluto/rea/commit/99d2d7ad22bcbd780919760008aba6a88e7d1077), [90e433d](https://github.com/morluto/rea/commit/90e433df06b3f83417176b8db1b0a905f9df1d3a), [#5548](https://github.com/morluto/rea/pull/5548), [ee12d51](https://github.com/morluto/rea/commit/ee12d517a25d34853d40e31fc039c7d1576c60e1), [#5538](https://github.com/morluto/rea/issues/5538). Thanks @coygeek, @ShuaixinHuang.
- **Documentation:** Add the Romanian README. [#7026](https://github.com/morluto/rea/pull/7026). Thanks @An3treiu.
Full contribution record: [1740 commits and 497 merged PRs](https://github.com/morluto/rea/blob/rea-agents-6.8.0/docs/releases/6.8.0.contributions.json).
## [6.7.0](https://github.com/morluto/rea/compare/rea-agents-6.6.0...rea-agents-6.7.0) (2026-10-18)
### Highlights
@@ -78,7 +180,7 @@ Full contribution record: [4 commits and 1 merged PR](https://github.com/morluto
### ⚠ 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)).
- **Saved Evidence:** Evidence and analysis snapshots now require explicit nullable `relationship_coverage` in their identity commitments. Earlier records whose digest omitted null coverage are incompatible even when the serialized field is explicitly null. 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)).
@@ -2581,7 +2683,7 @@ Read the [6.1 to 6.2 migration guide](docs/migration-6.2.md) before upgrading. R
* enforce conventional pull request titles ([3a21f16](https://github.com/morluto/rea/commit/3a21f16c1832430765083636511a8c12dbccf2dd))
* verify installed package with Linux Hopper ([4f97f69](https://github.com/morluto/rea/commit/4f97f699e4d5471557d1ce0ff91e144065d06a61))
## [0.3.0](https://github.com/morluto/rea/compare/rea-agents-0.2.1...rea-agents-0.3.0) (2026-07-12)
## [0.3.0](https://github.com/morluto/rea/compare/rea-0.2.1...rea-agents-0.3.0) (2026-07-12)
### Features
@@ -2634,7 +2736,7 @@ Read the [6.1 to 6.2 migration guide](docs/migration-6.2.md) before upgrading. R
* redesign and localize README ([051b6db](https://github.com/morluto/rea/commit/051b6db65687fd14826b6462cd623feb2504f123))
* redesign and localize README ([910eda6](https://github.com/morluto/rea/commit/910eda6f323ba1151b0771fe5de6c60a91e3d46d))
## [0.2.0](https://github.com/morluto/rea/compare/rea-0.1.0...rea-0.2.0) (2026-07-12)
## [0.2.0](https://github.com/morluto/rea/releases/tag/rea-0.2.0) (2026-07-12)
### Features
+77 -11
View File
@@ -10,13 +10,32 @@ and maintenance cost. Keep cosmetic polish with useful fixes; confusing or
incorrect user instructions warrant standalone corrections. Collect other
cosmetic-only suggestions in an issue rather than separate PRs.
For capability organization and provider composition, use the
[architecture map](docs/architecture.mermaid). Production factories in
`src/composition/` construct fresh providers without starting engines or acquiring
targets; optional adapter failures must preserve successful peers. Run
For selected architectural owners and dependency paths, use the
[architecture map](docs/architecture.mermaid). It is not a complete directory or
import inventory. For current provider wiring, start with
[`src/composition/binary.ts`](src/composition/binary.ts) for deep candidates and
the other `src/composition/` factories for tool services, including
[`gpu.ts`](src/composition/gpu.ts) and [`model.ts`](src/composition/model.ts).
Live Frida, Cutter, GDB and Rizin construction also uses shared composition
factories. For service ownership, availability and catalog declarations, follow
the [capability composition guide](docs/capability-composition.md).
Production factories construct fresh providers without starting engines or
acquiring targets; optional adapter failures must preserve successful peers. Run
`npm run verify:test-discovery` after adding or moving tests. Resolved import
boundaries are checked by `npm run verify:module-boundaries`, included in lint.
Before retiring an alias or facade, trace runtime registration, dynamic imports,
JavaScript verifiers, subprocess scripts, and packaged entrypoints as well as
TypeScript references. Tests may be the last consumers of a superseded internal
path; migrate their distinct assertions to the current production path before
removing it. Preserve validation, lazy loading, caller policy, and resource
acquisition and cleanup supplied by the old boundary. Published APIs, persisted
formats, and independently versioned bridges require an explicit compatibility
decision; an internal representation alone does not establish that obligation.
See [interpreting boundary facts once](docs/tool-design.md#interpret-boundary-facts-once)
for admission ownership and the distinction between repeated validation and
independent checks.
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
@@ -26,6 +45,10 @@ 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
[analysis settings, limits, and recovery](docs/design/limits-and-recovery.md)
for ownership, operation snapshots, provider integration, and the steps for
adding a runtime setting.
## Reporting issues
@@ -70,7 +93,8 @@ Before requesting maintainer review, the human owner must:
passing. Follow the [verification requirements](#development-feedback-and-pr-verification)
for required checks and real-provider coverage.
- Complete the **Author readiness** declaration in the PR template, explicitly
stating that the PR is ready for initial review.
stating that the PR is ready for initial review, unless the PR author is
exempt from the declaration as listed below.
Keep the PR description concise and current. Explain the problem, observable
outcome, approach, and useful evidence; link long logs or put them in optional
@@ -79,7 +103,11 @@ Human ownership and review remain required when CI or an automated reviewer
reports success. Readiness requests maintainer review; it does not grant scope
approval or permission to merge.
Draft PRs may request early feedback with open questions and incomplete checks.
Draft status is optional. Draft PRs may request early feedback with open questions
and incomplete checks. Agents preserve the current draft status unless asked to
change it; missing readiness declarations or later updates do not require a PR
to become a draft.
Leave the readiness declaration unchecked until the human owner has completed
it. The declaration records initial readiness; later pushes do not invalidate
it or require another human sign-off. Keep the PR description and affected
@@ -87,9 +115,12 @@ validation current as work changes. AI agents must leave human declarations to
the human owner and must not attest to a human review on their behalf.
The `PR readiness` check reports pending for drafts and requires a named human
owner and all three template declarations for non-drafts. It checks that the
declaration is filled, not whether the code or evidence is correct. Existing PRs
can copy the **Author readiness** section from the current template.
owner and all three template declarations for non-drafts. PRs authored by
@morluto, @kaoru0822-kitauji, @N0zoM1z0, or @gensokyo-dev are exempt from
the declaration; their human ownership, review, and evidence responsibilities
above still apply. The check identifies authors by GitHub account ID. It checks
that the declaration is filled, not whether the code or evidence is correct.
Existing PRs can copy the **Author readiness** section from the current template.
Open PRs must have distinct head commits because GitHub shares commit statuses.
Maintainers enable merge enforcement after the workflow is merged: run
@@ -204,6 +235,8 @@ Use the development toolchain pinned in `.nvmrc` and
`package.json#packageManager`. Run `nvm use` before installing dependencies;
installed-package runtime support is documented in
[installation](docs/installation.md#start-setup).
Use Node.js and npm for source builds and verifier scripts; the installed
package's Bun runtime support does not extend to those development scripts.
```bash
npm ci
@@ -218,6 +251,15 @@ catalogs, documentation projections, or managed verification evidence. Turbo
caches deterministic builds and static checks across Git worktrees. After a package, lockfile, or managed-skill version change, run
`npm run metadata:generate` before building.
Dependabot proposes updates for the npm lockfiles, website Python requirements,
and GitHub Actions. Review dependency changes and run the checks relevant to
their consumers. For dependencies maintained outside Dependabot, including
`third_party/` and the HTTP/3 lab's pinned requirements, the maintainer should
review upstream security advisories when updating or using them. Preserve source
and version provenance, update license notices when needed, and report which
verification was actually repeated. The lab requirements describe a recorded
environment; a dependency update does not verify the revised environment.
Repository invariants live in [AGENTS.md](AGENTS.md). Keep provider-specific
protocols in adapters and shared CLI/MCP workflows in the application layer.
@@ -239,6 +281,22 @@ 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. The embedded-artifact parent-swap tests exercise the selected-path
replacement when the rename succeeds; if Windows refuses it while the admitted
file is open, they instead verify the refusal and cleanup. 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`
@@ -345,9 +403,17 @@ Real-provider execution is uncached.
### Temporary files and diagnostics
Tests use `createTestTempDirectory` from `tests/fixtures/temporaryDirectory.ts`
for exact-path, awaited cleanup, including failures and timeouts.
for exact-path, awaited cleanup, including failures and timeouts. It registers
deletion with Vitest's `onTestFinished`; do not also add its result to a roots
array or an `afterEach` removal. `createTestWorkspace` from
`tests/support/workspace/workspaceFixture.ts` leaves cleanup with its caller,
which must await `removeTestWorkspace`. Keep explicit resource teardown before
directory deletion, using the case's `finally` or `afterEach`; Vitest runs
`afterEach` before `onTestFinished` and awaits finish hooks in reverse
registration order.
`npm run verify:test-temp-hygiene` runs the suite under a fresh `TMPDIR` and
rejects remaining REA-owned paths. Never glob-clean shared `/tmp/rea-*` content.
rejects remaining REA-owned paths. It cannot detect duplicate deletion owners or
prove resource-close ordering. Never glob-clean shared `/tmp/rea-*` content.
`REA_LOG_LEVEL` selects `trace`, `debug`, `info`, `warn`, `error`, `fatal`, or
`silent`. Logs go to stderr: MCP defaults to `info`, and one-shot CLI logging
+118 -42
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_ml.md)
· [Ελληνικά](docs/readme/README_el.md) · [മലയാളം](docs/readme/README_ml.md) · [Roman Urdu](docs/readme/README_ur-Latn.md) · [Română](docs/readme/README_ro.md)
# REA: Reverse Engineer Anything
@@ -20,7 +20,9 @@
**[Website](https://rea.tools/) · [Guides](https://rea.tools/guides/) · [Showcases](https://rea.tools/showcase/)**
[Quick start](#quick-start) · [How REA works](#how-rea-works) · [What you can analyze](#what-you-can-analyze) · [Showcases](#showcases) · [FAQ](#faq) · [Documentation](#documentation)
<a href="https://discord.com/invite/GkcryMnJDM"><img alt="Join the REA community on Discord" src="https://img.shields.io/badge/Join%20us%20on%20Discord-5865F2?style=for-the-badge&logo=discord&logoColor=white" height="40"></a>
[Quick start](#quick-start) · [How REA works](#how-rea-works) · [What you can analyze](#what-you-can-analyze) · [Showcases](#showcases) · [Build RL environments](#build-rl-environments) · [FAQ](#faq) · [Documentation](#documentation)
<code>npx rea-agents setup</code>
@@ -62,7 +64,7 @@ See a feature in an app that you want in your own product? Ask your agent to inv
REA connects your agent to tools for inspecting native binaries, JavaScript and Electron apps, .NET assemblies, and websites. You can also use the same tools from your terminal. Analysis runs locally, and results include the evidence and limitations behind each conclusion.
Setup registers REA with your agent and installs matching workflow instructions. Native analysis can use an existing Hopper, Ghidra, or IDA installation; setup can optionally install Hopper with approval. Static JavaScript analysis needs no native analysis engine.
Setup registers REA with your agent and installs matching workflow instructions. Native analysis can use an existing Hopper, Ghidra, IDA or Kuna installation; setup can optionally install Hopper with approval. Static JavaScript analysis needs no native analysis engine.
> **[Visit the REA website](https://rea.tools/)** for setup instructions, illustrated guides, and real case studies.
@@ -83,12 +85,27 @@ npx rea-agents setup
Choose your agents, review the proposed changes, and approve them. Setup adds
REA's MCP server and matching workflow instructions, with backups of existing
configuration. Restart your agent afterward.
configuration. Restart your agent afterward. Setup records the selected runtime and REA
entry script directly. If acquired through a package runner, keep its cache entry
available. Run package acquisition from a trusted directory outside the target
project. Installation and recovery details are in the setup guide below.
Setup supports Claude Code, Codex, Cursor, Gemini CLI, Grok Build and
[other agents](docs/installation.md#supported-agents). See
[installation and setup](docs/installation.md) for provider configuration and
manual MCP registration.
manual MCP registration and Claude Code/Codex plugin installation.
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.
Complete output schemas are advertised by default. If your client caps the
complete `tools/list` frame, set `REA_MCP_OUTPUT_SCHEMA_PROFILE=compact` to
bound each advertised output schema, or `off` to omit output schemas entirely.
Both settings keep canonical server-side validation and complete structured
results. See "Output schemas" in the MCP contracts guide.
### Ask your agent
@@ -171,38 +188,48 @@ 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 |
| ---------------------- | ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| 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 |
| 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) |
| 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, IDA or Kuna; [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) |
| PE imports | Import and delay-load DLL names, ordinals, IAT RVAs and pefile import hash | Built-in static PE inspection; [PE imports](docs/pe-imports.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) |
| Binary hardening | PIE, NX, RELRO, CFG and related header flags for ELF, PE and Mach-O | Built-in static header inspection; [binary hardening](docs/binary-hardening.md) |
| Apple IPS crashes | .ips metadata, threads, images, registers and documented crash classes | Built-in static JSON inspection; [Apple IPS](docs/apple-ips.md) |
| Linux kernel modules | .modinfo keys, __versions CRCs, this_module presence, and signature trailer | Built-in static ELF inspection; [kernel modules](docs/kernel-modules.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
@@ -229,7 +256,12 @@ for features added since the latest npm release.
## Showcases
[![Illustrations of the DX-Ball sound-pan, Notion clipboard-bridge, and TH04 bullet-ring showcases](docs/assets/rea-showcases.png)](https://rea.tools/showcase/)
<a href="https://rea.tools/showcase/">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="docs/assets/rea-showcases-dark.svg" />
<img alt="Illustrations of the DX-Ball sound-pan, Notion clipboard-bridge, and TH04 bullet-ring showcases" src="docs/assets/rea-showcases.svg" />
</picture>
</a>
### DX-Ball: reconstruct a sound-pan calculation
@@ -256,11 +288,53 @@ historical compiler output.
[Read the case study](https://rea.tools/showcase/th04/) ·
[Reconstruction repository](https://github.com/N0zoM1z0/th04)
### Build RL environments with REA
Record a web app session, turn its API into tools, and export an environment an AI agent can use. Run it with AgentEnv, then add tasks and reward checks for RL training or evaluation.
[Read the guide](https://rea.tools/guides/rl-environments/) ·
[Watch the video](https://x.com/reatools/status/2112574822584918075?s=20)
If you've used REA on something interesting, we'd love to see it. Share your
case in an [issue](https://github.com/morluto/rea/issues) or a
[pull request](https://github.com/morluto/rea/pulls), including the target,
your question, how REA helped, and what you found.
## Build RL environments
Give an AI agent a local environment to practice tasks such as finding an issue or creating a folder, without running the original app. Use it for reinforcement learning (RL) or to test how well an agent completes tasks.
REA turns recorded API requests and responses into tools the agent can call. The environment uses the recorded data and supports changes only when the recording confirms their effects.
With REA set up in your agent, replace the example URL and folder tasks below with your own:
```text
Use REA to build an RL environment from my test web app at
http://127.0.0.1:3000.
Cover listing, creating, renaming, and deleting folders. You may create and
remove test folders. Leave existing data unchanged.
Save it in a folder named my-env. Check which operations work and tell me
how to run it.
```
<details>
<summary>Use the command line instead</summary>
Use these commands if you already have a file containing the app’s recorded browser requests and responses. Running the result also requires AgentEnv and Docker.
```sh
rea environment-build ./session.har ./my-env --name my-env --json
agent-env run ./my-env
```
</details>
REA exports a runnable environment with a basic test to check that it starts and responds. Use it with your RL framework to define task goals, add reward checks, and run training.
[Build an environment](https://rea.tools/guides/rl-environments/) ·
[Environment reference](docs/agent-environments.md) ·
[Watch the demo on X](https://x.com/reatools/status/2112574822584918075?s=20)
## FAQ
<details>
@@ -273,11 +347,13 @@ Any agent that supports local MCP servers. Setup configures the
</details>
<details>
<summary><strong>Do I need Hopper, Ghidra or IDA?</strong></summary>
<summary><strong>Do I need a native analysis engine?</strong></summary>
Deep native analysis uses one of them. Static JavaScript and .NET inspection
work without a native analysis engine. Setup can install Hopper after approval;
Ghidra and IDA use your existing installations. See [provider setup](docs/installation.md#hopper).
Deep native analysis uses Hopper, Ghidra, IDA or Kuna, depending on the target
and operation. Static JavaScript and .NET inspection work without a native
analysis engine. Setup can install Hopper after approval; Ghidra, IDA and Kuna
use your existing installations. See [provider setup](docs/installation.md#hopper)
for the options and their supported targets.
</details>
@@ -357,7 +433,7 @@ Report vulnerabilities through [SECURITY.md](SECURITY.md).
## Star history
🎉 **300,000 GitHub stars — thank you!**
🎉 **400,000 GitHub stars — thank you!**
Thanks to everyone using REA, reporting bugs, requesting features, testing builds, and contributing fixes.
@@ -387,7 +463,7 @@ to improve the code or docs.
See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup and checks,
[testing](docs/testing.md) for verification lanes, and the
[architecture map](docs/architecture.mermaid) for the project structure.
[architecture map](docs/architecture.mermaid) for selected layers and dependency paths.
## License
+1 -1
View File
@@ -10,7 +10,7 @@ Do not disclose vulnerabilities, proof-of-concept exploits, credentials,
private binaries, Hopper documents, or private Ghidra projects in a public
issue.
Use **Report a vulnerability** in the repository's **Security** tab to submit the report privately. Do not open a public issue to request a private contact channel.
Use **Report a vulnerability** in the repository's **Security** tab as the preferred way to submit the report privately. If GitHub private reporting is unavailable to you, email [[email protected]](mailto:[email protected]). Do not open a public issue to request a private contact channel.
Include the affected REA version, operating system/distribution, selected
provider and version, impact, reproduction conditions, and any suggested
+23 -21
View File
@@ -16,6 +16,8 @@ notices supplied with the software you actually obtain or redistribute.
| Material | Use and licensing records |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| npm dependencies | [Complete inventory](licenses/npm-dependencies.txt) of every entry in `package-lock.json`, including transitive, optional platform, development, and website dependencies. [Runtime notices](licenses/npm-runtime-notices.txt) preserve the copyright, license, and NOTICE documents from integrity-verified release archives, plus the MIT notice for Rolldown's generated runtime helpers. Development dependencies are inventoried but are not all distributed in REA's npm package. |
| pefile ordinal tables | Import-hash ordinal names are derived from [pefile 2024.8.26](https://github.com/erocarrera/pefile/tree/4b3b1e2e568a88d4f1897d694d684f23d9e270c4), copyright 2004–2024 Ero Carrera. Preserve the [MIT license](licenses/pefile-2024.8.26-LICENSE.txt). The Python package is not a runtime dependency. |
| UF2 family names | UF2 family IDs and short names are copied from [microsoft/uf2 `utils/uf2families.json`](https://github.com/microsoft/uf2/blob/cf3bd7f9c274b949528f8cd6a19eef2502a687d2/utils/uf2families.json), copyright Microsoft Corporation. Preserve the [MIT license](licenses/microsoft-uf2-MIT.txt). |
| NativeAOT parser | REA's derived parser uses the MIT-licensed [ghidra-nativeaot](https://github.com/Washi1337/ghidra-nativeaot/tree/effeb734fc570c32650f88b159608979dc7b423e) work of Washi and the NativeAOT layout definitions in [dotnet/runtime](https://github.com/dotnet/runtime/tree/a3b2d40328be0be3f8dd43f0e5cc925b8827b044). Preserve [Washi's MIT notice](licenses/nativeaot-parser-MIT.txt) and the [.NET Foundation MIT notice](licenses/dotnet-runtime-MIT.txt). |
| EVM analysis | `[email protected]`, MIT, copyright 2023 Maxim Andreev. The published npm archive omits its license document; REA retains the [original upstream license](third_party/evmole/LICENSE) and [provenance](third_party/evmole/README.md). It is also included in the runtime notices. |
| Windows native addon | The release addon is compiled with MinGW-w64 and statically links GCC runtime libraries. Preserve the [MinGW-w64 runtime notices](licenses/MinGW-w64-runtime.txt), [GPLv3 text](licenses/GPL-3.0.txt), and [GCC Runtime Library Exception 3.1](licenses/GCC-Runtime-Library-Exception-3.1.txt). The exception governs eligible compiled combinations; it does not relicense the runtime itself under MIT. The Node-API build headers are from Node 22.19.0; its [original license and component notices](licenses/Node-22.19.0-LICENSE.txt) are retained. Node itself is supplied by the user. Snapshot provenance and release-toolchain review requirements are in [licenses/README.md](licenses/README.md). |
@@ -52,27 +54,27 @@ REA's adapters do not grant, replace, or waive these licenses. The upstream
executables and their dependency graphs are supplied separately. Git submodules
pin inspection sources; they are not included in the npm package.
| Project | License and source | REA relationship |
| ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Ghidra | [Apache-2.0](https://github.com/NationalSecurityAgency/ghidra/blob/Ghidra_12.1.4_build/LICENSE), with the government-work and component qualifications in its [NOTICE](https://github.com/NationalSecurityAgency/ghidra/blob/Ghidra_12.1.4_build/NOTICE) | Bring your own Ghidra 12.1.4. Copies of its [LICENSE](licenses/ghidra-12.1.4-LICENSE.txt), [NOTICE](licenses/ghidra-12.1.4-NOTICE.txt), and [DISCLAIMER](licenses/ghidra-12.1.4-DISCLAIMER.txt) are retained for reference. |
| ghidra-nativeaot | [MIT](https://github.com/Washi1337/ghidra-nativeaot/blob/effeb734fc570c32650f88b159608979dc7b423e/LICENSE.md) | Pinned inspection source; derived parser attribution above. |
| JADX | [Apache-2.0](https://github.com/skylot/jadx/blob/v1.5.6/LICENSE) | The audited jadx-headless-mcp source uses JADX 1.5.6 for Android decompilation; the engine JAR is separately supplied. |
| jadx-headless-mcp | [Apache-2.0](https://github.com/1013503897/jadx-headless-mcp/blob/5844800a486d2248fa949b74c9896285f5b54de2/LICENSE) | v0.7.1; pinned submodule and user-supplied JAR. |
| Kuna | [Apache-2.0](https://github.com/Noelo-Lab/kuna/blob/v1.833/LICENSE), with its [NOTICE](https://github.com/Noelo-Lab/kuna/blob/v1.833/NOTICE) | Bring your own `kuna`; REA verified 1.833 and never downloads, installs or bundles it. REA invokes it as a separate process. |
| Apktool | [Apache-2.0](https://github.com/iBotPeaches/Apktool/blob/master/LICENSE.md) | User-supplied Android resource decoder. |
| Binwalk | [MIT](https://github.com/ReFirmLabs/binwalk/blob/4fdab3d464d97b68e0af9088df3f9e2e1545b21c/LICENSE) | v3.1.0; pinned submodule and user-supplied firmware engine. |
| Unblob | [MIT](https://github.com/onekey-sec/unblob/blob/1fcc7a0a584a70a96c31f5a276c20944d199a089/LICENSE) | 26.6.4; pinned submodule and user-supplied firmware engine. |
| Wakaru | [Apache-2.0](https://github.com/pionxzh/wakaru/blob/8219bf5016a063d7fb215101a8c45d5959ec733f/LICENSE) | v1.14.0; pinned source and separately obtained JavaScript reconstruction tooling. |
| WABT | [Apache-2.0](https://github.com/WebAssembly/wabt/blob/ff0ef7e0009402740c805a9744c09b05be063e48/LICENSE) | v1.0.42; external WebAssembly tools; [integration provenance](third_party/wabt/README.md). |
| pwntools | [MIT, with GPL/BSD component qualifications](https://github.com/Gallopsled/pwntools/blob/3ad86ec5adab1abf9781ca7552a6f5d7cff7a33c/LICENSE-pwntools.txt) | 4.15.0; caller-supplied offline ELF analysis package. |
| pyelftools | [Public-domain dedication and component notices](https://github.com/eliben/pyelftools/blob/v0.33/LICENSE) | 0.33; includes separately licensed bundled material, including Construct. |
| Unicorn | [GPL-2.0 and component notices](https://github.com/unicorn-engine/unicorn/blob/2.1.2/COPYING) | 2.1.2; used by the caller-supplied ELF analysis profile for instruction emulation. |
| pwndbg | [MIT](third_party/pwndbg/LICENSE.md) | 2026.09.15, commit `55596aa676a2756efb8d67034f31675170eabe48`; separately obtained debugger extension. |
| GNU GDB | [GPL-3.0-or-later](https://sourceware.org/git/?p=binutils-gdb.git;a=blob;f=gdb/COPYING) | Caller-supplied debugger for the optional pwndbg core-file workflow. |
| LLDB / LLVM | [Apache-2.0 with LLVM exceptions](https://github.com/llvm/llvm-project/blob/main/LICENSE.TXT) | Caller-supplied debugger; the installed distribution's component and historical notices also apply. |
| mitmproxy | [MIT](https://github.com/mitmproxy/mitmproxy/blob/6c09d56e4c29a92f5ad01b03199977584b8ea14f/LICENSE) | 12.2.3; separately supplied network capture proxy. |
| Android Debug Bridge (adb) | [Apache-2.0](https://android.googlesource.com/platform/packages/modules/adb/+/refs/heads/main/NOTICE) | User-supplied Android device tool. A complete Android SDK download has its own [SDK license terms](https://developer.android.com/studio/terms); adb's source license does not replace them. |
| ILSpy / ICSharpCode.Decompiler | [MIT](https://github.com/icsharpcode/ILSpy/blob/master/ICSharpCode.Decompiler/Properties/AssemblyInfo.cs) | Optional separately installed .NET decompiler; not a redistributed REA dependency. |
| Project | License and source | REA relationship |
| ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Ghidra | [Apache-2.0](https://github.com/NationalSecurityAgency/ghidra/blob/Ghidra_12.1.4_build/LICENSE), with the government-work and component qualifications in its [NOTICE](https://github.com/NationalSecurityAgency/ghidra/blob/Ghidra_12.1.4_build/NOTICE) | Bring your own Ghidra; see [supported versions](docs/installation.md#ghidra). Copies of the 12.1.4 [LICENSE](licenses/ghidra-12.1.4-LICENSE.txt), [NOTICE](licenses/ghidra-12.1.4-NOTICE.txt), and [DISCLAIMER](licenses/ghidra-12.1.4-DISCLAIMER.txt) are retained for reference. |
| ghidra-nativeaot | [MIT](https://github.com/Washi1337/ghidra-nativeaot/blob/effeb734fc570c32650f88b159608979dc7b423e/LICENSE.md) | Pinned inspection source; derived parser attribution above. |
| JADX | [Apache-2.0](https://github.com/skylot/jadx/blob/v1.5.6/LICENSE) | The audited jadx-headless-mcp source uses JADX 1.5.6 for Android decompilation; the engine JAR is separately supplied. |
| jadx-headless-mcp | [Apache-2.0](https://github.com/1013503897/jadx-headless-mcp/blob/5844800a486d2248fa949b74c9896285f5b54de2/LICENSE) | v0.7.1; pinned submodule and user-supplied JAR. |
| Kuna | [Apache-2.0](https://github.com/Noelo-Lab/kuna/blob/v1.990/LICENSE), with its [NOTICE](https://github.com/Noelo-Lab/kuna/blob/v1.990/NOTICE) | Bring your own `kuna`; REA verified 1.990 and never downloads, installs or bundles it. REA invokes it as a separate process. |
| Apktool | [Apache-2.0](https://github.com/iBotPeaches/Apktool/blob/master/LICENSE.md) | User-supplied Android resource decoder. |
| Binwalk | [MIT](https://github.com/ReFirmLabs/binwalk/blob/4fdab3d464d97b68e0af9088df3f9e2e1545b21c/LICENSE) | v3.1.0; pinned submodule and user-supplied firmware engine. |
| Unblob | [MIT](https://github.com/onekey-sec/unblob/blob/1fcc7a0a584a70a96c31f5a276c20944d199a089/LICENSE) | 26.6.4; pinned submodule and user-supplied firmware engine. |
| Wakaru | [Apache-2.0](https://github.com/pionxzh/wakaru/blob/8219bf5016a063d7fb215101a8c45d5959ec733f/LICENSE) | v1.14.0; pinned source and separately obtained JavaScript reconstruction tooling. |
| WABT | [Apache-2.0](https://github.com/WebAssembly/wabt/blob/ff0ef7e0009402740c805a9744c09b05be063e48/LICENSE) | v1.0.42; external WebAssembly tools; [integration provenance](third_party/wabt/README.md). |
| pwntools | [MIT, with GPL/BSD component qualifications](https://github.com/Gallopsled/pwntools/blob/3ad86ec5adab1abf9781ca7552a6f5d7cff7a33c/LICENSE-pwntools.txt) | 4.15.0; caller-supplied offline ELF analysis package. |
| pyelftools | [Public-domain dedication and component notices](https://github.com/eliben/pyelftools/blob/v0.33/LICENSE) | 0.33; includes separately licensed bundled material, including Construct. |
| Unicorn | [GPL-2.0 and component notices](https://github.com/unicorn-engine/unicorn/blob/2.1.2/COPYING) | 2.1.2; used by the caller-supplied ELF analysis profile for instruction emulation. |
| pwndbg | [MIT](third_party/pwndbg/LICENSE.md) | 2026.09.15, commit `55596aa676a2756efb8d67034f31675170eabe48`; separately obtained debugger extension. |
| GNU GDB | [GPL-3.0-or-later](https://sourceware.org/git/?p=binutils-gdb.git;a=blob;f=gdb/COPYING) | Caller-supplied debugger for the optional pwndbg core-file workflow. |
| LLDB / LLVM | [Apache-2.0 with LLVM exceptions](https://github.com/llvm/llvm-project/blob/main/LICENSE.TXT) | Caller-supplied debugger; the installed distribution's component and historical notices also apply. |
| mitmproxy | [MIT](https://github.com/mitmproxy/mitmproxy/blob/6c09d56e4c29a92f5ad01b03199977584b8ea14f/LICENSE) | 12.2.3; separately supplied network capture proxy. |
| Android Debug Bridge (adb) | [Apache-2.0](https://android.googlesource.com/platform/packages/modules/adb/+/refs/heads/main/NOTICE) | User-supplied Android device tool. A complete Android SDK download has its own [SDK license terms](https://developer.android.com/studio/terms); adb's source license does not replace them. |
| ILSpy / ICSharpCode.Decompiler | [MIT](https://github.com/icsharpcode/ILSpy/blob/master/ICSharpCode.Decompiler/Properties/AssemblyInfo.cs) | Optional separately installed .NET decompiler; not a redistributed REA dependency. |
Ghidra's original notice credits software developed at the National Security
Agency. It explains that US-government-created portions are not subject to US
+10
View File
@@ -0,0 +1,10 @@
# Plugin icon
`rea-plugin.png` is a square adaptation of the left panel of REA's GitHub
social preview, retrieved on 2026-10-28:
https://repository-images.githubusercontent.com/1209966933/1a3fef9a-be4b-41f4-8830-a94c0e8288cc
The image was adapted with imagegen, preserving the cyan REA lettering,
pixel typography, orange rule, and taglines. Both plugin manifests reference
this shared asset; only the PNG is included in the npm package.
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

+262
View File
@@ -0,0 +1,262 @@
"""Run REA inside an agentenv build computer for REA on the host: capture the app, draft its task and export it.
REA sends this file's source through the computer's ``computer-exec/v1`` route as
``python3 -I -c <source> <command> <JSON arguments>``; each command prints one JSON line and exits. ``start``
leaves the capture running on its own: its stdin is a FIFO it holds open for reading and writing, so it never
sees the end of its input, and ``mark`` and ``stop`` write their requests there and wait for the capture's own
acknowledgement on its progress log. A small shell around the capture writes its exit status to a file, the
mark a later command reads. REA runs as the session user in the home, with no ``AGENTENV_*`` variable, so
neither REA nor the app it launches holds the computer's control token, and with ``APT_CONFIG`` naming the
package lists ``start`` downloads as that user, so the capture finds the package's sources.
"""
from __future__ import annotations
import hashlib
import json
import os
import re
import shutil
import subprocess
import sys
import time
POLL_S = 0.2
TAIL_BYTES = 4096
def _say(value: dict) -> int:
print(json.dumps(value), flush=True)
return 0
def _tail(path: str) -> str:
try:
with open(path, "rb") as handle:
handle.seek(max(0, os.path.getsize(path) - TAIL_BYTES))
return handle.read().decode("utf-8", "replace")
except OSError:
return ""
def _environment(work: str) -> dict:
environment = {name: value for name, value in os.environ.items() if not name.startswith("AGENTENV_")}
environment["APT_CONFIG"] = os.path.join(work, "apt", "apt.conf")
return environment
def _apt(work: str, timeout: float) -> dict:
"""Download the package lists into the work folder as the session user, until one update succeeds."""
root = os.path.join(work, "apt")
lists = os.path.join(root, "lists")
updated = os.path.join(root, "updated")
os.makedirs(os.path.join(lists, "partial"), exist_ok=True)
os.makedirs(os.path.join(root, "cache", "archives", "partial"), exist_ok=True)
with open(os.path.join(root, "apt.conf"), "w", encoding="utf-8") as conf:
conf.write(
f'Dir::State::Lists "{lists}";\n'
f'Dir::Cache "{root}/cache";\n'
'Debug::NoLocking "true";\n'
'APT::Update::Error-Mode "any";\n'
)
# apt keeps lists compressed as it fetched them, such as *_Packages.lz4 on Ubuntu.
count = lambda: len([name for name in os.listdir(lists) if re.search(r"_Packages(\.\w+)?$", name)]) # noqa: E731
# A marker left by an update that fetched nothing is not a success; download again.
if os.path.exists(updated) and count() > 0:
return {"lists": count(), "updated": False}
log = os.path.join(root, "update.log")
try:
with open(log, "wb") as output:
code = subprocess.run(["apt-get", "update"], env=_environment(work), stdin=subprocess.DEVNULL,
stdout=output, stderr=subprocess.STDOUT, timeout=timeout).returncode
except (OSError, subprocess.TimeoutExpired) as error:
return {"lists": count(), "updated": True, "error": f"{type(error).__name__}: {error}"}
lists_fetched = count()
if code == 0 and lists_fetched > 0:
# Only a whole update that fetched lists is kept, so a failed or empty one runs again.
open(updated, "w").close()
return {"lists": lists_fetched, "updated": True, "exit_code": code}
if code == 0:
return {"lists": lists_fetched, "updated": True, "exit_code": code,
"error": "apt-get update exited 0 without a package list"}
return {"lists": lists_fetched, "updated": True, "exit_code": code,
"error": f"apt-get update exited {code}: {_tail(log)[-1500:].strip()}"}
def _exit_code(capture: str):
try:
with open(os.path.join(capture, "exit"), encoding="utf-8") as handle:
return int(handle.read().strip() or "-1")
except (OSError, ValueError):
return None
def _event_line(path: str, offset: int, kinds: set, name):
"""The first progress line after ``offset`` whose event is of one of ``kinds``, for ``name`` when given."""
try:
with open(path, "rb") as handle:
handle.seek(offset)
lines = handle.read().decode("utf-8", "replace").splitlines()
except OSError:
return None
for line in lines:
try:
event = json.loads(line)["rea_progress"]["event"]
except (ValueError, KeyError, TypeError):
continue
if isinstance(event, dict) and event.get("kind") in kinds and (name is None or event.get("name") == name):
return line
return None
def _wait(capture: str, offset: int, kinds: set, name, timeout: float) -> dict:
"""Wait for the capture's event, or for it to exit, or for time to run out."""
log = os.path.join(capture, "progress.log")
deadline = time.monotonic() + timeout
while True:
line = _event_line(log, offset, kinds, name)
if line is not None:
return {"line": line}
code = _exit_code(capture)
if code is not None:
return {"exited": code, "progress": _tail(log), "output": _tail(os.path.join(capture, "capture.out"))}
if time.monotonic() > deadline:
return {"timed_out": True, "progress": _tail(log)}
time.sleep(POLL_S)
def start(arguments: dict) -> int:
work, capture = os.path.abspath(arguments["work"]), os.path.abspath(arguments["capture"])
rea = shutil.which("rea")
if rea is None:
return _say({"error": "REA is not installed in this computer; start the computer from a request, which installs it."})
if os.path.exists(capture):
return _say({"error": f"the capture folder {capture} is already used"})
apt = _apt(work, arguments.get("apt_timeout_s", 600))
try:
os.makedirs(capture)
except FileExistsError:
return _say({"error": f"a stop claimed {capture} before the capture launched", "apt": apt})
control = os.path.join(capture, "control")
os.mkfifo(control, 0o600)
argv = [rea, "capture-desktop-application", "--desktop-entry", arguments["desktop_entry"],
"--output", os.path.join(capture, "capture"), "--exclude", work, "--json"]
for value in arguments.get("arguments", []):
argv += ["--argument", value]
if arguments.get("package_file"):
argv += ["--package-file", arguments["package_file"]]
# Reading and writing, so the capture never reads the end of its input between requests.
reader = os.open(control, os.O_RDWR)
try:
with open(os.path.join(capture, "capture.out"), "wb") as out, \
open(os.path.join(capture, "progress.log"), "wb") as progress:
process = subprocess.Popen(
["sh", "-c", 'done=$1; shift; "$@"; code=$?; printf "%s" "$code" > "$done.tmp" && mv "$done.tmp" "$done"',
"sh", os.path.join(capture, "exit"), *argv],
cwd=os.path.expanduser("~"), env=_environment(work), stdin=reader, stdout=out, stderr=progress,
start_new_session=True)
finally:
os.close(reader)
waited = _wait(capture, 0, {"window_open"}, None, arguments.get("timeout_s", 120))
return _say({"pid": process.pid, "apt": apt, **waited})
def _request(capture: str, line: str) -> bool:
"""Write one request to the running capture; False when nothing reads its FIFO any more."""
try:
fd = os.open(os.path.join(capture, "control"), os.O_WRONLY | os.O_NONBLOCK)
except OSError:
return False
try:
os.write(fd, (line + "\n").encode("utf-8"))
finally:
os.close(fd)
return True
def mark(arguments: dict) -> int:
capture, name = os.path.abspath(arguments["capture"]), arguments["name"]
code = _exit_code(capture)
log = os.path.join(capture, "progress.log")
if code is not None:
return _say({"exited": code, "progress": _tail(log)})
# A checkpoint already taken, whose answer was lost, is answered from the capture's own log.
taken = _event_line(log, 0, {"checkpoint"}, name)
if taken is not None:
return _say({"line": taken})
offset = os.path.getsize(log)
if not _request(capture, f"checkpoint {name}"):
return _say({"error": "the capture is not reading its requests"})
return _say(_wait(capture, offset, {"checkpoint", "checkpoint_refused"}, name, arguments.get("timeout_s", 600)))
def stop(arguments: dict) -> int:
capture = os.path.abspath(arguments["capture"])
if _exit_code(capture) is None and not os.path.exists(os.path.join(capture, "control")):
# Never launched: claim the folder, so a start still on its way finds it used and launches nothing.
os.makedirs(capture, exist_ok=True)
return _say({"absent": True})
if _exit_code(capture) is None:
_request(capture, "stop")
deadline = time.monotonic() + arguments.get("timeout_s", 600)
while (code := _exit_code(capture)) is None and time.monotonic() < deadline:
time.sleep(POLL_S)
log = os.path.join(capture, "progress.log")
if code is None:
return _say({"timed_out": True, "progress": _tail(log)})
output = os.path.join(capture, "capture.out")
return _say({"exit_code": code, "progress": _tail(log), "output_bytes": os.path.getsize(output)})
def run(arguments: dict) -> int:
"""Run one REA command with its output in a file, after writing the files it reads."""
rea = shutil.which("rea")
if rea is None:
return _say({"error": "REA is not installed in this computer"})
for path, text in arguments.get("files", {}).items():
os.makedirs(os.path.dirname(path), exist_ok=True)
with open(path, "w", encoding="utf-8") as handle:
handle.write(text)
with open(arguments["stdout"], "wb") as out:
try:
done = subprocess.run([rea, *arguments["argv"]], cwd=os.path.expanduser("~"),
env=_environment(os.path.abspath(arguments["work"])), stdin=subprocess.DEVNULL, stdout=out,
stderr=subprocess.PIPE, timeout=arguments.get("timeout_s", 1800))
except subprocess.TimeoutExpired:
return _say({"timed_out": True})
return _say({"exit_code": done.returncode, "stdout_bytes": os.path.getsize(arguments["stdout"]),
"stderr": done.stderr[-TAIL_BYTES:].decode("utf-8", "replace")})
def listing(arguments: dict) -> int:
"""Every regular file under a folder, by its path relative to the home, with its size and SHA-256.
The list goes to the file ``output`` names, outside the folder, and the answer gives its size: a large
bundle's list outgrows the 1 MiB of stdout a command answers with, so REA reads it through ``data/get``.
"""
home = os.path.expanduser("~")
files, skipped = [], []
for directory, folders, names in os.walk(os.path.abspath(arguments["root"])):
folders.sort()
for name in sorted(names):
path = os.path.join(directory, name)
relative = os.path.relpath(path, home)
if os.path.islink(path) or not os.path.isfile(path):
skipped.append(relative)
continue
digest = hashlib.sha256()
with open(path, "rb") as handle:
for chunk in iter(lambda: handle.read(1 << 20), b""):
digest.update(chunk)
files.append({"path": relative, "size": os.path.getsize(path), "sha256": digest.hexdigest()})
output = os.path.abspath(arguments["output"])
os.makedirs(os.path.dirname(output), exist_ok=True)
with open(output, "w", encoding="utf-8") as handle:
json.dump({"files": files, "skipped": skipped}, handle)
return _say({"listing_bytes": os.path.getsize(output)})
COMMANDS = {"start": start, "mark": mark, "stop": stop, "run": run, "list": listing}
if __name__ == "__main__":
sys.exit(COMMANDS[sys.argv[1]](json.loads(sys.argv[2])))
+83
View File
@@ -0,0 +1,83 @@
"""Keep one agentenv computer up for REA until stdin closes or SIGTERM arrives.
REA runs ``computer.py <bundle folder> [task]`` with the agentenv runner's own interpreter, so ``agent_env``
imports. It deploys the bundle's task, ``smoke`` by default, on this machine with
``run_bundle(keep=True, emulate=False)``, prints one JSON line naming the computer's address, MCP URL, control
token and sandbox id, or ``{"error": ...}``, and tears the computer down once its stdin closes or it gets SIGTERM
or SIGINT. Everything else it or agentenv prints, a build's output included, goes to stderr. It exits 0 once
the teardown left nothing up, 1 when it did, and 2 when no computer came up.
"""
from __future__ import annotations
import json
import os
import signal
import sys
import threading
def _protocol():
"""A private copy of stdout for the JSON line; fd 1 then points at stderr, as do children that inherit it."""
protocol = os.fdopen(os.dup(1), "w", encoding="utf-8")
os.dup2(2, 1)
sys.stdout = sys.stderr
return protocol
def _say(protocol, value: dict) -> None:
protocol.write(json.dumps(value) + "\n")
protocol.flush()
def _computer(result):
"""The run's deployed computer, or why there is none."""
run = result.runs[0] if result.runs else None
if run is None:
return None, "the bundle ran no task"
if run.error is not None or run.failed_steps:
return None, f"the task failed: {run.error or run.failed_steps}"
for env in run.context.deployed_envs:
if env.environment_url and env.mcp_url and getattr(env, "sandbox_id", None):
return env, None
return None, "the task deployed no computer"
def main(argv: list[str]) -> int:
protocol = _protocol()
bundle, task = argv[1], argv[2] if len(argv) > 2 else "smoke"
try:
from agent_env.bundle import run_bundle
except ImportError as error:
_say(protocol, {"error": f"agent_env does not import with {sys.executable}: {error}"})
return 2
try:
result = run_bundle(bundle, tasks=[task], keep=True, emulate=False, sandbox="local",
on_progress=lambda line: print(line, file=sys.stderr, flush=True))
except BaseException as error: # noqa: BLE001 - a signal's RunInterrupted too; run_bundle tore its runs down
_say(protocol, {"error": f"{type(error).__name__}: {error}"})
return 2
stop = threading.Event()
for signum in (signal.SIGTERM, signal.SIGINT):
signal.signal(signum, lambda *_: stop.set())
env, problem = _computer(result)
try:
if env is None:
_say(protocol, {"error": problem})
else:
_say(protocol, {"base_url": env.environment_url, "mcp_url": env.mcp_url,
"control_token": env.control_token, "sandbox_id": env.sandbox_id})
threading.Thread(target=lambda: (sys.stdin.buffer.read(), stop.set()), daemon=True).start()
while not stop.wait(0.5):
pass
finally:
torn_down = result.teardown()
still_up = [sandbox for run in torn_down.runs if run.torn_down is not None for sandbox in run.torn_down.still_up]
if still_up:
print(f"still up after the teardown: {still_up}", file=sys.stderr, flush=True)
return 1
return 0 if env is not None else 2
if __name__ == "__main__":
sys.exit(main(sys.argv))
+630 -35
View File
@@ -1,19 +1,35 @@
import com.atxx.jhmcp.*;
import com.google.gson.*;
import com.android.tools.smali.dexlib2.Opcodes;
import com.android.tools.smali.dexlib2.dexbacked.DexBackedDexFile;
import com.android.tools.smali.dexlib2.dexbacked.DexBackedClassDef;
import com.android.tools.smali.dexlib2.dexbacked.DexBackedMethod;
import io.modelcontextprotocol.kotlin.sdk.server.*;
import io.modelcontextprotocol.kotlin.sdk.types.*;
import jadx.api.*;
import jadx.api.plugins.input.data.annotations.EncodedValue;
import jadx.api.plugins.input.data.attributes.JadxAttrType;
import jadx.api.plugins.input.data.ICodeReader;
import jadx.api.plugins.input.data.IMethodRef;
import jadx.api.plugins.input.insns.InsnIndexType;
import jadx.api.plugins.input.insns.Opcode;
import jadx.core.codegen.TypeGen;
import jadx.core.dex.info.MethodInfo;
import jadx.core.dex.instructions.args.ArgType;
import jadx.core.dex.nodes.*;
import jadx.core.xmlgen.BinaryXMLParser;
import java.io.PrintStream;
import java.io.StringReader;
import java.io.InputStream;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.stream.Collectors;
import java.util.zip.CRC32;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import kotlin.Unit;
import kotlin.ResultKt;
import kotlin.coroutines.Continuation;
@@ -23,6 +39,10 @@ import kotlin.coroutines.intrinsics.IntrinsicsKt;
import kotlinx.coroutines.BuildersKt;
import kotlinx.io.CoreKt;
import kotlinx.io.JvmCoreKt;
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 {
@@ -30,15 +50,27 @@ class ReaJadxBridge {
private final SessionHolder holder;
private JadxSession indexedSession;
private Map<String, List<ClassSnapshot>> classes = Map.of();
private JadxSession manifestSession;
private ManifestSnapshot manifestSnapshot;
private ExternalInventory externalInventory;
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 ClassSnapshot(JavaClass node, List<MethodSnapshot> methods, Map<String, Object> summary,
Map<String, Object> summaryWithValues) {}
private record ManifestSnapshot(String text, Map<String, Object> summary) {}
private record ExternalMethod(MethodInfo info, Map<String, Object> summary) {}
private record ExternalInventory(JadxSession session, String className, String methodName,
List<ExternalMethod> methods) {}
private static final class InputFailure extends IllegalArgumentException {
final String field;
InputFailure(String field, String message) { super(message); this.field = field; }
}
private static final class ReferenceLimit extends IllegalArgumentException {
final String stage;
ReferenceLimit(String stage) { super("External reference representation limit"); this.stage = stage; }
}
private ReaJadxBridge(SessionHolder holder) { this.holder = holder; }
@@ -93,6 +125,7 @@ class ReaJadxBridge {
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);
}
@@ -133,6 +166,14 @@ class ReaJadxBridge {
return value.getAsString();
}
private static boolean flag(JsonObject args, String name) {
JsonElement value = args.get(name);
if (value == null) return false;
if (!value.isJsonPrimitive() || !value.getAsJsonPrimitive().isBoolean())
throw new IllegalArgumentException(name + " must be a boolean");
return value.getAsBoolean();
}
private static int integer(JsonObject args, String name, int fallback) {
JsonElement value = args.get(name);
if (value == null) return fallback;
@@ -153,7 +194,292 @@ class ReaJadxBridge {
return current;
}
private ClassSnapshot selected(JsonObject args) {
private void clearManifest() {
manifestSession = null;
manifestSnapshot = null;
externalInventory = null;
}
// One bounded DEX at a time, beside the already admitted analysis JVM. The
// response allowance accounts for worst-case JSON escaping before retention.
private static final int NATIVE_DEX_BYTES = 64 * 1024 * 1024;
private static final long NATIVE_TOTAL_DEX_BYTES = 256L * 1024 * 1024;
// This JSON is escaped again inside an 8 MiB MCP frame.
private static final long NATIVE_JSON_BYTES = 4L * 1024 * 1024;
/** The bundled decoder allocates its declared UTF-16 length before validating bytes. */
private static final class NativeDex extends DexBackedDexFile {
private OptionalIndexedSection<String> boundedStrings;
NativeDex(byte[] bytes) { super(Opcodes.getDefault(), bytes); }
@Override public OptionalIndexedSection<String> getStringSection() {
if (boundedStrings == null) {
var source = super.getStringSection();
boundedStrings = new OptionalIndexedSection<>() {
@Override public int size() { return source.size(); }
@Override public int getOffset(int index) { return source.getOffset(index); }
@Override public String getOptional(int index) { return index == -1 ? null : get(index); }
@Override public String get(int index) {
int offset = getBuffer().readSmallUint(source.getOffset(index));
int units = getDataBuffer().readerAt(offset).readSmallUleb128();
if (units > NATIVE_JSON_BYTES / 6
|| units > getDataBuffer().getBuf().length - offset)
throw new IllegalArgumentException("DEX string exceeds the native decoding allowance");
return source.get(index);
}
};
}
return boundedStrings;
}
}
private static void checkNativeInterrupted() {
if (Thread.currentThread().isInterrupted())
throw new java.util.concurrent.CancellationException("Native declaration inventory interrupted");
}
private static final class NativeInventory {
final List<Map<String, Object>> methods = new ArrayList<>();
final List<String> unknowns = new ArrayList<>();
long retainedBytes = 1024;
int dexEntries;
int examinedDexEntries;
int examinedClasses;
int examinedMethods;
boolean partial;
boolean diagnosticLimit;
void unknown(String reason) {
partial = true;
long bytes = 64L + reason.length() * 6L;
if (unknowns.size() < 16 && retainedBytes + bytes <= NATIVE_JSON_BYTES - 4096) {
unknowns.add(reason);
retainedBytes += bytes;
} else if (!diagnosticLimit) {
diagnosticLimit = true;
unknowns.add("Additional native-inventory limitations exceed the retained diagnostic allowance.");
}
}
boolean retain(Map<String, Object> method, long bytes) {
if (retainedBytes + bytes > NATIVE_JSON_BYTES - 4096) {
unknown("Native declarations exceed the 4 MiB retained JSON allowance; remaining declarations are unknown.");
return false;
}
retainedBytes += bytes;
methods.add(method);
return true;
}
Map<String, Object> result() {
return Map.of("scope", "apk-root-dex-declarations", "methods", List.copyOf(methods),
"coverage", Map.of("status", partial ? "partial" : "complete",
"dex_entries", dexEntries, "examined_dex_entries", examinedDexEntries,
"examined_classes", examinedClasses, "examined_methods", examinedMethods),
"unknowns", List.copyOf(unknowns));
}
}
/** Match Android's standard root multidex carriers, without a basename guess. */
private static boolean nativeDexEntry(String name) {
return name.equals("classes.dex") || name.matches("classes[0-9]+\\.dex");
}
private static byte[] readNativeDex(ZipFile archive, ZipEntry entry) throws IOException {
long size = entry.getSize();
if (size < 0 || size > NATIVE_DEX_BYTES)
throw new IllegalArgumentException("DEX exceeds the 64 MiB single-member read allowance");
byte[] bytes = new byte[(int) size];
CRC32 crc = new CRC32();
try (InputStream input = archive.getInputStream(entry)) {
int offset = 0;
while (offset < bytes.length) {
checkNativeInterrupted();
int read = input.read(bytes, offset, Math.min(65536, bytes.length - offset));
if (read < 0) throw new IOException("DEX is shorter than its ZIP declaration");
if (read == 0) continue;
crc.update(bytes, offset, read);
offset += read;
}
if (input.read() != -1) throw new IOException("DEX exceeds its ZIP declaration");
}
if (entry.getCrc() < 0 || entry.getCrc() != crc.getValue())
throw new IOException("DEX does not match its ZIP CRC32 declaration");
return bytes;
}
/** Raw DEX ownership precedes JADX's duplicate-class selection and aliases. */
static Map<String, Object> nativeDeclarations(Path apk) {
NativeInventory inventory = new NativeInventory();
try (ZipFile archive = new ZipFile(apk.toFile())) {
Map<String, Integer> names = new HashMap<>();
var directory = archive.entries();
while (directory.hasMoreElements()) {
checkNativeInterrupted();
ZipEntry entry = directory.nextElement();
if (!entry.isDirectory() && nativeDexEntry(entry.getName())) {
inventory.dexEntries++;
names.merge(entry.getName(), 1, Integer::sum);
}
}
long totalBytes = 0;
int entryIndex = -1;
directory = archive.entries();
while (directory.hasMoreElements()) {
checkNativeInterrupted();
ZipEntry entry = directory.nextElement();
entryIndex++;
String path = entry.getName();
if (entry.isDirectory() || !nativeDexEntry(path)) continue;
if (names.get(path) != 1) {
inventory.unknown("Duplicate root DEX entry name cannot be selected uniquely by the ZIP reader: " + path);
continue;
}
if (entry.getSize() < 0 || entry.getSize() > NATIVE_DEX_BYTES
|| entry.getSize() > NATIVE_TOTAL_DEX_BYTES - totalBytes) {
inventory.unknown("Root DEX member exceeds the 64 MiB member or 256 MiB total read allowance: " + path);
continue;
}
totalBytes += entry.getSize();
try {
DexBackedDexFile dex = new NativeDex(readNativeDex(archive, entry));
var classes = dex.getClassSection();
for (int classIndex = 0; classIndex < classes.size(); classIndex++) {
checkNativeInterrupted();
DexBackedClassDef cls = classes.get(classIndex);
String type = cls.getType();
if (!type.startsWith("L") || !type.endsWith(";"))
throw new IllegalArgumentException("Class definition has no reference type");
String className = type.substring(1, type.length() - 1).replace('/', '.');
// Passing false retains duplicate encoded method references too.
for (Iterable<? extends DexBackedMethod> group : List.of(
cls.getDirectMethods(false), cls.getVirtualMethods(false))) {
for (DexBackedMethod method : group) {
checkNativeInterrupted();
inventory.examinedMethods++;
if ((method.getAccessFlags() & 0x100) == 0) continue;
String methodName = method.getName();
String returnType = method.getReturnType();
long characters = 2L + returnType.length();
for (CharSequence parameter : method.getParameterTypes()) {
characters += parameter.length();
if (characters * 6L > NATIVE_JSON_BYTES) break;
}
long bytes = 512L + 6L * (className.length() + methodName.length()
+ path.length() + characters);
if (inventory.retainedBytes + bytes > NATIVE_JSON_BYTES - 4096) {
inventory.unknown("Native declarations exceed the 4 MiB retained JSON allowance; remaining declarations are unknown.");
return inventory.result();
}
StringBuilder descriptor = new StringBuilder((int) characters).append('(');
for (CharSequence parameter : method.getParameterTypes()) descriptor.append(parameter);
descriptor.append(')').append(returnType);
Map<String, Object> row = Map.of("source_index", inventory.methods.size(),
"class_name", className, "method_name", methodName, "descriptor", descriptor.toString(),
"dex_entry", path, "dex_entry_index", entryIndex, "class_index", classIndex,
"method_index", method.getMethodIndex());
if (!inventory.retain(row, bytes)) return inventory.result();
}
}
inventory.examinedClasses++;
}
inventory.examinedDexEntries++;
} catch (IOException | RuntimeException failure) {
if (failure instanceof java.util.concurrent.CancellationException) throw (RuntimeException) failure;
inventory.unknown("Root DEX native declarations could not be fully read from " + path + ": "
+ failure.getClass().getSimpleName()
+ (failure.getMessage() == null ? "" : " (" + failure.getMessage() + ")"));
}
}
} catch (IOException failure) {
// A failed archive close must fail the operation so the provider retires
// its owned JVM; do not cache a result with lost file-resource ownership.
throw new IllegalArgumentException("APK ZIP native declaration inventory could not finish its reader", failure);
}
return inventory.result();
}
/** 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");
String xml = manifestText(selected);
ManifestSnapshot snapshot = new ManifestSnapshot(xml, manifestSummary(xml));
manifestSnapshot = snapshot;
manifestSession = current;
}
return manifestSnapshot;
}
/** Decode with resource names but without JADX's class alias substitution. */
static String manifestText(ResourceFile resource) {
var decompiler = resource.getDecompiler();
// BinaryXMLParser resolves android:name against its root's class identities
// and replaces raw '$' and relative names with Java-style aliases. A fresh
// root owns no classes, while its resource-name table preserves reference
// rendering from the loaded target.
RootNode root = new RootNode(decompiler.getArgs());
root.getConstValues().setResourcesNames(decompiler.getRoot().getConstValues().getResourcesNames());
try {
return ResourcesLoader.decodeStream(resource,
(size, input) -> new BinaryXMLParser(root).parse(input).getCodeStr());
} catch (jadx.core.utils.exceptions.JadxException failure) {
throw new IllegalArgumentException("Root AndroidManifest.xml did not decode as text", failure);
}
}
/** 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 void indexClasses() {
JadxSession current = session();
if (indexedSession != current) {
Map<String, List<ClassSnapshot>> index = new LinkedHashMap<>();
@@ -179,27 +505,60 @@ class ReaJadxBridge {
.map(Object::toString).collect(Collectors.joining(", "))
+ "): " + method.getReturnType().toString();
return new MethodSnapshot(name, rawName, shortId, List.copyOf(bodyAbsenceMarkers), Map.of(
"name", name, "signature", signature,
"name", name, "raw_name", rawName, "signature", signature,
"dex_descriptor", descriptor,
"is_constructor", method.isConstructor(), "def_pos", 0));
}).toList();
List<Map<String, Object>> fields = node.getFields().stream()
.sorted(Comparator.comparing(FieldNode::getAlias))
.map(field -> Map.<String, Object>of("name", field.getAlias(),
"type", field.getType().toString(), "def_pos", 0)).toList();
List<FieldNode> orderedFields = new ArrayList<>(node.getFields());
orderedFields.sort(Comparator.comparing(FieldNode::getAlias));
List<Map<String, Object>> fields = new ArrayList<>();
List<Map<String, Object>> valuedFields = new ArrayList<>();
boolean anyValue = false;
for (FieldNode field : orderedFields) {
// Raw DEX identity for mapping lookups; the display type can carry generics.
Map<String, Object> entry = Map.of("name", field.getAlias(),
"raw_name", field.getFieldInfo().getName(),
"type", field.getType().toString(),
"dex_type", TypeGen.signature(field.getFieldInfo().getType()), "def_pos", 0);
fields.add(entry);
Map<String, Object> value = dexStaticValue(field);
if (value == null) valuedFields.add(entry);
else {
anyValue = true;
Map<String, Object> valued = new LinkedHashMap<>(entry);
valued.put("dex_static_value", value);
valuedFields.add(Collections.unmodifiableMap(valued));
}
}
List<String> inner = node.getInnerClasses().stream().map(ClassNode::getFullName).toList();
List<String> innerRaw = node.getInnerClasses().stream().map(ClassNode::getRawName).toList();
Map<String, Object> summary = Map.of(
"full_name", cls.getFullName(), "name", cls.getName(),
"full_name", cls.getFullName(), "raw_name", cls.getRawName(), "name", cls.getName(),
"method_count", methods.size(), "field_count", fields.size(),
"inner_class_count", inner.size(), "inner_classes", inner,
"methods", methods.stream().map(MethodSnapshot::summary).toList(), "fields", fields);
index.computeIfAbsent(cls.getFullName(), ignored -> new ArrayList<>())
.add(new ClassSnapshot(cls, methods, summary));
"inner_class_count", inner.size(), "inner_classes", inner, "inner_class_raw_names", innerRaw,
"methods", methods.stream().map(MethodSnapshot::summary).toList(), "fields", List.copyOf(fields));
// DEX static values are opt-in, so method and reference lookups keep the member-only reply.
Map<String, Object> summaryWithValues = summary;
if (anyValue) {
Map<String, Object> copy = new LinkedHashMap<>(summary);
copy.put("fields", List.copyOf(valuedFields));
summaryWithValues = Collections.unmodifiableMap(copy);
}
ClassSnapshot snapshot = new ClassSnapshot(cls, methods, summary, summaryWithValues);
index.computeIfAbsent(cls.getFullName(), ignored -> new ArrayList<>()).add(snapshot);
// Keep the binary identity supplied by JADX; '$' can also be a literal
// name character, so replacing separators would invent lookup aliases.
if (!cls.getRawName().equals(cls.getFullName()))
index.computeIfAbsent(cls.getRawName(), ignored -> new ArrayList<>()).add(snapshot);
}
}
classes = Collections.unmodifiableMap(index);
indexedSession = current;
}
}
private ClassSnapshot selected(JsonObject args) {
indexClasses();
String name = string(args, "class_name");
List<ClassSnapshot> candidates = classes.get(name);
if (candidates == null) throw new InputFailure("class_name", "Exact class not found: " + name
@@ -209,6 +568,28 @@ class ReaJadxBridge {
return candidates.get(0);
}
/** A static final field's class-definition static value; a static initializer can still assign it. */
private static Map<String, Object> dexStaticValue(FieldNode field) {
if (!field.getAccessFlags().isStatic() || !field.getAccessFlags().isFinal()) return null;
EncodedValue encoded = field.get(JadxAttrType.CONSTANT_VALUE);
if (encoded == null) return null;
Object value = encoded.getValue();
// JADX also attaches the shared null to static finals without a value, so null is never reported.
// Wide and floating values keep their Java text instead of a lossy JSON number.
return switch (encoded.getType()) {
case ENCODED_BOOLEAN -> Map.of("type", "boolean", "value", value);
case ENCODED_BYTE -> Map.of("type", "byte", "value", value);
case ENCODED_SHORT -> Map.of("type", "short", "value", value);
case ENCODED_CHAR -> Map.of("type", "char", "value", (int) (Character) value);
case ENCODED_INT -> Map.of("type", "int", "value", value);
case ENCODED_LONG -> Map.of("type", "long", "value", value.toString());
case ENCODED_FLOAT -> Map.of("type", "float", "value", value.toString());
case ENCODED_DOUBLE -> Map.of("type", "double", "value", value.toString());
case ENCODED_STRING -> Map.of("type", "string", "value", value);
default -> null;
};
}
private List<MethodSnapshot> methods(ClassSnapshot cls, JsonObject args) {
String name = string(args, "method_name");
List<MethodSnapshot> result = cls.methods().stream().filter(method -> method.name().equals(name)).toList();
@@ -277,12 +658,18 @@ class ReaJadxBridge {
// JSON-RPC/CallToolResult envelope and generated numeric request id, and
// account for JSON text being escaped again inside TextContent.
private static final int MAX_XREF_TEXT_BYTES = 8 * 1024 * 1024 - 1024;
// Leave room for the prototype reply, reference reply, retained raw calls,
// normalized Evidence and their text/structured MCP copies, not just one frame.
private static final int MAX_EXTERNAL_XREF_BYTES = MAX_XREF_TEXT_BYTES / 8;
private static int escapedJsonBytes(Object value) {
return JSON.toJson(JSON.toJson(value)).getBytes(StandardCharsets.UTF_8).length;
}
private static final class InvokeBudget {
long remaining;
InvokeBudget(long remaining) { this.remaining = Math.max(0, remaining); }
boolean fitsText(long characters) {
return remaining >= 256 && characters <= (remaining - 256) / 12;
}
boolean admit(Map<String, Object> site) {
// Check a conservative two-layer escape bound before serialization.
long bound = 256;
@@ -297,26 +684,55 @@ class ReaJadxBridge {
}
private record DirectInvokes(List<Map<String, Object>> sites, boolean exhaustive) {}
/** Raw DEX invokes within one JADX using method; offsets are code units from its code start. */
private static DirectInvokes directInvokes(JavaMethod source, MethodInfo target, InvokeBudget budget) {
MethodNode node = source.getMethodNode();
@FunctionalInterface private interface InvokeVisitor {
void accept(MethodInfo target, int offset, int index, Opcode opcode);
}
/** Decode raw invoke identity once at the shared bridge boundary, without generating source. */
private static void visitInvokes(MethodNode node, InvokeVisitor visitor) {
checkNativeInterrupted();
ICodeReader code = node.getCodeReader();
if (code == null) return null;
List<Map<String, Object>> sites = new ArrayList<>();
boolean[] exhaustive = {true};
if (code == null) return;
code.visitInstructions(insn -> {
checkNativeInterrupted();
insn.decode();
if (insn.getIndexType() != InsnIndexType.METHOD_REF) return;
Opcode opcode = insn.getOpcode();
if (!opcode.name().startsWith("INVOKE_")) return;
IMethodRef ref = insn.getIndexAsMethod();
ref.load();
if (!MethodInfo.fromRef(node.root(), ref).getRawFullId().equals(target.getRawFullId())) return;
visitor.accept(MethodInfo.fromRef(node.root(), ref), insn.getOffset(), insn.getIndex(), opcode);
});
}
/** Raw DEX invokes within one JADX using method; offsets are code units from its code start. */
private static DirectInvokes directInvokes(JavaMethod source, MethodInfo target, InvokeBudget budget) {
return directInvokes(source.getMethodNode(), target, budget);
}
private static void visitDirectInvokes(MethodNode node, MethodInfo target, InvokeVisitor visitor) {
visitInvokes(node, (referenced, offset, index, opcode) -> {
if (referenced.getRawFullId().equals(target.getRawFullId()))
visitor.accept(referenced, offset, index, opcode);
});
}
private static boolean referencesMethod(MethodNode node, MethodInfo target) {
boolean[] found = {false};
visitDirectInvokes(node, target, (referenced, offset, index, opcode) -> found[0] = true);
return found[0];
}
private static DirectInvokes directInvokes(MethodNode node, MethodInfo target, InvokeBudget budget) {
if (node.getCodeReader() == null) return null;
List<Map<String, Object>> sites = new ArrayList<>();
boolean[] exhaustive = {true};
visitDirectInvokes(node, target, (referenced, offset, index, opcode) -> {
Map<String, Object> site = Map.of(
"dex_entry", node.getInputFileName(),
"source_raw_method_id", node.getMethodInfo().getRawFullId(),
"dex_code_unit_offset", insn.getOffset(),
"dex_method_index", insn.getIndex(),
"dex_code_unit_offset", offset,
"dex_method_index", index,
"opcode", opcode.name());
if (budget.admit(site)) sites.add(site);
else exhaustive[0] = false;
@@ -324,6 +740,143 @@ class ReaJadxBridge {
return new DirectInvokes(sites, exhaustive[0]);
}
private List<ClassSnapshot> sourceClasses() {
indexClasses();
Set<ClassSnapshot> seen = Collections.newSetFromMap(new IdentityHashMap<>());
List<ClassSnapshot> result = new ArrayList<>();
for (List<ClassSnapshot> group : classes.values())
for (ClassSnapshot source : group) if (seen.add(source)) result.add(source);
result.sort(Comparator.comparing(source -> source.node().getRawName()));
return result;
}
private static long displayTypeCharacters(ArgType type) {
if (type.isArray())
return 2L * type.getArrayDimension() + displayTypeCharacters(type.getArrayRootElement());
if (type.isPrimitive()) return type.getPrimitiveType().getLongName().length();
if (type.isObject() && !type.isGeneric() && !type.isGenericType()) return type.getObject().length();
throw new IllegalArgumentException("External prototype contains a non-DEX type");
}
private static Map<String, Object> externalMethodSummary(MethodInfo info, InvokeBudget budget) {
String name = info.getName();
long signatureCharacters = name.length() + 4L + displayTypeCharacters(info.getReturnType())
+ 2L * Math.max(0, info.getArgsCount() - 1);
for (ArgType type : info.getArgumentsTypes()) signatureCharacters += displayTypeCharacters(type);
long descriptorCharacters = info.getShortId().length() - (long) name.length();
// Use parsed type facts and the existing short-id length before copying/joining strings.
if (!budget.fitsText(2L * name.length() + descriptorCharacters + signatureCharacters))
throw new ReferenceLimit("method_inventory");
String descriptor = info.getShortId().substring(name.length());
String signature = name + "(" + info.getArgumentsTypes().stream()
.map(Object::toString).collect(Collectors.joining(", "))
+ "): " + info.getReturnType();
Map<String, Object> summary = Map.of("name", name, "raw_name", name, "signature", signature,
"dex_descriptor", descriptor, "is_constructor", info.isConstructor(), "def_pos", 0);
if (!budget.admit(summary)) throw new ReferenceLimit("method_inventory");
return summary;
}
/** Only directly referenced prototypes, not invented definitions or an external classpath. */
private List<ExternalMethod> externalMethods(JsonObject args) {
String className = string(args, "class_name");
String methodName = string(args, "method_name");
JadxSession current = session();
if (externalInventory != null && externalInventory.session() == current
&& externalInventory.className().equals(className) && externalInventory.methodName().equals(methodName))
return externalInventory.methods();
// Retain only the latest bounded selection, never results from a replaced APK/session.
externalInventory = null;
SortedMap<String, ExternalMethod> methods = new TreeMap<>();
boolean[] observedClass = {false};
InvokeBudget budget = new InvokeBudget(MAX_EXTERNAL_XREF_BYTES
- 4096L - 12L * (className.length() + (long) methodName.length()));
for (ClassSnapshot source : sourceClasses()) {
for (MethodSnapshot selected : source.methods()) {
visitInvokes(method(source, selected).getMethodNode(), (info, offset, index, opcode) -> {
if (!info.getDeclClass().getRawName().equals(className)) return;
observedClass[0] = true;
if (!info.getName().equals(methodName) || methods.containsKey(info.getShortId())) return;
methods.put(info.getShortId(), new ExternalMethod(info, externalMethodSummary(info, budget)));
});
}
}
if (!observedClass[0]) throw new InputFailure("class_name",
"No APK definition or direct DEX invoke references the exact class: " + className);
if (methods.isEmpty()) throw new InputFailure("method_name",
"No direct DEX invoke references " + className + "." + methodName);
List<ExternalMethod> result = List.copyOf(methods.values());
externalInventory = new ExternalInventory(current, className, methodName, result);
return result;
}
private Map<String, Object> externalSummary(JsonObject args) {
List<ExternalMethod> methods = externalMethods(args);
return Map.of("class_name", string(args, "class_name"), "reference_target", "external",
"observed_method_count", methods.size(),
"prototypes", methods.stream().map(ExternalMethod::summary).toList());
}
private record ExternalCaller(MethodNode node, Map<String, Object> row) {}
/** Reserve the complete using-node inventory before optional instruction positions. */
private List<ExternalCaller> externalCallers(MethodInfo target, InvokeBudget budget) {
List<ExternalCaller> callers = new ArrayList<>();
for (ClassSnapshot source : sourceClasses()) {
String className = String.valueOf(source.summary().get("full_name"));
Map<String, Integer> overloads = new HashMap<>();
for (MethodSnapshot candidate : source.methods()) {
int overload = overloads.merge(candidate.name(), 1, Integer::sum) - 1;
MethodNode node = method(source, candidate).getMethodNode();
if (!referencesMethod(node, target)) continue;
if (classes.get(className).size() != 1)
throw new IllegalArgumentException("External caller has an ambiguous APK class identity: " + className);
Map<String, Object> row = new LinkedHashMap<>(Map.of(
"kind", "method", "name", candidate.name(), "full_name", className + "." + candidate.name(),
"containing_class", className, "top_class", source.node().getTopParentClass().getFullName(),
"def_pos", 0, "source_selector", Map.of("class_name", className,
"method_name", candidate.name(), "overload_index", overload),
"direct_invokes", List.of(), "direct_invokes_exhaustive", false));
if (!budget.admit(row)) throw new ReferenceLimit("caller_inventory");
callers.add(new ExternalCaller(node, row));
}
}
return List.copyOf(callers);
}
private Map<String, Object> externalReferences(JsonObject args) {
List<ExternalMethod> methods = externalMethods(args);
if (methods.size() != 1 && !args.has("overload_index"))
throw new InputFailure("overload_index", "Select one of " + methods.size() + " observed external prototypes");
int index = integer(args, "overload_index", 0);
if (index >= methods.size()) throw new InputFailure("overload_index", "External prototype index out of range: " + index);
ExternalMethod method = methods.get(index);
MethodInfo target = method.info();
Map<String, Object> selected = method.summary();
InvokeBudget budget = new InvokeBudget(MAX_EXTERNAL_XREF_BYTES
- 4096L - escapedJsonBytes(selected));
List<ExternalCaller> callers = externalCallers(target, budget);
List<Map<String, Object>> items = new ArrayList<>();
for (ExternalCaller caller : callers) {
DirectInvokes invokes = directInvokes(caller.node(), target, budget);
caller.row().put("direct_invokes", invokes.sites());
caller.row().put("direct_invokes_exhaustive", invokes.exhaustive());
items.add(caller.row());
}
Map<String, Object> result = new LinkedHashMap<>();
result.put("target", string(args, "class_name") + "." + target.getName());
result.put("reference_target", "external");
result.put("reference_scope", "direct_dex_invokes");
result.put("dex_descriptor", selected.get("dex_descriptor"));
result.put("reported_signature", selected.get("signature"));
result.put("overload_index", index);
result.put("total", items.size());
result.put("count", items.size());
result.put("resolve_line", false);
result.put("items", items);
return result;
}
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(
@@ -332,7 +885,12 @@ class ReaJadxBridge {
"available_processors", Runtime.getRuntime().availableProcessors(),
"java_version", System.getProperty("java.version"),
"metadata_scope", "parsed_members"));
if (name.equals("rea_native_declarations"))
return json(nativeDeclarations(Path.of(string(args, "path"))));
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") : "";
@@ -355,8 +913,15 @@ class ReaJadxBridge {
"scope", "top_level", "offset", offset, "limit", limit,
"items", items));
}
indexClasses();
if (flag(args, "reference_target") && !classes.containsKey(string(args, "class_name"))) {
if (name.equals("get_class_summary")) return json(externalSummary(args));
if (name.equals("get_xrefs_to_method")) return json(externalReferences(args));
throw new InputFailure("class_name", "External prototypes are only available to method reference queries");
}
ClassSnapshot cls = selected(args);
if (name.equals("get_class_summary")) return json(cls.summary());
if (name.equals("get_class_summary"))
return json(flag(args, "dex_static_values") ? cls.summaryWithValues() : cls.summary());
if (name.equals("get_method_body")) {
List<MethodSnapshot> overloads = methods(cls, args);
int index = integer(args, "overload_index", 0);
@@ -371,7 +936,7 @@ class ReaJadxBridge {
"markers", code.getMarkers(), "body", code.getText()));
}
List<JavaNode> uses;
String target = string(args, "class_name");
String target = (String) cls.summary().get("full_name");
MethodSnapshot selectedMethod = null;
int overloadIndex = 0;
if (name.equals("get_xrefs_to_class")) uses = cls.node().getUseIn();
@@ -420,6 +985,20 @@ 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 (ReferenceLimit error) { return text(JSON.toJson(Map.of(
"kind", "reference-output-limit", "stage", error.stage,
"limit_bytes", MAX_EXTERNAL_XREF_BYTES)), 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);
@@ -428,10 +1007,9 @@ class ReaJadxBridge {
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", "4", null, null, null),
Server server = new Server(new Implementation("rea-jadx-bridge", "10", 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);
@@ -440,7 +1018,7 @@ class ReaJadxBridge {
server.removeTool("load_apk");
server.addTool(loadTool, (connection, request, continuation) ->
bridge.load(request.getArguments(), defaults, continuation));
Set<String> consumed = Set.of("load_apk", "get_app_info", "get_android_manifest",
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()))
@@ -449,6 +1027,18 @@ class ReaJadxBridge {
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()));
registerBridgeTool(server, bridge, "rea_native_declarations",
new ToolSchema(null, new kotlinx.serialization.json.JsonObject(Map.of("path",
new kotlinx.serialization.json.JsonObject(Map.of("type",
kotlinx.serialization.json.JsonElementKt.JsonPrimitive("string"))))), List.of("path"), null),
"Original ACC_NATIVE declarations from the admitted APK's standard root DEX members, without loading or decompiling the APK.");
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();
@@ -464,20 +1054,24 @@ class ReaJadxBridge {
if (name.equals("list_classes")) properties.put("query",
new kotlinx.serialization.json.JsonObject(Map.of(
"type", kotlinx.serialization.json.JsonElementKt.JsonPrimitive("string"))));
if (name.equals("get_class_summary")) properties.put("dex_static_values",
new kotlinx.serialization.json.JsonObject(Map.of(
"type", kotlinx.serialization.json.JsonElementKt.JsonPrimitive("boolean"))));
if (name.equals("get_class_summary") || name.equals("get_xrefs_to_method"))
properties.put("reference_target", new kotlinx.serialization.json.JsonObject(Map.of(
"type", kotlinx.serialization.json.JsonElementKt.JsonPrimitive("boolean"))));
if (name.equals("get_class_summary")) properties.put("method_name",
new kotlinx.serialization.json.JsonObject(Map.of(
"type", kotlinx.serialization.json.JsonElementKt.JsonPrimitive("string"))));
String description = switch (name) {
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_class_summary" -> "Parsed member metadata for one exact class name; no source generation. Set dex_static_values to include encoded static final field values.";
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 kotlinx.serialization.json.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; });
@@ -503,6 +1097,7 @@ class ReaJadxBridge {
BuildersKt.runBlocking(EmptyCoroutineContext.INSTANCE,
(scope, continuation) -> server.close(continuation));
} finally {
bridge.clearManifest();
BuildersKt.runBlocking(EmptyCoroutineContext.INSTANCE,
(scope, continuation) -> holder.unload(continuation));
}
+604 -99
View File
@@ -11,6 +11,7 @@ import json
import hashlib
import hmac
import os
import re
import secrets
import socket
import stat
@@ -33,9 +34,151 @@ except ImportError:
from PySide2.QtCore import QCoreApplication, QObject, Signal
MAX_FRAME_BYTES = 16 * 1024 * 1024
# 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())
)
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
@@ -480,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"
@@ -500,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"))
@@ -541,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
@@ -550,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, {
@@ -614,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]:
@@ -742,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):
@@ -775,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()
@@ -798,13 +1277,22 @@ def _within_response_limit(value: Any) -> bool:
# Unencodable output also needs the signed omission fallback.
return False
size += current.count('"') + current.count("\\")
size += sum(5 for character in current if ord(character) < 0x20)
# 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())
@@ -814,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;
}
}
File diff suppressed because it is too large. Load diff
+193
View File
@@ -0,0 +1,193 @@
import ghidra.app.script.GhidraScript;
import ghidra.program.database.mem.FileBytes;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.Memory;
import ghidra.program.model.mem.MemoryBlock;
import ghidra.program.model.mem.MemoryBlockSourceInfo;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
/**
* Rebuild a GameCube/Wii DOL memory map from the validated load ranges REA's TypeScript
* admission wrote. BinaryLoader maps the whole file once at address 0; this
* script replaces that block with text, data, and BSS blocks at their load addresses.
*/
public final class ReaGhidraPrepareDol extends GhidraScript {
// Canonical v2 header, 18 section rows, 12 gap rows and entry, using uint32 hex.
private static final int MAX_PLAN_BYTES = 13 + 18 * 49 + 12 * 30 + 17;
private static final class Section {
final String kind;
final int index;
final long fileOffset;
final long address;
final long size;
Section(String kind, int index, long fileOffset, long address, long size) {
this.kind = kind;
this.index = index;
this.fileOffset = fileOffset;
this.address = address;
this.size = size;
}
}
private record BssGap(long address, long size) {}
@Override
public void run() throws Exception {
String[] arguments = getScriptArgs();
if (arguments.length != 1) {
throw new IllegalArgumentException(
"GameCube DOL preparation requires exactly one section table path"
);
}
if (!currentProgram.getExecutableFormat().equals("Raw Binary") ||
!currentProgram.getLanguageID().getIdAsString().startsWith("PowerPC:BE:32:")) {
throw new IllegalArgumentException(
"GameCube DOL preparation requires a BinaryLoader import in big-endian 32-bit PowerPC"
);
}
Path tablePath = Path.of(arguments[0]);
if (!Files.isRegularFile(tablePath, LinkOption.NOFOLLOW_LINKS)) {
throw new IllegalArgumentException("GameCube DOL section table is not a regular file");
}
if (Files.size(tablePath) > MAX_PLAN_BYTES) {
throw new IllegalArgumentException("GameCube DOL plan exceeds the canonical format's byte bound");
}
List<Section> sections = new ArrayList<>();
List<BssGap> bssGaps = new ArrayList<>();
int expectedSections = -1;
int expectedGaps = -1;
long entry = -1;
for (String line : Files.readAllLines(tablePath)) {
String[] fields = line.trim().split("\\s+");
switch (fields[0]) {
case "plan" -> {
requireFields(fields, 4, line);
if (expectedSections >= 0 || !fields[1].equals("2")) {
throw new IllegalArgumentException("Duplicate or unsupported GameCube DOL plan header");
}
expectedSections = Integer.parseInt(fields[2]);
expectedGaps = Integer.parseInt(fields[3]);
// A DOL has 18 slots, including at most 11 data slots and 12 BSS gaps.
if (expectedSections < 1 || expectedSections > 18 || expectedGaps < 0 || expectedGaps > 12) {
throw new IllegalArgumentException("GameCube DOL plan counts exceed the format's slot bounds");
}
}
case "section" -> {
requireFields(fields, 6, line);
if (expectedSections < 0 || sections.size() >= expectedSections) {
throw new IllegalArgumentException("GameCube DOL section exceeds its declared count");
}
sections.add(new Section(
fields[1],
Integer.parseInt(fields[2]),
Long.decode(fields[3]),
Long.decode(fields[4]),
Long.decode(fields[5])
));
}
case "bss-gap" -> {
requireFields(fields, 3, line);
if (expectedGaps < 0 || bssGaps.size() >= expectedGaps) {
throw new IllegalArgumentException("GameCube DOL BSS gap exceeds its declared count");
}
long address = Long.decode(fields[1]);
long size = Long.decode(fields[2]);
if (address < 0 || size <= 0) {
throw new IllegalArgumentException("GameCube DOL BSS gap is not a positive range");
}
bssGaps.add(new BssGap(address, size));
}
case "entry" -> {
requireFields(fields, 2, line);
entry = Long.decode(fields[1]);
}
default -> throw new IllegalArgumentException("Unknown GameCube DOL table line: " + line);
}
}
if (expectedSections != sections.size() || expectedGaps != bssGaps.size() || entry < 0) {
throw new IllegalArgumentException("GameCube DOL section table is incomplete");
}
Memory memory = currentProgram.getMemory();
List<FileBytes> files = memory.getAllFileBytes();
if (files.size() != 1) {
throw new IllegalStateException("GameCube DOL preparation requires exactly one imported file image");
}
FileBytes fileBytes = files.get(0);
AddressSpace space = currentProgram.getAddressFactory().getDefaultAddressSpace();
// Remove the whole-file block before creating section blocks, so no two blocks claim one address.
for (MemoryBlock block : memory.getBlocks()) {
for (MemoryBlockSourceInfo source : block.getSourceInfos()) {
if (source.getFileBytes().isPresent() && source.getFileBytes().get().equals(fileBytes)) {
memory.removeBlock(block, monitor);
break;
}
}
}
for (Section section : sections) {
if (section.fileOffset + section.size > fileBytes.getSize()) {
throw new IllegalArgumentException(
"GameCube DOL " + section.kind + " " + section.index + " exceeds the imported file"
);
}
boolean text = section.kind.equals("text");
if (!text && !section.kind.equals("data")) {
throw new IllegalArgumentException("Unknown GameCube DOL section kind: " + section.kind);
}
MemoryBlock block = memory.createInitializedBlock(
"." + section.kind + section.index,
space.getAddress(section.address),
fileBytes,
section.fileOffset,
section.size,
false
);
block.setRead(true);
block.setWrite(!text);
block.setExecute(text);
}
// Apply the domain owner's exact uninitialized ranges; do not reinterpret BSS here.
int gapIndex = 0;
for (BssGap gap : bssGaps) {
MemoryBlock block = memory.createUninitializedBlock(
".bss" + gapIndex++,
space.getAddress(gap.address()),
gap.size(),
false
);
block.setRead(true);
block.setWrite(true);
block.setExecute(false);
}
Address entryAddress = space.getAddress(entry);
MemoryBlock entryBlock = memory.getBlock(entryAddress);
if (entryBlock == null || !entryBlock.isExecute()) {
throw new IllegalArgumentException("GameCube DOL entry must lie in an executable section");
}
addEntryPoint(entryAddress);
if (!disassemble(entryAddress) || createFunction(entryAddress, "__start") == null) {
throw new IllegalStateException("GameCube DOL entry cannot be decoded as a function");
}
currentProgram.getOptions(Program.PROGRAM_INFO).setBoolean("REA raw binary prepared", true);
currentProgram.getOptions(Program.PROGRAM_INFO).setBoolean("REA GameCube DOL prepared", true);
}
private static void requireFields(String[] fields, int count, String line) {
if (fields.length != count) {
throw new IllegalArgumentException("Malformed GameCube DOL table line: " + line);
}
}
}
@@ -0,0 +1,283 @@
import ghidra.app.script.GhidraScript;
import ghidra.program.database.SpecExtension;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.DoubleDataType;
import ghidra.program.model.data.Float16DataType;
import ghidra.program.model.data.FloatDataType;
import ghidra.program.model.data.FunctionDefinition;
import ghidra.program.model.data.IntegerDataType;
import ghidra.program.model.data.StructureDataType;
import ghidra.program.model.lang.CompilerSpec;
import ghidra.program.model.lang.Language;
import ghidra.program.model.lang.PrototypeModel;
import ghidra.program.model.lang.Register;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Parameter;
import ghidra.program.model.listing.Program;
import ghidra.program.model.listing.VariableStorage;
import ghidra.program.model.pcode.Varnode;
import ghidra.program.model.pcode.XmlEncode;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Pattern;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
/**
* Apply the committed RISC-V calling-convention policy after analysis. Ghidra's generic RV32
* compiler specification models ilp32d. Each policy derives a program-specific prototype from the
* program's own default prototype, as its {@link Policy} row and docs/ghidra-riscv.md state. The
* script installs the prototype as a specification extension and assigns it, with dynamic
* storage, to every non-thunk function, external function and function definition.
*/
public final class ReaGhidraRiscvCallingConvention extends GhidraScript {
static final String REPORT_KEY = "REA RISC-V calling convention";
/** RISC-V floating-point registers by ABI name: ft0-ft11, fs0-fs11 and fa0-fa7. */
private static final Pattern FLOAT_REGISTER = Pattern.compile("f[ast]\\d+");
/**
* What a policy changes in the default ilp32d prototype. Every policy also passes values of 9
* bytes or more, such as {@code long double}, by reference.
*
* @param floatEntryBytes the largest value a floating-point register may pass or return; 0
* removes those entries and every rule that assigns floating-point storage
* @param preservesFloatRegisters whether fs0-fs11 stay preserved across calls
*/
private record Policy(
String id, String prototype, int floatEntryBytes, boolean preservesFloatRegisters) {}
private static final List<Policy> POLICIES =
List.of(new Policy("ilp32-prototype-v1", "__rea_ilp32", 0, false));
@Override
public void run() throws Exception {
String[] arguments = getScriptArgs();
Policy policy =
POLICIES.stream()
.filter(candidate -> arguments.length == 1 && candidate.id().equals(arguments[0]))
.findFirst()
.orElseThrow(
() ->
new IllegalArgumentException(
"REA RISC-V calling convention requires one of the policies "
+ POLICIES.stream().map(Policy::id).toList()));
Language language = currentProgram.getLanguage();
if (!language.getProcessor().toString().equals("RISCV")
|| currentProgram.getDefaultPointerSize() != 4)
throw new IllegalStateException(
"REA " + policy.id() + " requires a 32-bit RISC-V program, not "
+ language.getLanguageID());
CompilerSpec spec = currentProgram.getCompilerSpec();
new SpecExtension(currentProgram)
.addReplaceCompilerSpecExtension(
derive(spec.getDefaultCallingConvention(), spec, language, policy), monitor);
PrototypeModel model =
currentProgram.getCompilerSpec().getCallingConvention(policy.prototype());
if (model == null)
throw new IllegalStateException("REA " + policy.prototype() + " was not installed");
selfCheck(model, policy, language);
int functions = 0;
List<Function> callables = new ArrayList<>();
currentProgram.getFunctionManager().getFunctions(true).forEach(callables::add);
currentProgram.getFunctionManager().getExternalFunctions().forEach(callables::add);
for (Function function : callables) {
// A thunk's signature is its target's; the target is in this list.
if (function.isThunk()) continue;
if (function.hasCustomVariableStorage()) function.setCustomVariableStorage(false);
function.setCallingConvention(policy.prototype());
if (policy.floatEntryBytes() == 0) {
List<VariableStorage> storage = new ArrayList<>();
storage.add(function.getReturn().getVariableStorage());
for (Parameter parameter : function.getParameters())
storage.add(parameter.getVariableStorage());
// An untyped function has no assigned storage yet; the decompiler applies the
// prototype when it recovers one.
requireIntegerStorage(
storage.toArray(VariableStorage[]::new), function.getName(true), true);
}
functions += 1;
}
int definitions = 0;
Iterator<DataType> types = currentProgram.getDataTypeManager().getAllDataTypes();
while (types.hasNext()) {
if (!(types.next() instanceof FunctionDefinition definition)) continue;
definition.setCallingConvention(policy.prototype());
definitions += 1;
}
// Written last: any failure above leaves no report, and REA refuses the session.
currentProgram.getOptions(Program.PROGRAM_INFO).setString(
REPORT_KEY,
"{\"policy\":\"" + policy.id() + "\",\"functions\":" + functions
+ ",\"definitions\":" + definitions + "}");
println(
"REA RISC-V " + policy.prototype() + ": functions=" + functions
+ " definitions=" + definitions);
}
/** The default ilp32d prototype changed as the policy states. */
private static String derive(
PrototypeModel base, CompilerSpec spec, Language language, Policy policy) throws Exception {
XmlEncode encoder = new XmlEncode();
base.encode(encoder, spec.getPcodeInjectLibrary());
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
Document document =
factory.newDocumentBuilder().parse(new InputSource(new StringReader(encoder.toString())));
Element root = document.getDocumentElement();
if (!root.getTagName().equals("prototype"))
throw new IllegalStateException("Unexpected default prototype encoding " + root.getTagName());
root.setAttribute("name", policy.prototype());
List<Element> floatEntries = new ArrayList<>();
NodeList entries = root.getElementsByTagName("pentry");
for (int index = 0; index < entries.getLength(); index += 1) {
Element entry = (Element) entries.item(index);
if (entry.getAttribute("storage").equals("float")) floatEntries.add(entry);
}
if (floatEntries.isEmpty())
throw new IllegalStateException("Default prototype has no floating-point entries");
if (policy.floatEntryBytes() == 0) {
List<Node> removed = new ArrayList<>(floatEntries);
NodeList rules = root.getElementsByTagName("rule");
for (int index = 0; index < rules.getLength(); index += 1) {
Element rule = (Element) rules.item(index);
if (usesFloatStorage(rule)) removed.add(rule);
}
if (removed.size() == floatEntries.size())
throw new IllegalStateException("Default prototype has no floating-point rules to remove");
for (Node node : removed) node.getParentNode().removeChild(node);
} else
for (Element entry : floatEntries)
entry.setAttribute("maxsize", Integer.toString(policy.floatEntryBytes()));
// Values over 2 x XLEN go by reference. Returns need no rule: a value that the return
// registers cannot hold already becomes a caller buffer passed in a0.
NodeList inputs = root.getElementsByTagName("input");
if (inputs.getLength() != 1)
throw new IllegalStateException("Default prototype has no single input list");
Element input = (Element) inputs.item(0);
Element last = null;
for (Node child = input.getFirstChild(); child != null; child = child.getNextSibling())
if (child instanceof Element element && element.getTagName().equals("rule")) last = element;
if (last == null || !joinsAnyValue(last))
throw new IllegalStateException("Default prototype's input list does not end with a join");
Element byReference = document.createElement("rule");
Element datatype = document.createElement("datatype");
datatype.setAttribute("name", "any");
datatype.setAttribute("minsize", "9");
byReference.appendChild(datatype);
byReference.appendChild(document.createElement("convert_to_ptr"));
input.insertBefore(byReference, last);
if (!policy.preservesFloatRegisters()) {
NodeList killedLists = root.getElementsByTagName("killedbycall");
Element killed = killedLists.getLength() == 0 ? null : (Element) killedLists.item(0);
if (killed == null) {
killed = document.createElement("killedbycall");
root.appendChild(killed);
}
List<Element> moved = new ArrayList<>();
NodeList preservedLists = root.getElementsByTagName("unaffected");
for (int list = 0; list < preservedLists.getLength(); list += 1) {
NodeList preserved = preservedLists.item(list).getChildNodes();
for (int index = 0; index < preserved.getLength(); index += 1) {
if (preserved.item(index) instanceof Element varnode
&& isFloatRegister(varnode, language))
moved.add(varnode);
}
}
for (Element varnode : moved) {
varnode.getParentNode().removeChild(varnode);
killed.appendChild(varnode);
}
}
StringWriter xml = new StringWriter();
var transformer = TransformerFactory.newInstance().newTransformer();
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
transformer.transform(new DOMSource(document), new StreamResult(xml));
return xml.toString();
}
/** The installed prototype places each class of value as its policy states. */
private void selfCheck(PrototypeModel model, Policy policy, Language language) {
StructureDataType pair = new StructureDataType("rea_float_pair", 0);
pair.add(FloatDataType.dataType);
pair.add(FloatDataType.dataType);
VariableStorage[] storage =
model.getStorageLocations(
currentProgram,
new DataType[] {
DoubleDataType.dataType, FloatDataType.dataType, DoubleDataType.dataType,
IntegerDataType.dataType, pair, Float16DataType.dataType
},
false);
VariableStorage quad = storage[storage.length - 1];
if (quad.size() != currentProgram.getDefaultPointerSize() || quad.getRegister() == null)
throw new IllegalStateException(
"REA " + policy.prototype() + " does not pass a 16-byte value by reference: " + quad);
if (policy.floatEntryBytes() == 0)
requireIntegerStorage(storage, policy.prototype() + " self-check", false);
if (policy.preservesFloatRegisters()) return;
for (Varnode preserved : model.getUnaffectedList()) {
Register register = language.getRegister(preserved.getAddress(), preserved.getSize());
if (register != null && FLOAT_REGISTER.matcher(register.getName()).matches())
throw new IllegalStateException(
"REA " + policy.prototype() + " still preserves " + register.getName());
}
}
private static boolean joinsAnyValue(Element rule) {
List<Element> children = new ArrayList<>();
for (Node child = rule.getFirstChild(); child != null; child = child.getNextSibling())
if (child instanceof Element element) children.add(element);
return children.size() == 2
&& children.get(0).getTagName().equals("datatype")
&& children.get(0).getAttribute("name").equals("any")
&& children.get(1).getTagName().equals("join");
}
/** Whether a rule's action, at any depth, assigns floating-point storage. */
private static boolean usesFloatStorage(Element element) {
NodeList children = element.getChildNodes();
for (int index = 0; index < children.getLength(); index += 1) {
if (!(children.item(index) instanceof Element child)) continue;
if (child.getAttribute("storage").equals("float") || usesFloatStorage(child)) return true;
}
return false;
}
private static boolean isFloatRegister(Element varnode, Language language) {
if (!varnode.getTagName().equals("varnode")
|| !varnode.getAttribute("space").equals("register"))
return false;
Register register =
language.getRegister(
language.getAddressFactory().getRegisterSpace()
.getAddress(Long.decode(varnode.getAttribute("offset"))),
Integer.parseInt(varnode.getAttribute("size")));
return register != null && FLOAT_REGISTER.matcher(register.getName()).matches();
}
private static void requireIntegerStorage(
VariableStorage[] storage, String owner, boolean allowUnassigned) {
for (VariableStorage item : storage) {
if (item.isBadStorage() || (!allowUnassigned && item.isUnassignedStorage()))
throw new IllegalStateException("REA RISC-V storage unresolved for " + owner);
List<Register> registers = item.getRegisters();
if (registers == null) continue; // stack-only storage
for (Register register : registers) {
if (FLOAT_REGISTER.matcher(register.getName()).matches())
throw new IllegalStateException(
"REA RISC-V storage still uses " + register.getName() + " for " + owner);
}
}
}
}
+305 -39
View File
@@ -8,12 +8,103 @@ 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.
# Encoded JSON allowance, in bytes, shared by one instruction list, one
# native-caller list, and one caller-scan page. This release has no runtime override.
CALLER_SCAN_ALLOWANCE_BYTES = 512 * 1024
_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 = {}
@@ -144,6 +235,9 @@ EXHAUSTIVE_ANALYSIS_METHODS = frozenset(
"procedure_assembly",
"procedure_callees",
"procedure_callers",
"rea_caller_scan_page",
"rea_caller_scan_target",
"rea_caller_base",
"procedure_info",
"procedure_pseudo_code",
"procedure_references",
@@ -236,20 +330,27 @@ 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):
def _bookmark_api(document, name):
method = getattr(document, name, None)
if not callable(method):
raise CapabilityUnavailableError(
"Hopper's public Python API does not expose getBookmarkName"
"Hopper's public Python API does not expose %s" % name
)
return getter(address)
return method
def _address(document, value=None):
"""Keep explicit hexadecimal coordinates distinct from symbol names."""
def _bookmark_name(document, address):
return _bookmark_api(document, "getBookmarkName")(address)
def _address_selection(document, value=None):
"""Own the coordinate and whether it was selected by symbol or address."""
if value is None:
return document.getCurrentAddress()
return document.getCurrentAddress(), "cursor"
_api_text(value, "Address")
if value.startswith("file-offset:"):
raise InvalidRequestError("Hopper does not support file-offset: selectors; select Ghidra for source-file coordinates or use a Hopper analysis address")
source = "address"
if value.lower().startswith("0x"):
try:
address = int(value, 16)
@@ -260,6 +361,8 @@ def _address(document, value=None):
# Prefer an observed symbol, retaining bare-hex compatibility only
# when Hopper has no symbol with the supplied name.
address = document.getAddressForName(value)
if address not in BAD_ADDRESSES:
source = "symbol"
if address in BAD_ADDRESSES:
try:
address = int(value, 16)
@@ -267,7 +370,11 @@ def _address(document, value=None):
raise InvalidRequestError("Unknown Hopper address or name: %s" % value) from error
if not 0 <= address < 0xFFFFFFFFFFFFFFFF:
raise InvalidRequestError("Address is outside Hopper's usable unsigned 64-bit range: %s" % value)
return address
return address, source
def _address(document, value=None):
return _address_selection(document, value)[0]
def _segment(document, address):
@@ -278,10 +385,15 @@ def _segment(document, address):
def _procedure(document, value=None):
address = document.getCurrentAddress() if value is None else _address(document, value)
address, source = _address_selection(document, value)
result = _segment(document, address).getProcedureAtAddress(address)
if result is None:
raise InvalidRequestError("No procedure exists at the requested address")
if source == "symbol" and address != result.getEntryPoint():
raise InvalidRequestError(
"Symbol %s at %s is inside procedure %s at %s; select its entry name/address or use resolve_containing_procedure for explicit containment"
% (value, _hex(address), _procedure_name(result), _hex(result.getEntryPoint()))
)
return result
@@ -294,6 +406,7 @@ def _procedure_identity(procedure):
return {
"address": _hex(procedure.getEntryPoint()),
"name": _procedure_name(procedure),
"symbol": None,
"classification": None,
"body": {
"available": False,
@@ -378,19 +491,30 @@ def _instruction_addresses(procedure):
return result
def _native_calls(procedure, direction):
def _iter_native_calls(procedure, direction):
references = procedure.getAllCallees() if direction == "outgoing" else procedure.getAllCallers()
kinds = {0: "none", 1: "unknown", 2: "direct", 3: "objective_c"}
calls = {}
unresolved = []
for reference in references:
source, target, kind = reference.fromAddress(), reference.toAddress(), reference.type()
if source in BAD_ADDRESSES:
yield None, None, None
continue
if target in BAD_ADDRESSES:
unresolved.append({"address": _hex(source), "reason": "Native CallReference has no resolved target (type %s)" % kind})
yield source, None, {"address": _hex(source), "reason": "Native CallReference has no resolved target (type %s)" % kind}
continue
calls[(source, target)] = {"classification": kinds.get(kind, "unknown"), "provider_type": kind, "provenance": "hopper-public-python-api:CallReference"}
yield source, target, {"classification": kinds.get(kind, "unknown"), "provider_type": kind, "provenance": "hopper-public-python-api:CallReference"}
def _native_calls(procedure, direction):
calls = {}
unresolved = []
for source, target, detail in _iter_native_calls(procedure, direction):
if detail is None:
continue
if target is None:
unresolved.append(detail)
else:
calls[(source, target)] = detail
return calls, sorted(unresolved, key=lambda item: int(item["address"], 16))
@@ -440,6 +564,136 @@ def _procedure_map(document):
return result
def _caller_target_data(procedure):
addresses = []
seen = set()
size = 0
for block in procedure.basicBlockIterator():
for address in _block_instruction_addresses(procedure, block):
if address in seen:
continue
encoded = _hex(address)
size += len(encoded) + 4
if size > CALLER_SCAN_ALLOWANCE_BYTES:
return {"target": _procedure_identity(procedure), "instructions": addresses, "instructions_complete": False}
addresses.append(encoded)
seen.add(address)
return {"target": _procedure_identity(procedure), "instructions": addresses, "instructions_complete": True}
def _caller_scan_target(document, params):
return _caller_target_data(_procedure(document, params.get("procedure")))
def _caller_base(document, params):
procedure = _procedure(document, params.get("procedure"))
callers = []
size = 0
complete = True
for item in procedure.getAllCallerProcedures():
address = _hex(item.getEntryPoint())
size += len(address) + 4
if size > CALLER_SCAN_ALLOWANCE_BYTES:
complete = False
break
callers.append(address)
return {
**_caller_target_data(procedure),
"callers": sorted(callers, key=lambda value: int(value, 16)),
"callers_complete": complete,
}
def _caller_scan_page(document, params):
"""Observe forward relationships in one bounded, stateless bridge request."""
start = params.get("start")
if isinstance(start, bool) or not isinstance(start, int) or start < 0:
raise InvalidRequestError("Caller scan start must be a non-negative integer")
segments = document.getSegmentsList()
counts = [segment.getProcedureCount() for segment in segments]
total = sum(counts)
end = min(start + 64, total)
if start > total:
raise InvalidRequestError("Caller scan start exceeds the procedure inventory")
items = []
item_bytes = 0
page_limit = CALLER_SCAN_ALLOWANCE_BYTES
oversized = False
identities = {}
containing = {}
def identity(procedure):
entry = procedure.getEntryPoint()
if entry not in identities:
identities[entry] = _procedure_identity(procedure)
return identities[entry]
def owner(address):
if address not in containing:
procedure, _ = _containing_procedure(document, address)
containing[address] = identity(procedure) if procedure is not None else None
return containing[address]
segment_start = 0
for segment, count in zip(segments, counts):
begin, stop = max(start - segment_start, 0), min(end - segment_start, count)
segment_start += count
for index in range(begin, max(begin, stop)):
candidate = segment.getProcedureAtIndex(index)
item = {"caller": identity(candidate), "callees": [], "references": []}
size = len(json.dumps(item, ensure_ascii=True).encode("utf-8")) + 1
remaining = page_limit - item_bytes
too_large = size > remaining
if not too_large:
for callee in candidate.getAllCalleeProcedures():
address = _hex(callee.getEntryPoint())
size += len(address) + 4
if size > remaining:
too_large = True
break
item["callees"].append(address)
if not too_large:
callee_entries = set(item["callees"])
for source, destination, call in _iter_native_calls(candidate, "outgoing"):
# Account for every native record, even when its target cannot
# be associated with an observed procedure.
size += 32
if size > remaining:
too_large = True
break
if destination is None:
continue
source_procedure = item["caller"] if _hex(source) == item["caller"]["address"] else owner(source)
target_address = _hex(destination)
if target_address in callee_entries:
target_entry = target_address
else:
target_procedure = owner(destination)
if target_procedure is None:
continue
target_entry = target_procedure["address"]
edge = {
"source_address": _hex(source), "target_address": _hex(destination),
"source_procedure": "caller" if source_procedure is not None and source_procedure["address"] == item["caller"]["address"] else source_procedure,
"target_procedure": target_entry,
"call": call,
}
size += len(json.dumps(edge, ensure_ascii=True).encode("utf-8")) + 1
if size > remaining:
too_large = True
break
item["references"].append(edge)
if too_large:
oversized = not items
break
items.append(item)
item_bytes += size
else:
continue
break
return {"start": start, "next": start + len(items), "total": total, "items": items, "oversized": oversized}
def _string_record(segment, address, display):
"""Read one native typed object, retaining its shortened display separately."""
length = segment.getObjectLength(address)
@@ -740,6 +994,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()
@@ -779,6 +1034,15 @@ def _dispatch(method, params):
if method in EXHAUSTIVE_ANALYSIS_METHODS:
_api().require_analysis_complete(document, method)
if method == "rea_caller_scan_target":
return _caller_scan_target(document, params)
if method == "rea_caller_base":
return _caller_base(document, params)
if method == "rea_caller_scan_page":
return _caller_scan_page(document, params)
if method == "analyze_function":
return _analyze_function(document, params)
if method == "read_function_instructions":
@@ -963,6 +1227,7 @@ def _dispatch(method, params):
length = sum(max(0, _basic_block_end(procedure, block) - block.getStartingAddress()) for block in blocks)
return {
"name": _procedure_name(procedure),
"symbol": None,
"entrypoint": _hex(procedure.getEntryPoint()),
"basicblock_count": procedure.getBasicBlockCount(),
"length": length,
@@ -1003,39 +1268,24 @@ 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": _bookmark_name(document, item)} for item in document.getBookmarks()]
return [{"address": _hex(item), "name": _bookmark_name(document, item)} for item in _bookmark_api(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")
has_bookmark = getattr(document, "hasBookmarkAtAddress", None)
if not callable(has_bookmark):
raise CapabilityUnavailableError(
"Hopper's public Python API does not expose hasBookmarkAtAddress"
)
has_bookmark = _bookmark_api(document, "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"
)
_bookmark_api(document, "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 = _bookmark_api(document, "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 = _bookmark_api(document, "setBookmarkAtAddress")
setter(address, name)
if not has_bookmark(address):
return False
@@ -1047,10 +1297,12 @@ def _dispatch(method, params):
address = _address(document, params.get("address"))
# Permit removing a legacy orphan bookmark, but do not report success
# for an unmapped address with no bookmark to remove.
if not document.hasBookmarkAtAddress(address):
has_bookmark = _bookmark_api(document, "hasBookmarkAtAddress")
remove_bookmark = _bookmark_api(document, "removeBookmarkAtAddress")
if not has_bookmark(address):
_segment(document, address)
document.removeBookmarkAtAddress(address)
return not document.hasBookmarkAtAddress(address)
remove_bookmark(address)
return not has_bookmark(address)
raise InvalidRequestError("Unknown bridge method")
@@ -1060,9 +1312,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
+26
View File
@@ -0,0 +1,26 @@
import Foundation
import CoreFoundation
struct AccessibilityValue {
let value: Any
let gap: String?
}
/** Preserve JSON scalars; report successful reads that JSON cannot represent faithfully. */
func captureAccessibilityValue(_ source: Any?) -> AccessibilityValue {
guard let source else { return AccessibilityValue(value: NSNull(), gap: nil) }
if let text = source as? String { return AccessibilityValue(value: text, gap: nil) }
if let number = source as? NSNumber {
if CFGetTypeID(number) == CFBooleanGetTypeID() {
return AccessibilityValue(value: number.boolValue, gap: nil)
}
let value = number.doubleValue
let encoding = String(cString: number.objCType)
let floating = encoding == "f" || encoding == "d"
if number is NSDecimalNumber || !value.isFinite || (value == 0 && value.sign == .minus) || (!floating && abs(value) > 9_007_199_254_740_991) {
return AccessibilityValue(value: NSNull(), gap: "AXValue number \(number.stringValue) cannot be represented faithfully as a JSON number")
}
return AccessibilityValue(value: number, gap: nil)
}
return AccessibilityValue(value: NSNull(), gap: "AXValue type \(type(of: source)) is not a supported JSON scalar")
}
+13 -5
View File
@@ -43,10 +43,13 @@ func windowBounds(_ element: AXUIElement) -> CGRect? {
return CGRect(origin: point, size: size)
}
func observe(_ request: Request) async throws -> [String: Any] {
guard let app = NSRunningApplication(processIdentifier: request.pid), let url = app.executableURL else {
try fail("target-mismatch", "Selected PID does not expose an application executable URL")
guard let app = NSRunningApplication(processIdentifier: request.pid) else {
try fail("pid-not-application", "LaunchServices does not expose an application for the selected PID; this does not establish that the process is absent")
}
guard let canonicalPointer = realpath(url.path, nil) else { try fail("target-mismatch", "Running executable path cannot be resolved") }
guard let url = app.executableURL else {
try fail("application-executable-unavailable", "The selected application does not expose an executable URL; select a PID with an identifiable application executable")
}
guard let canonicalPointer = realpath(url.path, nil) else { try fail("executable-path-unavailable", "Running executable path cannot be resolved") }
defer { free(canonicalPointer) }
let canonicalExecutable = String(cString: canonicalPointer)
guard canonicalExecutable == request.executable else {
@@ -62,7 +65,10 @@ func observe(_ request: Request) async throws -> [String: Any] {
guard SHA256.hash(data: executableBytes).map({ String(format: "%02x", $0) }).joined() == request.sha256 else {
try fail("target-mismatch", "Running executable bytes differ from the active target digest")
}
guard let windows = CGWindowListCopyWindowInfo([.optionIncludingWindow], request.window_id) as? [[String: Any]],
// .optionIncludingWindow omits a selected window while it is off screen, which
// is not absence: select from every window, and let capture report what it can.
guard let windows = (CGWindowListCopyWindowInfo([.optionAll], kCGNullWindowID) as? [[String: Any]])?
.filter({ ($0[kCGWindowNumber as String] as? NSNumber)?.uint32Value == request.window_id }),
windows.count == 1, let window = windows.first,
(window[kCGWindowOwnerPID as String] as? NSNumber)?.int32Value == request.pid,
(window[kCGWindowLayer as String] as? NSNumber)?.intValue == 0,
@@ -117,7 +123,9 @@ func observe(_ request: Request) async throws -> [String: Any] {
let childCountResult = childCount(element)
var actions: CFArray?
AXUIElementCopyActionNames(element, &actions)
nodes.append(["path": path, "role": text(element, kAXRoleAttribute), "title": text(element, kAXTitleAttribute), "value": text(element, kAXValueAttribute), "actions": actions as? [String] ?? [], "children_count": childCountResult.value.map { $0 as Any } ?? NSNull()])
let observedValue = captureAccessibilityValue(attribute(element, kAXValueAttribute))
if let gap = observedValue.gap { gaps.append("\(gap) at path \(path)"); truncated = true }
nodes.append(["path": path, "role": text(element, kAXRoleAttribute), "title": text(element, kAXTitleAttribute), "value": observedValue.value, "actions": actions as? [String] ?? [], "children_count": childCountResult.value.map { $0 as Any } ?? NSNull()])
guard let totalChildren = childCountResult.value else {
truncated = true
gaps.append("AX child count unavailable at path \(path): AXError \(childCountResult.error ?? -1)")
+16 -21
View File
@@ -26,7 +26,6 @@ ARGUMENT_REGISTERS = {
"x86_64h": ["rdi", "rsi", "rdx", "rcx", "r8", "r9"],
}
MAX_SELECTOR_BYTES = 1024
MAX_REPORTED_LOCATIONS = 64
MAX_OTHER_STOPS = 256
MAX_TRACE_BYTES = 8 * 1024 * 1024
MAX_TRACE_FRAMES = 65_536
@@ -505,20 +504,19 @@ def _breakpoint_report(target, created, config):
if not _wanted(config["breakpoints"][index], location["symbol"] or ""):
continue
count += 1
if len(locations) < MAX_REPORTED_LOCATIONS:
remaining_bytes = (
MAX_BREAKPOINT_LOCATION_BYTES - retained_location_bytes
)
if not _fits_trace_budget(location, remaining_bytes):
locations_truncated = True
continue
location_bytes = len(json.dumps(location).encode("utf-8"))
addition = location_bytes + (1 if locations else 0)
if addition > remaining_bytes:
locations_truncated = True
continue
locations.append(location)
retained_location_bytes += addition
remaining_bytes = (
MAX_BREAKPOINT_LOCATION_BYTES - retained_location_bytes
)
if not _fits_trace_budget(location, remaining_bytes):
locations_truncated = True
continue
location_bytes = len(json.dumps(location).encode("utf-8"))
addition = location_bytes + (1 if locations else 0)
if addition > remaining_bytes:
locations_truncated = True
continue
locations.append(location)
retained_location_bytes += addition
report.append({"index": index, "location_count": count, "locations": locations})
return report, locations_truncated
@@ -661,18 +659,15 @@ def trace(debugger, config):
open(observation_path, "wb")
)
process = target.Launch(_launch_info(target, config, listener), error)
if not error.Success() or not process.IsValid():
process_created = process.IsValid()
if not error.Success() or not process_created:
exit_observed = _retire_failed_launch(
process, config, listener, stdout, stderr
)
return {
"status": "launch-error",
"error": error.GetCString(),
**(
{"target_process_created": True}
if process.IsValid()
else {}
),
"target_process_created": process_created,
**(
{"target_process_exit_observed": True}
if exit_observed
+95
View File
@@ -25,6 +25,73 @@ spec.loader.exec_module(tracer)
class NativeTraceBudgetTests(unittest.TestCase):
def test_invalid_launch_process_reports_explicit_no_creation(self):
class InvalidProcess:
def IsValid(self):
return False
def Kill(self):
raise AssertionError("an invalid process cannot own target cleanup")
class LaunchError:
def Success(self):
return False
def GetCString(self):
return "No such file or directory: fixture/absent"
class Target:
def IsValid(self):
return True
def Launch(self, _launch_info, _error):
return InvalidProcess()
class Debugger:
def SetAsync(self, _enabled):
pass
def GetInstanceName(self):
return "fixture"
def CreateTarget(self, *_arguments):
return Target()
class Pipe:
def __init__(self, *_arguments):
self.closed = False
pipes.append(self)
def start(self):
pass
def close(self, expected_exit=False):
self.closed = True
pipes = []
debugger_api = types.SimpleNamespace(SetInternalVariable=lambda *_arguments: None)
with patch.object(tracer.lldb, "SBDebugger", debugger_api, create=True), patch.object(
tracer.lldb, "SBError", LaunchError, create=True
), patch.object(tracer.lldb, "SBListener", lambda _name: object(), create=True), patch.object(
tracer, "_hash_file", return_value="expected"
), patch.object(tracer, "_file_identity", return_value=(1, 2)), patch.object(
tracer, "_module_identity", return_value=("fixture", (1, 2), "fixture-uuid")
), patch.object(tracer, "_launch_info", return_value=object()), patch.object(
tracer, "_OutputPipe", Pipe
):
result = tracer.trace(Debugger(), {
"executable": "fixture", "architecture": "arm64", "expected_sha256": "expected",
"breakpoints": [], "working_directory": "fixture/absent",
"stdout_path": "stdout", "stdout_capture_path": "stdout-capture",
"stderr_path": "stderr", "stderr_capture_path": "stderr-capture",
})
self.assertEqual(result["status"], "launch-error")
self.assertEqual(result["error"], "No such file or directory: fixture/absent")
self.assertIs(result["target_process_created"], False)
self.assertNotIn("target_process_exit_observed", result)
self.assertEqual(len(pipes), 2)
self.assertTrue(all(pipe.closed for pipe in pipes))
def test_failed_launch_does_not_infer_exit_from_a_failed_kill(self):
class Process:
def IsValid(self):
@@ -363,6 +430,34 @@ class NativeTraceBudgetTests(unittest.TestCase):
self.assertEqual(report[0]["locations"], [])
self.assertFalse(locations_truncated)
def test_breakpoint_report_keeps_all_locations_under_aggregate_budget(self):
class Location:
def __init__(self, index):
self.index = index
def GetAddress(self):
return self.index
class Breakpoint:
def GetNumLocations(self):
return 65
def GetLocationAtIndex(self, index):
return Location(index)
with patch.object(
tracer,
"_location",
side_effect=lambda address, _target: {"address": address, "symbol": None},
), patch.object(tracer, "_wanted", return_value=True):
report, locations_truncated = tracer._breakpoint_report(
object(), [[Breakpoint()]], {"breakpoints": [{}]}
)
self.assertEqual(report[0]["location_count"], 65)
self.assertEqual(len(report[0]["locations"]), 65)
self.assertFalse(locations_truncated)
def test_breakpoint_report_bounds_aggregate_location_json(self):
tracer.lldb.LLDB_INVALID_ADDRESS = -1
symbol_name = (
+72 -34
View File
@@ -15,6 +15,8 @@ struct ProcessIdentityObservation: Encodable {
let effective_uid: UInt32?
let lifecycle: String?
let relation: ProcessRelationObservation?
/** Display-only kernel accounting name (p_comm, at most MAXCOMLEN bytes). */
let name: String?
let reason: String?
}
@@ -31,6 +33,7 @@ struct ProcessMetadata {
let parentPid: Int32
let processGroupId: Int32
let pointerSize: Int
let name: String
}
struct ProcessSnapshot {
@@ -92,7 +95,8 @@ func readProcessSnapshot(
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
pointerSize: process.kp_proc.p_flag & P_LP64 != 0 ? 8 : 4,
name: processName(process)
)
)
},
@@ -103,6 +107,13 @@ func readProcessSnapshot(
return ProcessSnapshot(metadata: [:], failure: "process_table_failed_\(ENOMEM)")
}
/** Decode the NUL-terminated accounting name; invalid UTF-8 is replaced for display. */
private func processName(_ process: kinfo_proc) -> String {
withUnsafeBytes(of: process.kp_proc.p_comm) { bytes in
String(decoding: bytes.prefix { $0 != 0 }, as: UTF8.self)
}
}
func readProcessIdentities(pids: [Int32]) -> [ProcessIdentityObservation] {
readProcessIdentities(pids: pids, snapshot: readProcessSnapshot())
}
@@ -130,6 +141,7 @@ func readProcessIdentities(
effective_uid: nil,
lifecycle: nil,
relation: nil,
name: nil,
reason: "not-in-snapshot"
)
}
@@ -144,6 +156,7 @@ func readProcessIdentities(
parent_pid: metadata.parentPid,
process_group_id: metadata.processGroupId
),
name: metadata.name,
reason: nil
)
}
@@ -157,12 +170,25 @@ private func unavailableIdentity(pid: Int32, reason: String) -> ProcessIdentityO
effective_uid: nil,
lifecycle: nil,
relation: nil,
name: nil,
reason: reason
)
}
enum ProcargsMalformedStage: String, CaseIterable {
case header
case argumentCount = "argument_count"
case executablePath = "executable_path"
case pointerSize = "pointer_size"
case pathPadding = "path_padding"
case arguments
case terminator
case trailingEntries = "trailing_entries"
}
enum RunTokenReadError: Error {
case invalidBuffer
case invalidBufferAt(ProcargsMalformedStage)
case duplicateToken
case invalidTokenEncoding
case environmentUnavailable
@@ -202,6 +228,22 @@ func processRunTokenObservation(
parse: Result<ParsedRunToken, RunTokenReadError>,
codeSigningFlags: () -> UInt32?
) -> RunTokenObservation {
// Native inspection restrictions apply even when argv is incomplete or
// overwritten, before parsing reaches the environment. Keep this fact at
// the native boundary; cleanup still decides whether the process is related.
func withheldEnvironment() -> RunTokenObservation? {
guard let flags = codeSigningFlags() else { return nil }
if (flags & csPlatformBinary) != 0 {
return unavailable(pid: pid, reason: "platform_binary_environment_withheld")
}
if (flags & csRestrict) != 0 {
return unavailable(pid: pid, reason: "restricted_process_environment_withheld")
}
return nil
}
func unavailableEnvironment(_ reason: String) -> RunTokenObservation {
withheldEnvironment() ?? unavailable(pid: pid, reason: reason)
}
switch parse {
case .success(let parsed):
if let runId = parsed.runId {
@@ -212,38 +254,25 @@ func processRunTokenObservation(
if parsed.environmentEntryCount > 0 {
return RunTokenObservation(pid: pid, state: "readable", run_id: nil, reason: nil)
}
let flags = codeSigningFlags()
if let flags, (flags & csPlatformBinary) != 0 {
return unavailable(pid: pid, reason: "platform_binary_environment_withheld")
}
if let flags, (flags & csRestrict) != 0 {
return unavailable(pid: pid, reason: "restricted_process_environment_withheld")
}
if let withheld = withheldEnvironment() { return withheld }
return RunTokenObservation(pid: pid, state: "readable", run_id: nil, reason: nil)
case .failure(let failure):
switch failure {
case .environmentUnavailable:
let flags = codeSigningFlags()
if let flags, (flags & csPlatformBinary) != 0 {
return unavailable(pid: pid, reason: "platform_binary_environment_withheld")
}
if let flags, (flags & csRestrict) != 0 {
return unavailable(pid: pid, reason: "restricted_process_environment_withheld")
}
return unavailable(pid: pid, reason: "environment_unavailable")
return unavailableEnvironment("environment_unavailable")
case .ambiguousEnvironmentBoundary:
if let flags = codeSigningFlags(), (flags & csRestrict) != 0 {
return unavailable(pid: pid, reason: "restricted_process_environment_withheld")
}
return unavailable(pid: pid, reason: "ambiguous_environment_boundary")
return unavailableEnvironment("ambiguous_environment_boundary")
// These failures already establish that token bytes were available.
case .duplicateToken:
return unavailable(pid: pid, reason: "duplicate_run_token")
case .invalidTokenEncoding:
return unavailable(pid: pid, reason: "invalid_run_token_encoding")
case .appleVectorUnavailable:
return unavailable(pid: pid, reason: "apple_vector_unavailable")
return unavailableEnvironment("apple_vector_unavailable")
case .invalidBuffer:
return unavailable(pid: pid, reason: "malformed_procargs")
return unavailableEnvironment("malformed_procargs")
case .invalidBufferAt(let stage):
return unavailableEnvironment("malformed_procargs_\(stage.rawValue)")
}
}
}
@@ -302,27 +331,30 @@ func parseProcessEnvironment(
in bytes: [UInt8],
pointerSize: Int = MemoryLayout<UnsafeRawPointer>.size
) throws -> ParsedRunToken {
guard bytes.count >= MemoryLayout<Int32>.size else { throw RunTokenReadError.invalidBuffer }
guard bytes.count >= MemoryLayout<Int32>.size else { throw RunTokenReadError.invalidBufferAt(.header) }
let argc = bytes.withUnsafeBytes { $0.loadUnaligned(as: Int32.self).littleEndian }
guard argc >= 0, Int(argc) <= bytes.count else { throw RunTokenReadError.invalidBuffer }
guard argc >= 0, Int(argc) <= bytes.count else { throw RunTokenReadError.invalidBufferAt(.argumentCount) }
var offset = MemoryLayout<Int32>.size
guard let executablePath = readString(in: bytes, offset: &offset) else {
throw RunTokenReadError.invalidBuffer
throw RunTokenReadError.invalidBufferAt(.executablePath)
}
guard pointerSize == 4 || pointerSize == 8 else { throw RunTokenReadError.invalidBuffer }
guard pointerSize == 4 || pointerSize == 8 else { throw RunTokenReadError.invalidBufferAt(.pointerSize) }
let pathRecordSize = Array("executable_path=".utf8).count + executablePath.count + 1
let padding = (pointerSize - pathRecordSize % pointerSize) % pointerSize
guard offset + padding <= bytes.count,
bytes[offset..<(offset + padding)].allSatisfy({ $0 == 0 }) else {
throw RunTokenReadError.invalidBuffer
throw RunTokenReadError.invalidBufferAt(.pathPadding)
}
offset += padding
let tokenPrefix = Array("REA_PROCESS_RUN_ID=".utf8)
var tokenInArgument = false
for _ in 0..<argc {
guard readString(in: bytes, offset: &offset, allowEmpty: true) != nil else {
throw RunTokenReadError.invalidBuffer
guard let argument = readString(in: bytes, offset: &offset, allowEmpty: true) else {
throw RunTokenReadError.invalidBufferAt(.arguments)
}
tokenInArgument = tokenInArgument || argument.starts(with: tokenPrefix)
}
guard bytes.last == 0 else { throw RunTokenReadError.invalidBuffer }
guard bytes.last == 0 else { throw RunTokenReadError.invalidBufferAt(.terminator) }
if offset == bytes.count {
throw RunTokenReadError.environmentUnavailable
}
@@ -340,7 +372,7 @@ func parseProcessEnvironment(
continue
}
guard let entry = readString(in: bytes, offset: &offset) else {
throw RunTokenReadError.invalidBuffer
throw RunTokenReadError.invalidBufferAt(.trailingEntries)
}
trailingEntries.append(entry)
}
@@ -361,22 +393,28 @@ func parseProcessEnvironment(
throw RunTokenReadError.appleVectorUnavailable
}
if trailingEntries[appleVectorStart...].contains(where: {
$0.starts(with: Array("REA_PROCESS_RUN_ID=".utf8))
$0.starts(with: tokenPrefix)
}) {
throw RunTokenReadError.ambiguousEnvironmentBoundary
}
let environmentEntries = trailingEntries[..<appleVectorStart]
var found: String?
for entry in environmentEntries {
if entry.starts(with: Array("REA_PROCESS_RUN_ID=".utf8)) {
if entry.starts(with: tokenPrefix) {
guard found == nil else { throw RunTokenReadError.duplicateToken }
let value = entry.dropFirst("REA_PROCESS_RUN_ID=".utf8.count)
let value = entry.dropFirst(tokenPrefix.count)
guard let decoded = String(bytes: value, encoding: .utf8) else {
throw RunTokenReadError.invalidTokenEncoding
}
found = decoded
}
}
// A changed process title can overwrite the original argument boundaries,
// causing a real environment entry to be consumed as an argument. A real
// argument can have this form too, so it proves neither ownership nor absence.
if tokenInArgument {
throw RunTokenReadError.ambiguousEnvironmentBoundary
}
return ParsedRunToken(runId: found, environmentEntryCount: environmentEntries.count)
}
+38 -12
View File
@@ -15,6 +15,11 @@ import sys
PROFILE = "[email protected];[email protected];[email protected]"
PACKAGES = {"pwntools": "4.15.0", "pyelftools": "0.33", "unicorn": "2.1.2"}
OUTPUT_BYTES = 64 * 1024 * 1024
# (e_machine, ELF class bits, byte order) -> architecture pwntools must also report.
PROFILES = {(62, 64, "little"): "amd64", (183, 64, "little"): "aarch64", (3, 32, "little"): "i386",
(40, 32, "little"): "arm", (8, 32, "little"): "mips", (8, 32, "big"): "mips"}
PROFILE_SCOPE = ("Layout profile requires ELF64 x86-64, ELF64 AArch64 little-endian, ELF32 i386, "
"ELF32 ARM little-endian or ELF32 MIPS little- or big-endian objects.")
class LayoutFailure(Exception):
@@ -116,13 +121,16 @@ def inspect_elf(path, cache):
raise LayoutFailure("format", "Selected object does not contain an ELF header.")
if len(content) < 16:
raise LayoutFailure("format", "Selected ELF identification is truncated.")
if content[4] != 2 or content[5] != 1:
raise LayoutFailure("unsupported-target", "Initial layout profile requires ELF64 little-endian objects.")
if len(content) < 64:
raise LayoutFailure("format", "Selected ELF64 header is truncated.")
if int.from_bytes(content[18:20], "little") != 62:
raise LayoutFailure("unsupported-target", "Initial layout profile requires x86-64 ELF objects.")
if int.from_bytes(content[16:18], "little") not in (1, 2, 3):
if content[4] not in (1, 2) or content[5] not in (1, 2):
raise LayoutFailure("unsupported-target", "Selected ELF declares an unknown class or byte order.")
bits, header_size = (32, 52) if content[4] == 1 else (64, 64)
order = "little" if content[5] == 1 else "big"
if len(content) < header_size:
raise LayoutFailure("format", f"Selected ELF{bits} header is truncated.")
profile = (int.from_bytes(content[18:20], order), bits, order)
if profile not in PROFILES:
raise LayoutFailure("unsupported-target", PROFILE_SCOPE)
if int.from_bytes(content[16:18], order) not in (1, 2, 3):
raise LayoutFailure("unsupported-target", "Selected ELF type is outside the EXEC/DYN/REL layout profile; recorded core analysis is separate.")
from pwnlib.context import context
with context.local(cache_dir=str(cache), log_level="warning"):
@@ -132,6 +140,11 @@ def inspect_elf(path, cache):
try:
headers = ELFFile(io.BytesIO(content))
section_count = headers.num_sections()
if section_count:
location(headers.header.e_shoff, section_count * headers.header.e_shentsize, len(content))
segment_count = headers.num_segments()
if segment_count:
location(headers.header.e_phoff, segment_count * headers.header.e_phentsize, len(content))
if headers.header.e_shstrndx == 0xffff and section_count == 0:
raise LayoutFailure("format", "ELF extended section-name index requires a declared section-zero header.")
raw_name_index = headers.header.e_shstrndx
@@ -147,9 +160,21 @@ def inspect_elf(path, cache):
if names_header.sh_type != "SHT_STRTAB":
raise LayoutFailure("format", f"ELF section-name table {section_name_index} is not SHT_STRTAB.")
location(names_header.sh_offset, names_header.sh_size, len(content))
except ELFError as error:
except (ELFError, ValueError, OverflowError, AssertionError, IndexError) as error:
raise LayoutFailure("format", str(error)) from error
class LayoutELF(ELF):
def search(self, needle, writable=False, executable=False):
# This adapter exposes structural facts, GOT/PLT and mitigation
# indicators, not kernel config/version discovery. Upstream's
# constructor searches virtual memory for those unrelated
# heuristics, materializing NOBITS tails and interpreting
# ordinary IKCFG_ST data as a compressed kernel config.
return iter(())
def _populate_kernel_version(self):
# Kernel banner decoding is outside this report too.
return None
def _get_section_name(self, header):
# SHN_UNDEF declares no names; upstream would otherwise read
# strings at the inactive section-zero offset.
@@ -168,8 +193,8 @@ def inspect_elf(path, cache):
if type is None or section.header.sh_type == type)
try:
image = LayoutELF(str(path), checksec=False)
if image.arch != "amd64" or image.bits != 64 or image.endian != "little":
raise LayoutFailure("unsupported-target", "Initial layout profile requires x86-64 ELF64 little-endian objects.")
if (image.arch, image.bits, image.endian) != (PROFILES[profile], bits, order):
raise LayoutFailure("unsupported-target", PROFILE_SCOPE)
if image.header.e_type not in ("ET_EXEC", "ET_DYN", "ET_REL"):
raise LayoutFailure("unsupported-target", "Selected ELF type is outside the EXEC/DYN/REL layout profile; recorded core analysis is separate.")
length = len(content)
@@ -191,7 +216,7 @@ def inspect_elf(path, cache):
})
if h.sh_type in ("SHT_SYMTAB", "SHT_DYNSYM"):
if h.sh_entsize < image.structs.Elf_Sym.sizeof():
raise LayoutFailure("format", "ELF symbol entry is smaller than the decoded Elf64_Sym structure.")
raise LayoutFailure("format", f"ELF symbol entry is smaller than the decoded Elf{bits}_Sym structure.")
if h.sh_link >= len(all_sections) or all_sections[h.sh_link].header.sh_type != "SHT_STRTAB":
raise LayoutFailure("format", f"ELF symbol table {index} links to invalid string table {h.sh_link}.")
strings = all_sections[h.sh_link]
@@ -294,6 +319,7 @@ def inspect_elf(path, cache):
"Relocatable relocation targets with SHN_UNDEF or inactive SHT_NULL headers retain their reported section index/offset as unknown; they do not establish an associated target section.",
"GOT/PLT are derived upstream convenience maps; PLT inference can emulate selected instructions in Unicorn. They do not establish target runtime behavior; aliases may collapse and warnings may indicate incomplete coverage. Their completeness is unknown.",
"Mitigations are upstream static heuristics, not runtime protection. ET_DYN does not prove an executable; absent canary symbols do not prove every function unprotected.",
"Kernel configuration/version discovery is not performed; these memory-content scans do not supply the reported structural, linkage or mitigation facts.",
"This profile inspects ELF EXEC/DYN/REL layout only. It does not analyze recorded cores, resolve loaded libraries, launch the target as a host process or start a debugger.",
"Symbol/relocation inventories reflect original section tables. A sectionless image can still report dynamic dependency names; missing tables do not prove absence of dynamic symbols or relocations.",
*(["Absent section-name table: section-dependent RELRO/canary heuristics may be incomplete; protection coverage is unknown."] if all_sections and section_name_index == 0 else []),
@@ -302,7 +328,7 @@ def inspect_elf(path, cache):
}
except LayoutFailure:
raise
except (ELFError, ValueError, AssertionError, IndexError) as error:
except (ELFError, ValueError, OverflowError, AssertionError, IndexError) as error:
raise LayoutFailure("format", "Selected ELF failed unchanged upstream structural parsing: " + str(error)) from error
+14 -8
View File
@@ -88,16 +88,18 @@ def inspect_core(path, cache):
segments, notes, padding, threads, signals = [], [], [], [], []
for segment_index, segment in enumerate(image.iter_segments()):
h = segment.header
backing = h.p_type != "PT_NULL" and h.p_filesz > 0
header_offset = header.e_phoff + segment_index * header.e_phentsize
type_id = int.from_bytes(content[header_offset:header_offset + 4], "little")
backing = type_id != 0 and h.p_filesz > 0
if backing:
location(h.p_offset, h.p_filesz, len(content))
if h.p_type == "PT_LOAD" and h.p_filesz > h.p_memsz:
if type_id == 1 and h.p_filesz > h.p_memsz:
raise CoreFailure("format", "Recorded PT_LOAD file bytes exceed memory size.")
segments.append({"index": segment_index, "type": h.p_type,
"header_location": location(header.e_phoff + segment_index * header.e_phentsize, header.e_phentsize, len(content)),
segments.append({"index": segment_index, "type": h.p_type, "type_id": type_id,
"header_location": location(header_offset, header.e_phentsize, len(content)),
"offset": hex(h.p_offset), "file_size": hex(h.p_filesz), "memory_size": hex(h.p_memsz),
"virtual_address": hex(h.p_vaddr), "physical_address": hex(h.p_paddr), "alignment": hex(h.p_align), "flags": hex(h.p_flags), "file_backing": "file" if backing else "none"})
if h.p_type != "PT_NOTE" or h.p_filesz == 0:
if type_id != 4 or h.p_filesz == 0:
continue
position, end = h.p_offset, h.p_offset + h.p_filesz
header_size = image.structs.Elf_Nhdr.sizeof()
@@ -109,6 +111,7 @@ def inspect_core(path, cache):
padding.append({"segment_index": segment_index, "location": location(position, len(raw), end), "bytes_base64": encoded(raw)})
break
note = struct_parse(image.structs.Elf_Nhdr, image.stream, stream_pos=position)
note_type_id = int.from_bytes(content[position + 8:position + 12], "little")
owner_start = position + header_size
descriptor_start = owner_start + ((note.n_namesz + 3) // 4) * 4
note_end = descriptor_start + ((note.n_descsz + 3) // 4) * 4
@@ -119,7 +122,10 @@ def inspect_core(path, cache):
display = owner.split(b"\0", 1)[0].decode("utf-8", "replace") if owner else None
descriptor = content[descriptor_start:descriptor_start + note.n_descsz]
note_index = len(notes)
notes.append({"index": note_index, "segment_index": segment_index, "type": note.n_type,
# pyelftools selects ET_CORE labels without owner context. Keep
# those labels only for exact CORE owners; other kinds are opaque.
note_type = note.n_type if owner == b"CORE\0" else note_type_id
notes.append({"index": note_index, "segment_index": segment_index, "type": note_type, "type_id": note_type_id,
"location": location(position, note_end - position, end),
"owner_location": location(owner_start, len(owner), end), "owner_bytes_base64": encoded(owner), "owner_display": display,
"descriptor_location": location(descriptor_start, len(descriptor), end), "descriptor_bytes_base64": encoded(descriptor)})
@@ -127,7 +133,7 @@ def inspect_core(path, cache):
# Only the initial Linux CORE ABI is interpreted. Other owners retain exact bytes.
if owner != b"CORE\0":
continue
if note.n_type == "NT_PRSTATUS":
if note_type_id == 1:
if len(descriptor) < ctypes.sizeof(prstatus_type):
raise CoreFailure("format", "Linux " + profile_label + " NT_PRSTATUS descriptor is truncated.")
status = prstatus_type.from_buffer_copy(descriptor)
@@ -136,7 +142,7 @@ def inspect_core(path, cache):
relative = prstatus_type.pr_reg.offset + getattr(registers_type, name).offset
registers.append({"name": name, "value": hex(getattr(status.pr_reg, name)), "location": location(descriptor_start + relative, ctypes.sizeof(kind), end)})
threads.append({"note_index": note_index, "historical_pid": ctypes.c_int32(status.pr_pid).value, "recorded_current_signal": status.pr_cursig, "registers": registers})
elif note.n_type == "NT_SIGINFO":
elif note_type_id == 0x53494749:
if len(descriptor) < ctypes.sizeof(elf_siginfo_64):
raise CoreFailure("format", "Linux " + profile_label + " NT_SIGINFO descriptor is truncated.")
signal = elf_siginfo_64.from_buffer_copy(descriptor)
+48 -1
View File
@@ -32,6 +32,15 @@ export default defineConfig({
{ 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: "PE imports", link: "/pe-imports" },
{ text: "PE TLS/load config", link: "/pe-load-config" },
{ text: "ELF notes", link: "/elf-notes" },
{ text: "Binary hardening", link: "/binary-hardening" },
{ text: "Apple IPS crash reports", link: "/apple-ips" },
{ text: "Linux kernel modules", link: "/kernel-modules" },
{ text: "CPython pyc files", link: "/python-pyc" },
{ text: "Erlang BEAM", link: "/erlang-beam" },
{
text: "JavaScript and Electron",
link: "/javascript-artifact-reconstruction",
@@ -45,15 +54,48 @@ export default defineConfig({
text: "Web network captures",
link: "/web-network-captures",
},
{ text: "Agent environments", link: "/agent-environments" },
{
text: "Build RL environments",
link: "/agent-environments-quickstart",
},
{ text: "Environment reference", link: "/agent-environments" },
{ text: "Electron observation", link: "/electron-observation" },
{ text: "Electron fuses", link: "/electron-fuses" },
{ text: "Process capture", link: "/process-capture" },
{
text: "Desktop application capture",
link: "/desktop-application-capture",
},
{
text: "Desktop application state",
link: "/desktop-application-state",
},
{
text: "Desktop application export",
link: "/desktop-application-export",
},
{
text: "Desktop application tasks",
link: "/desktop-application-tasks",
},
{
text: "Desktop build computer",
link: "/desktop-build-computer",
},
{
text: "Building and witnessing",
link: "/desktop-build-witness",
},
{ 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: "iNES containers", link: "/ines-containers" },
{ text: "SQLite databases", link: "/sqlite-databases" },
{ text: "Packet captures", link: "/packet-captures" },
{ text: "Signal recordings", link: "/signal-recordings" },
@@ -70,6 +112,7 @@ export default defineConfig({
{ text: "Ghidra raw binary", link: "/raw-binary" },
{ text: "Ghidra NativeAOT", link: "/ghidra-nativeaot" },
{ text: "Ghidra PowerPC", link: "/ghidra-ppc" },
{ text: "Ghidra GameCube DOL", link: "/ghidra-dol" },
{ text: "Ghidra semantics", link: "/ghidra-provider" },
],
},
@@ -79,6 +122,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" },
{
+85 -16
View File
@@ -25,7 +25,21 @@ REA never installs the Android SDK, an emulator, or a system image.
Readiness is `rea inspect-adb-client`: it runs `adb version` and reports the
resolved binary, its version, and where it was selected from. A missing
binary reports `capability_unavailable` with the `REA_ADB_PATH` remediation
rather than attempting an installation.
rather than attempting an installation. A path that cannot resolve to a file
(`ENOENT`, `ENOTDIR`, `ELOOP` or `ENAMETOOLONG`) is reported as missing. A
selected path the host refuses to execute, such as a directory or a file
without execute permission (`EACCES`) or one the host does not permit
(`EPERM`), and any other spawn error report `capability_unavailable` naming
that path. Exhausted host capacity (`EAGAIN`, `EMFILE`, `ENFILE` or `ENOMEM`)
is not a configuration failure. `inspect_adb_client`, and any device
operation whose adb cannot start at its version probe or at a later adb
command, report a retryable `capacity` resource constraint rather than a
capability refusal or an adb protocol failure; the `binary_session` inventory
reports the provider as `runtime_unavailable`. Both say the host could not
start adb and ask for a retry after freeing processes or descriptors, without
blaming `REA_ADB_PATH`. When adb never started, the refusal diagnostics record
the spawn error as `filesystem_error` with `path_source`, never as an
`exit_code`.
The `binary_session` inventory uses the same bounded client-version probe; it
does not query devices, start the adb server, or establish that a requested
serial is reachable. Device state is checked by each device operation.
@@ -90,7 +104,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 |
@@ -103,6 +117,15 @@ existing analysis families directly, for example
| `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 |
Screen capture validates the PNG signature, IHDR framing, dimensions, and IHDR
checksum before publishing. It does not validate encoding fields or decode and
validate image contents; that limitation is retained in Evidence.
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
@@ -131,13 +154,38 @@ and during shutdown.
## Interpret the result
- **Package numeric declarations retain failures.** `inspect_adb_package`
admits the first valid `userId` or `appId` as `user_id`: a nonnegative safe
integer, including zero. A null value means no valid declaration was found.
Malformed declarations before or after a valid value remain in
`numeric_issues`: the first ten issues with command, field, reason and original
`raw_line`, plus the exact `total`. Reasons are `invalid_format` or
`unsafe_integer`. Any issue makes package coverage partial; existing section
omissions can also make it partial. `version_code` remains exact source text.
- **Display nulls preserve uncertainty.** `inspect_adb_display` admits positive
safe integers. Absent or malformed optional values are null; `numeric_issues`
distinguishes malformed declarations from absence. It keeps the first ten
issues and exact total, with command, field, original line and reason
(`invalid_format`, `unsafe_integer` or `nonpositive`). The last recognized
labeled declaration wins, even when malformed. A physical-density label
suppresses bare-number fallback even when malformed; without that label, the
first unsigned bare number wins, including zero or an unsafe integer that
leaves the value null. Display coverage is complete only with known physical
density and no numeric issues. Missing or malformed required physical size
produces a protocol failure with `details.partial_observation`: an
`inspect_adb_display` observation from provider `adb`, reason
`physical_size_absent` or `physical_size_malformed`, and a `result` retaining
client, serial, optional display facts, numeric issues, null physical size
and partial coverage.
- **Directory names retain whitespace and newlines.** Symlink targets do too,
including trailing newlines. Records containing invalid UTF-8 remain as
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
@@ -162,16 +210,25 @@ and during shutdown.
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. 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
transfer even with `overwrite`. A completed `test -e` exit of 1 establishes
absence only with empty stdout and either empty stderr or a recognized,
completed adb-server startup notice sequence. Startup notices are host
observations, not remote-path diagnostics; successful push receipts retain
them in `raw_result.device_path_probes`. Incomplete startup, extra diagnostics,
truncation and cancellation do not establish 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; installs preserve the
`sha256sum` verifies the transfer. A missing command, output without a
recognizable digest, or host capacity that prevents the digest check from
starting reports `device_digest_source: "unavailable"` with an
unverified-transfer limitation rather than a retryable error; 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`).
@@ -183,9 +240,11 @@ and during shutdown.
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. Output capture overflow reports `resource_constraint`;
deadline expiration, including the client-version probe, reports
`provider_timeout`. Command cancellation preserves the caller's exact reason;
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
@@ -218,15 +277,25 @@ beyond already-connected devices (`adb connect`) are not exposed yet.
## Real verification
`npm run verify:adb` exercises every operation against a live device. It
`npm run verify:adb` exercises observation and transfer operations against a live device. It
requires `adb` (or `REA_ADB_PATH`) and any attached emulator or phone;
`--serial` selects one when several are attached, `--pull PACKAGE` pulls a
real package and verifies the returned digests against the files on disk,
and `--install-apk PATH` drives a full install → resolve → uninstall
lifecycle with a lane-owned probe.
real package and verifies the returned digests against the files on disk.
The optional `--install-apk` lifecycle is unavailable until the selected APK
identity and device cleanup ownership can be established safely.
Record: verified against adb 34.0.5 on Linux against an Android 14
(API 34) x86_64 emulator — all operations above, a 224-package inventory,
287 services, 92 features, a real two-APK split set pulled with byte-exact
digests, a verified push/pull roundtrip, a 1.3 MB screen capture, and the
complete package lifecycle; see `docs/testing.md` for the lane summary.
Intent integer extras use signed decimal text. The ADB argument encoder removes
redundant leading zeros for Android's radix-sensitive `--ei` parser while retained
Evidence keeps the caller's original spelling. Package inspection accepts both
`userId` and Android 16's `appId` spelling for the legacy `user_id` result field.
A completed push and device digest verification are separate observations. A
verification timeout, cancellation, or output budget failure returns the typed
failure with partial Evidence of the completed transfer. An unavailable checksum
command or unrecognized checksum output leaves verification explicitly unknown.
+204
View File
@@ -0,0 +1,204 @@
# Build RL environments with REA
REA turns a recorded web app session into a runnable environment for training
and testing AI agents. An agent can practice tasks such as finding an issue,
creating a folder, or editing a record without running the original app.
REA builds the tools the agent calls, the local data those tools use, and a
test that checks the environment starts and responds. It supports changes
when the recording confirms their effects. You can then use the environment
with your RL framework to set task goals, check rewards, and run training.
## Before you start
Install [REA and its agent integration](installation.md). To record an app,
you also need a running test instance and a Chrome-family browser. Choose an
instance where the agent is allowed to create, edit, and delete data. REA
records the app you provide; you start it and prepare its test data.
To run the exported environment, install the
[AgentEnv framework](https://github.com/scaleapi/agentenv-framework#install)
and start Docker. REA's [compatibility check](testing.md#exported-environment-bundles)
uses AgentEnv version `0.9.1431` with Python 3.12.
There are two ways to build an environment: ask your agent to record the app,
or start from a recording you already have.
## 1. Record and build with your agent
Give your agent the app URL, the operations you need, your browser's executable
path, and an output directory. For an app with folders, adapt this prompt:
```text
Use REA to build an AgentEnv environment from my test web app at
http://127.0.0.1:3000. Cover listing, creating, renaming, and deleting folders.
You may create and remove test folders. Leave the existing data unchanged.
Sign in as admin using the password in APP_PASSWORD. Use the browser at
/absolute/path/to/chromium. Save the environment to
/absolute/path/to/my-env, which does not exist yet.
Record the session, check which operations the environment supports, and
export it. Report any unsupported operations and what is missing.
```
Replace the URL, account, paths, and folder operations with your own. Set
`APP_PASSWORD` in the environment that starts REA's MCP server so REA can use
it without putting the password in the prompt.
Your agent inspects the app, records a session, checks the result, and exports
a folder containing the runnable environment. It reports which operations
work and which need more evidence. The app is used during recording; the
exported environment runs from the saved data.
After your agent exports the folder, skip to
[run the environment](#3-run-the-environment). The next section is an
alternative for an existing recording.
## 2. Or build from a saved recording
A HAR file records a browser's network requests and responses. Export one
with API response bodies from your browser's network panel, or use a
recording you already have. To include create, edit, and delete operations,
record those actions and the reads that show their results.
Build the environment into a new or empty directory:
```sh
rea environment-build ./session.har ./my-env --name my-env --json
```
You can also pass capture Evidence JSON saved by REA's
`capture-browser-scenario` or `inspect-web-network-capture` commands. Here,
Evidence means REA's saved recording with its source information. The build
accepts HAR files and those JSON recordings. It reads local data and writes
the environment folder without contacting the app.
Add `--dry-run` to review the build report without writing the folder. For an
unreleased REA checkout, use `--package-tarball` with its packed package so
the exported environment runs the same implementation.
The build report tells you what the environment supports:
| Report field | What to check |
| ----------------- | ------------------------------------------------------------------------------------------------------------------------ |
| `state` | Whether the environment can answer from local state or uses recorded-response replay. |
| `coverage.reads` | Which read operations use state and which replay recorded answers. |
| `coverage.writes` | Which create, edit, and delete operations are supported. Each withheld operation includes a reason and a recording hint. |
| `warnings` | Recorded content or local paths to review before sharing the folder. |
A successful build means the folder was created. Check the report to confirm
it supports the operations your tasks need. `coverage: null` means the build
could not prepare state for this environment and it will use replay.
## 3. Run the environment
Whether your agent exported the folder or you built it from a HAR, run it
with AgentEnv:
```sh
agent-env run ./my-env
```
Use the folder path your agent reported if it differs. The first run builds
the container image, so it needs access to the image and package sources.
REA includes a basic test, called the smoke task. It checks the expected tool
count and, when a suitable read tool exists, calls it and checks its response
status. Passing this test shows the environment starts and responds in the
tested setup. It does not yet score an agent on an application task.
The folder contains the recording, a tool manifest describing the available
operations, a Dockerfile, environment configuration, and the test. The
[export reference](agent-environments.md#export-a-bundle) describes each file.
## 4. Add tasks and reward checks
Start with one task the exported tools support. Give it a clear goal and a
checkable result. For example, if folder creation is supported, ask your
agent:
```text
Use this exported environment to add an AgentEnv task that asks an agent
to create a folder named Practice. Check the saved environment state to
confirm the folder exists with that name, and use that check to score the
result. Reset the environment before each run so attempts start with the
same data. Use the AgentEnv task and verifier formats.
```
This asks your agent to help build the task and its grading check around the
environment REA generated. A retrieval task might check an answer against a
recorded value. A task that changes data should check the resulting state.
When state is available, the environment supports reset, load, and read-back
operations for repeatable runs.
Use the [AgentEnv documentation](https://www.agentenvframework.com/docs) for
task configuration, agent connections, scoring, and execution. Connect the
resulting tasks to your chosen RL training workflow.
## Improve an operation that is missing
For REA to support a change, the recording needs to show what happened before
and after it. When recording against your test app:
1. Read the list and an existing item before making changes. Repeat the
initial reads to show which values change independently.
2. Create a test item, read it back, and read the list before the next change.
Repeat with a second item and different text.
3. Edit each test item, then read the item and list again. Leave more than a
second between creation and the first edit to show time-dependent changes.
4. Delete the test items, then read them back and read the list to confirm
their removal.
Also record filters, searches, sorting, and paging if your tasks need them.
The agent should check that every recording step completed before using it.
The [recording workflow](https://github.com/morluto/rea/blob/main/.agents/skills/reverse-engineer-anything/references/environments.md)
has the detailed browser instructions, including timeouts and handling IDs
assigned by the app.
Use the report's hints to decide what to record next:
| Reason | Next step |
| ------------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
| `unconfirmed` | Read the item and its list after the change. |
| `no-state-target` | Check the path template and recorded views; record missing reads. The route form may not be supported as a mutation target. |
| `no-observation` | Read back a created item. For an edit or delete, also read it before changing it. |
| `contradicted` or `side-effect` | Inspect the reported disagreement. The operation may need behavior REA cannot model yet. |
See the [full reason table](agent-environments.md#build-a-bundle-in-one-step)
for the other cases. Rebuild from the new recording and check the report
again. A new recording replaces the build input; combining separate recording
sessions is not supported.
## Compare with the original app
For a stronger check, record a separate sequence of calls against the real
app, starting from the same test data as the original recording. This is a
probe. Restore that starting data before each probe sequence, then compare
the saved probe recordings with the environment:
```sh
rea environment-compare ./my-env/envs/my-env/manifest.json \
--evidence ./my-env/envs/my-env/evidence.json \
--probe ./probe-create-edit-delete.json --probe ./probe-reads.json --json
```
Each probe file must be capture Evidence JSON from REA. The comparison runs
offline and reports differences in responses, unsupported calls, and missing
observations. It accounts for supported differences in IDs, times, and
modeled generated values. Agreement applies to the calls those probes check.
See the [comparison reference](agent-environments.md#measure-fidelity-against-the-application)
for the exact rules.
## Before sharing the environment
The exported folder includes retained response bodies and local source paths.
Standard authentication headers are excluded, but custom headers, query
values, and body tokens can remain. Review the content you plan to share.
Credential-name warnings point to values worth reviewing; they do not remove
those values or establish that the recording contains no credentials.
For direct MCP serving, replay behavior, state controls, and resource limits,
see the [environment reference](agent-environments.md). The
[implementation index](specs/agent-environments/README.md) links the contributing
PRs and their verification records.
File diff suppressed because it is too large. Load diff
+384 -7
View File
@@ -30,10 +30,118 @@ code units; follow `coverage.next_offset`. The page preserves `source_status` an
`reported_total_bytes`: exhausting retained text does not make a partial decoded
manifest complete. Complete source Evidence remains exportable under the existing
session lifetime. Servers without retention reject summary before starting JADX.
One-shot CLI output remains complete. Older servers require the existing complete
request after schema discovery. APK authenticity and runtime behavior remain
One-shot CLI output remains complete by default. Select `--detail summary` with
`--evidence_output /local/capture.json` to retain complete Evidence before returning
a linked view. Older servers require the existing complete request after schema discovery. APK authenticity and runtime behavior remain
unverified.
## Manifest component projection
`inspect_android_package` also returns a static inventory of declared
activities, aliases, services, receivers and providers from the decoded
manifest. The binary XML decoder preserves component class spelling, including
`$` in nested classes, without applying JADX class aliases. `declared_name`
preserves the decoded source attribute; `name` resolves a
relative name only when the manifest package is known and the resolved copy
fits the UTF-8 byte budget. An unresolved package or exhausted name budget
retains the declared name and marks the inventory partial. Empty component
names and names with forbidden DEX characters retain their raw spelling and
are not treated as identities. Valid DEX spaces, including boundary spaces,
are preserved when resolving component names and matching alias targets.
Whitespace in the manifest package is rejected by its separate package grammar.
Component permissions inherit the application
permission when no component permission is declared, except an activity alias
with no permission: Android documents that case as requiring no permission
through the alias. `permission_source` distinguishes a local declaration,
inherited value, no requirement (including an explicit empty override), and a
value omitted because its resource/manifest-placeholder token is unresolved
or the total permission-byte budget was reached. Resource and placeholder
references are not expanded at this parser boundary. Repeated inherited
values share one inventory-wide byte budget.
Provider `android:readPermission` and `android:writePermission` overrides are
not projected as separate fields; their presence marks the inventory partial
and is called out in `limitations`. Relative component names are not resolved
against a manifest package with surrounding whitespace.
Class-name segments follow DEX `SimpleName` characters, including a leading
digit or hyphen, rather than Java source identifier rules. Relative-name
expansion does not prove class existence, DEX-version compatibility or runtime
loadability. SDK defaults accept JADX's decimal and hexadecimal integer
spellings; the original SDK declaration strings remain unchanged.
`exported` is the declared literal `true` or `false`, otherwise null.
`exported_default` is the documented default only where it can be established;
it remains null for unknown provider target SDKs, nonliteral declarations, or
components with filters targeting API 31 or later that omit the now-required
explicit declaration. A limitation records the latter validation error.
As in Android's package parser, an activity, alias or receiver filter without
an `<action>` is discarded and does not count toward either rule; a service
keeps such a filter. `intent_filter_count` still counts every declared filter.
Provider authorities and all inventory dimensions are bounded. `status:
"partial"` and `limitations` identify omitted declarations or values; a
capacity limitation appears only when an actual collection limit was reached.
The projection does not merge manifests, expand resource references, or
observe runtime enabled/export state.
`enabled` and `grant_uri_permissions` report only literal component-level
`android:enabled` and provider-level `android:grantUriPermissions` declarations.
They are `null` when absent or not a literal boolean; missing attributes are not
filled from application defaults. A nonliteral declaration gets a limitation.
`process` preserves the exact component-level `android:process` spelling (including
empty or symbolic values), or `null` when absent or omitted under the inventory-wide
1 MiB copy budget. Neither field resolves resources, merges manifest values or
establishes the effective runtime process or enabled state. The exact boolean
attributes are retained in `enabled_declaration` and
`grant_uri_permissions_declaration`, including resource references.
`intent_filters` preserves each filter's source ordinal, exact priority spelling,
actions, categories and individual data declarations (scheme, host, port, path,
path prefix/pattern/advanced pattern/suffix and MIME type). These are declarations,
not computed Android matching rules. `meta_data` lists names; values remain in the
complete decoded manifest. Component, filter, data and metadata `location` values
use one-based lines/columns and UTF-16 offsets into that manifest. A component's
`declaration_index` preserves its original declaration ordinal, including gaps.
`declaration_coverage` distinguishes complete, partial and unknown observations.
Historical Evidence omits newly introduced fields and defaults coverage to
`unknown`; omission never establishes absence. Current records contain the fields,
with null for absent or unresolved scalar declarations. The inventory retains at
most 32 filters and 64 nested action/category/data/metadata records per component,
and shares a 1 MiB structured-value copy budget across components. Omitted values
or unsupported filter constructs mark coverage partial and include a limitation;
complete retained manifest text remains accessible.
For a small answer, select `detail: "summary"` with an explicit view:
```json
{
"kind": "android",
"facet": "components",
"actions": ["android.provider.Telephony.SMS_RECEIVED"],
"fields": ["name", "permission", "enabled", "process", "intent_filters"],
"offset": 0,
"limit": 16
}
```
The same JSON works with CLI `--view` or retained `inspect_analysis_view`.
`kinds`, `names` and `actions` use exact matching; names accept either the recorded
declared or resolved name, actions match any requested action, and different
filters combine with AND. `exported` matches only explicit declarations, keeping
`exported_default` separate. Empty selectors match nothing. Every selected row
retains its parent-array `source_index` and `declaration_coverage`; source coverage,
parent identity and limitations remain alongside the page. Follow
`coverage.next_offset`. The 1 MiB projection budget can shorten a page; a single
oversized row requires fewer selected fields. Exhausting a partial source does
not establish complete manifest coverage. `facet: "component-summary"` applies
the same filters and counts kinds, declaration coverage and explicit/default
exported values separately. Both adapters use the same retained projection;
repeated pages do not reload JADX.
The service `foreground_service_type` field preserves the declared attribute,
including an empty value or surrounding spaces; `null` means the attribute is
absent or the component is not a service. This field does not validate service
type tokens or establish runtime eligibility.
## Supply tools
Use a **full JDK 17 or newer** (including its compiler module) and
@@ -147,6 +255,50 @@ 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.
Method reference queries also accept an external class, such as
`android.app.NotificationManager --method-name notify`, when the APK directly
invokes that exact DEX class and method name. If multiple prototypes are observed,
the input error lists their exact DEX descriptors and local overload indices;
repeat with an explicit `--overload-index`. These are the prototypes observed in
the APK, not the complete method inventory of an Android framework or library.
Such results report `target_definition: "external"` and
`reference_scope: "direct_dex_invokes"`. Each using method has `source_selector`
with `class_name`, `method_name` and `overload_index`; pass those fields with the
same APK path to `inspect_android_method` to inspect the caller. Repeated direct
invokes in one method remain one using node with multiple instruction offsets.
This scans raw invoke instructions in JADX's admitted APK methods without
generating their source. It does not resolve reflection, JNI, method handles,
inherited method lookup or possible runtime dispatch. Class-only queries and
class/method inspection still require an APK definition; an external target has
no invented body. Unknown class/method names remain input errors.
External prototype and caller metadata use a bridge-frame-derived representation
allowance that reserves space for retained raw calls and CLI/MCP copies. Exhausting
it returns a typed `resource_constraint`, rather than a truncated caller list
reported as complete. Direct instruction offsets have their separate explicit
`direct_invokes_exhaustive` coverage. Older retained Evidence may omit the new
definition, scope and caller-selector fields; omission does not establish them.
The bridge checks prototype expansion before copying descriptors or joining
displayed signatures. It reuses one bounded prototype inventory for the exact
class/method selection in the current APK session; replacing the session or
selection invalidates that retained result.
`inspect_android_class` also reports `dex_static_value` for each `static final`
field whose class definition encodes a value in its DEX static values, so
compile-time constants are readable even when the class has no `<clinit>`.
Each value is typed: `boolean`, `byte`, `short`, `char` (UTF-16 code unit) and
`int` values are JSON booleans or numbers, while `long`, `float` and `double`
values are strings holding Java's text for the value, so 64-bit values stay
exact. The field reports `null` when no value is encoded, the encoded value is
null or of another kind, or the field is not `static final`. The value is read from parsed
metadata without generating source, and a static initializer can still assign
the field; use `inspect_android_method` on `<clinit>` for that evidence.
Only `inspect_android_class` asks the bridge for these values.
`inspect_android_method` and `trace_android_references` read the member-only
class summary, so large encoded strings cannot push their member lookup past
the 8 MiB protocol frame limit.
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
@@ -168,11 +320,173 @@ instructions into field declarations, leaving an empty or incomplete method
body. The selected method's disassembly preserves those instructions and retains
the same source byte limit and explicit truncation reporting.
## Apply an R8/ProGuard mapping
Release builds shrunk by R8 or ProGuard rename classes and members. Select the
build's `mapping.txt` with `mapping_path` on `inspect_android_class` or
`inspect_android_method`, or `--mapping-path` on the matching CLI command:
```sh
rea inspect-android-class /path/Example.apk a.b --mapping-path /path/mapping.txt \
--detail summary --evidence_output /tmp/class-evidence.json
```
For agent workflows, select `detail: "summary"` on class and method inspection.
It preserves the complete normalized member inventory or selected method body,
including mapping annotations, while retaining duplicate raw provider calls in
the complete parent Evidence. The reply links that parent's identity and digest;
export it with `export_evidence_bundle` or read its summary again through
`inspect_analysis_view`. CLI summaries require `--evidence_output` and return
the portable bundle path. Omitted or `complete` detail preserves complete inline
Evidence. Search and reference operations retain their existing contracts.
Symbol summary views admit at most **1 MiB**, including view context. An admitted
method's source can already consume that amount; a larger combined summary
fails explicitly instead of silently truncating facts. MCP refusal includes
the retained parent ID. Select complete detail, or inspect one method rather
than a whole class, when the requested summary does not fit.
The mapping path must be absolute. Selectors still take the observed names, such as
`a.b`, and observed rows are unchanged. The result adds one `mapping` block with
the file's path, SHA-256 and size, the header values the file reports
(`compiler`, `compiler_version`, `min_api`, `map_version`, `pg_map_id`,
`pg_map_hash`), valid record counts and up to 16 malformed line samples. Its
`symbols` list has one entry per observed class, inner class, method and field;
`inspect_android_method` lists the class and the selected method. `coverage`
counts each resolution status. With summary delivery, these unchanged facts are
under the view's `summary` field; the view separately reports source coverage.
Lookups use the bridge's raw DEX names, not JADX display aliases such as a
renamed reserved word. Each symbol resolves as:
- `mapped`: exactly one record matches. A class needs one record for its raw
name (`verified_by: "class"`). A method must also match its DEX prototype
(`"descriptor"`) and a field its DEX type (`"type"`). R8 residual signature
metadata is used when present; otherwise original types are translated
through the file's class records.
- `ambiguous`: `multiple-records`, `descriptor-mismatch`, `type-mismatch`,
`ambiguous-class` or `residual-signature-conflict`, with `candidate_count` and
up to 8 candidates. Conflicting residual metadata never selects a winner. A name match
whose prototype or type does not verify is never reported as mapped.
- `unmapped`: no valid record matched the raw identity.
- `unknown` with reason `projection-budget`: this claim did not fit the combined
reply budget. The observed raw symbol remains present; `coverage.unknown`
counts these omissions. Inspect that specific class or method separately,
or consult the selected local mapping file for complete source records.
A line continues an inline frame stack when it repeats the previous line's
minified range and obfuscated name and gives a single call-site line, such as
`:7`. Only the outermost original of a stack becomes a candidate. Original
line numbers and source files are not projected. Malformed records are counted
and skipped, so an `unmapped` symbol might be described by one of them. A file
that is missing, not a regular file, not UTF-8, larger than the byte limit or
without any class record, such as `usage.txt` or `seeds.txt`, is refused before
JADX is used. Requests read their mappings one at a time, in the same queue as
the JADX work and under the same operation deadline. A mapping failure therefore never starts or retires the JADX
session, and a different mapping reuses the loaded APK.
## Link native declarations to JNI candidates
When advertised, `inspect_android_jni_bindings` links original `ACC_NATIVE`
declarations from standard root `classes*.dex` members to bundled ELF libraries.
The supplied JAR's DEX reader preserves duplicate declarations before decompiler
deduplication. This operation does not load the APK into the decompiler.
MCP defaults to retained summary delivery when `detail` is omitted; select
`complete` explicitly to receive the full report inline.
```json
{
"path": "/absolute/Example.apk",
"class_name": "org.conscrypt.NativeCrypto",
"method_names": ["EVP_PKEY_new_RSA"],
"abis": ["arm64-v8a"],
"detail": "summary"
}
```
Class/method names and optional `descriptors` select raw DEX identities.
`library_paths` selects exact logical APK paths such as
`lib/arm64-v8a/libconscrypt_jni.so`; each library's `source_path` preserves its
original ZIP spelling. Omitted filters include all scoped facts; `[]` matches
none. Class selection never narrows the global inventory used for table-class
attribution.
Independent `declarations`, `libraries`, `candidates` and `bindings` collections
have stable IDs and source ordinals. Candidates retain APK/library digests,
ABI, function addresses and file offsets. Table candidates also retain section,
table and entry identity plus all three name/signature/function pointer slots
and their relocation sources. JNI exports follow the
[UTF-16 escaping and short-before-long rules](https://docs.oracle.com/en/java/javase/21/docs/specs/jni/design.html#resolving-native-method-names);
failed escaping does not acquire an invented export match. Tables require
NUL-terminated [Modified UTF-8 strings](https://docs.oracle.com/en/java/javase/21/docs/specs/jni/types.html#modified-utf-8-strings),
valid descriptors and executable load mappings. REL, RELA and RELR retain
separate provenance; unsupported packed relocations and unresolved pointer
spans stay explicit unknowns.
A static `JNINativeMethod` entry names no class. Attribute it uniquely only
when exactly one declaration in the complete recorded native inventory has
that `(name, descriptor)` pair. Multiple classes remain ambiguous after a
class filter; a partial inventory cannot establish unique attribution.
Each selected declaration has a row per selected/observed ABI. `matched` means
candidate presence was observed; `ambiguous` means multiple observed targets
or class associations remain. Binding `coverage` is separate: a matched
partial row does not establish complete uniqueness, and an empty partial scan
is `unknown`, never `unmatched`. ELF/ABI label mismatches preserve facts without
asserting a binding. No result establishes library loading, runtime registration
or method invocation.
Summary returns the first 64 selected binding rows, their declaration/candidate
closure and every selected library's identity, coverage and failure reason;
counts and page coverage describe the full selected binding set. Selected
declaration IDs have separate preview coverage, so native declarations remain
visible even when the APK has no bundled ABI libraries. Complete
Evidence stays retained. Read exact fields or further pages without reopening
files or rerunning analysis:
```json
{
"source": { "kind": "retained-evidence", "evidence_id": "ev_<digest>" },
"view": {
"kind": "android-jni",
"collection": "bindings",
"offset": 64,
"limit": 64,
"fields": ["declaration_id", "abi", "status", "coverage", "candidate_ids"]
}
}
```
Collections also include `declarations`, `candidates` and `libraries`, with
exact filters and batch lookup by declaration/candidate/library IDs. `row_index`
is the original parent collection index; DEX and
ZIP source ordinals stay separate. Complete detail returns the full report.
CLI uses the same selection and delivery contracts. Its common one-shot
default remains complete; summary delivery requires an explicit Evidence path:
```sh
rea inspect-android-jni-bindings /absolute/Example.apk \
--query '{"class_name":"org.conscrypt.NativeCrypto","method_names":["EVP_PKEY_new_RSA"],"abis":["arm64-v8a"]}' \
--detail summary --evidence_output /absolute/fresh-jni-evidence.json
```
Optional `--view` accepts the same JSON page and requires summary delivery.
Evidence bundles never overwrite files. Complete declarations and one library
selection are cached within the immutable APK session. Changing ABI/library
selection replaces the scan; class/method selection reuses it. Fresh analysis
still validates current caller APK bytes. Idle/close releases the caches;
retained Evidence remains historical and independently readable.
## Interpret the result
- Package inspection retains decoded manifest XML, summary declarations,
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 filters the engine's cached top-level names before transferring
pages, then follows contiguous inventory positions without rescanning earlier
@@ -187,10 +501,22 @@ the same source byte limit and explicit truncation reporting.
full reported display types, fields and inner class names. Synthetic members
that code generation hides can appear in the inventory; it stays consistent
after method decompilation within the retained session.
Class and method inspection and reference tracing accept either the exact
reported class name or its original binary name, including `$` in nested and
anonymous classes. The bridge keeps both identities from JADX; it does not
replace separators or guess suffix matches. Ambiguous selectors are refused.
Follow-up calls preserve the selected name, while results report the frozen
JADX display name. Quote `$` names in the shell, for example
`'com.example.Manager$WorkerThread'`.
Exact DEX prototypes are reported from frozen JADX method metadata as
`dex_descriptor`, for example `(I)V`. Display names can be JADX aliases;
the descriptor alone is not a complete raw DEX method ID. Overload indices
are tied to this artifact and engine, not stable identities across builds.
- A `mapping` block appears only when the caller selected `mapping_path`. Its
names are claims from that file, not observations of the APK. REA does not
check that the mapping belongs to this build; `pg_map_id` is reported, not
compared. Evidence keeps the selected path in its parameters and the file's
SHA-256 in the result.
- Method inspection reports Java or smali, fallback markers and complete/partial
returned text. Decompiled source is a derived representation. A complete text
result does not establish complete semantics or a byte-identical reconstruction.
@@ -256,6 +582,10 @@ 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
@@ -276,6 +606,19 @@ target/configuration changes, failure, timeout, cancellation, MCP disconnect or
server shutdown. One-shot CLI commands always join cleanup before returning.
REA never writes to the original APK or launches target code.
JNI scans reuse the analyzer process without loading the APK into the
decompiler. They admit at most 64 MiB per DEX member and 256 MiB of DEX bytes,
with a 4 MiB native-declaration JSON allowance before MCP string escaping.
Native libraries are processed sequentially: one 64 MiB buffer, 256 MiB total
selected read work, and a 16 MiB parsed-library-facts allowance. ZIP inventory
and library counts also have explicit resource bounds. Unexamined occurrences
remain unknown. A complete relationship report has a separate 16 MiB serialized
allowance; summaries/pages allow 1 MiB including their provenance envelope.
These are resource admissions, not claims about maximum valid Android formats.
Select fewer ABI/library occurrences or smaller retained pages when needed.
Completed parsed library facts survive failed reader finalization in typed
partial observations; the exact reader owner remains available for cleanup retry.
On Windows, stdin writes preserve stream backpressure and run outside the Node
event loop. Cancellation terminates the retained job, including descendants,
before workspace cleanup. On Windows, a detached REA helper owns the private
@@ -335,9 +678,18 @@ 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. The same deadline bounds each upstream
call, a timeout reports the selected value, and a passing JADX configuration
check in `rea doctor` reports the effective `operation_timeout_ms`. Explicit
else is refused before Java starts. REA reads it from the environment captured
at process start. Changing that environment requires a new REA process. On a
running MCP server, set `jadx.operation_timeout_ms` through
`update_analysis_settings` to change the default for newly accepted operations;
queued or running operations retain their accepted budget. The same deadline
bounds each upstream call. Timeout recovery reports the effective milliseconds
and range, with `details.limit` identifying the runtime setting. An invalid
provider-local launch value can also be repaired by a runtime override; reset
exposes that invalid launch value again. See the
[settings design](design/limits-and-recovery.md#invalid-launch-configuration).
A passing JADX configuration check in `rea doctor` reports its launch-derived
`operation_timeout_ms`; it does not inspect another running server's overrides. 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**
@@ -352,6 +704,27 @@ complete, case-sensitive search contract. A protocol frame above **8 MiB**, or c
**32 MiB per operation**, fails with a diagnostic instead of returning an incomplete inventory
as complete. These are memory-safety boundaries, not undocumented result caps.
A selected R8/ProGuard mapping is re-read and SHA-256 checked on every call, up
to **64 MiB**. The serialized provider keeps one successful parsed index, keyed
by byte length and digest with fixed parser options. Identical bytes reuse that
index; changed bytes are reparsed, missing files still fail, and each response
preserves the currently selected path. The index is released on provider idle
retirement or close. Omitting `mapping_path` never applies the cached mapping.
During replacement, the old index can coexist with the new parse until it
succeeds; these transient allocations count toward process memory.
Before retaining records, the parser admits an estimated **128 MiB** index
representation and **8 MiB** of expanded mapped claims per residual class,
including shared header facts. The index allowance accounts for strings and
containers; it is not a measured V8 heap cap. Parameter and metadata expansion
is admitted before splitting or JSON decoding. These fixed resource bounds
have no runtime override; refusal reports `resource_constraint`, the source
line and mapping SHA-256. Ambiguous lookups count all candidates while keeping
only the first eight in stable file order. A separate **8 MiB** combined claim
budget applies to the actual query set, including outer and inner classes,
before cloning the report. Claims that do not fit become explicit `unknown`
entries; other admitted claims and all observed identities remain available.
## Real verification
Download the explicit public fixtures once; this installs no tools:
@@ -395,3 +768,7 @@ containing node's unknown `dex_offset`. `direct_invokes_exhaustive: false`
means instruction rows were bounded by the local bridge frame budget; `null`
means the producer did not establish instruction coverage. Using-node totals
and coverage do not imply complete instruction evidence.
`direct_invokes_coverage` reports counts of using nodes with complete, partial,
or unknown positions, plus the number of retained instruction sites. This
aggregate makes position truncation visible without scanning every caller;
older retained Evidence may omit it, which means the aggregate was unexamined.
+178
View File
@@ -0,0 +1,178 @@
# Offline API contract inventory
`inspect_api_contract` / `rea inspect-api-contract` supports local OpenAPI JSON
and GraphQL SDL (`format: "graphql-sdl"`). Neither profile contacts endpoints.
## OpenAPI JSON
The default profile reads one local OpenAPI
3.0, 3.1 or 3.2 JSON document. It inventories inline operation slots under
`paths`; the facts describe declarations, not implemented, reachable or
authorized runtime behavior. YAML, AsyncAPI, callbacks/webhooks, schema graphs,
contract diffs and traffic correlation are outside this version.
The OpenAPI importer never resolves references, opens sibling documents, evaluates
examples or contacts declared servers. OpenAPI 3.2 `query` and
`additionalOperations` follow the [Path Item contract](https://spec.openapis.org/oas/v3.2.1.html#path-item-object),
including case-sensitive additional HTTP method spelling.
## Select a useful answer
MCP defaults to a summary retaining the complete inventory. An optional view
returns selected facts immediately, taking precedence over `detail`:
```json
{
"path": "/artifacts/openapi.json",
"view": {
"kind": "api-contract",
"collection": "operations",
"methods": ["GET"],
"limit": 20
}
}
```
`operations` supports exact `operation_ids`, `operation_keys`, `methods`,
`paths`, `declared_ids` and `states` filters. Filters combine by intersection;
empty arrays select nothing. `fields` chooses columns; identity, source pointer,
state and unknown reason remain. The other collections are
`path-item-references` (optional `reference_ids`) and `diagnostics` (optional
`codes`), also with fields and offset/limit. The default page contains 64 rows.
Query a retained result with `inspect_analysis_view`, using the same view and
`source: {"kind":"retained-evidence","evidence_id":"..."}`. Copy the complete
parent ID from `normalized_result.parent_evidence_id`, not the summary's own ID.
These pages use immutable retained facts without rereading the specification.
Complete Evidence remains available through `get_evidence_bundle`.
CLI defaults to complete Evidence. For a selected answer, save that complete
Evidence in an absent bundle path:
```sh
rea inspect-api-contract ./openapi.json --detail summary \
--view '{"kind":"api-contract","collection":"operations","methods":["GET"],"limit":20}' \
--evidence_output ./contract.evidence.json --json
```
Later `rea inspect-analysis-view` can read that bundle through an exact
`evidence-file` source selection, even after the original specification is
unavailable. Publication never overwrites an existing file. CLI preserves its
original path spelling in `caller_input_path`; analysis locations and MCP inputs
use absolute filesystem paths.
## Identity, provenance and coverage
Each operation has an artifact-scoped `operation_id`, an exact method/path
`operation_key`, an optional `declared_id` from `operationId`, and its original
JSON Pointer. Keys can correlate exact declarations in a caller-selected pair
of revisions; they do not establish equivalence across unrelated APIs, renamed
path templates or runtime handlers. Declared IDs are not assumed unique.
The artifact includes the verified byte length, SHA-256 and selected path.
Malformed siblings retain useful declarations plus diagnostics. A Path Item
`$ref`, including a local fragment, remains unresolved; adjacent inline slots
are unknown because referenced fields may overlap. Missing or malformed paths
are explicit, and missing required OpenAPI 3.0 paths does not become a complete
empty inventory. `source_coverage` is limited to inline paths and records that
reference resolution and full schema validation were not performed. Page
exhaustion describes the selected rows and never upgrades source coverage.
Structured transport userinfo credentials are excluded from reference URIs;
`credential_redacted` reports that change, and an ambiguous credential span leaves
the displayed reference unknown. Ordinary reference query/fragment text and
names are retained. Original bytes remain in the caller's artifact; complete
inventory Evidence does not embed the entire source JSON. Duplicate JSON members
use the shared parser's last-value-wins behavior, recorded in source coverage.
If decoding or later validation fails after a verified read, the typed error
retains a compact `file-snapshot-hash` observation with the exact path, digest
and byte length. Syntax diagnostics do not quote source JSON. Cancellation
does not replace a parser failure that has already completed.
## Resource bounds and verification
The file-byte allowance and conservative expanded-facts allowance are each
32 MiB resource policies, not OpenAPI format limits. `max_file_bytes` can lower
the input allowance. Native JSON parsing additionally checks available heap
against the shared representation-expansion estimate before decoding. The
shared JSON nesting limit is 1,000, including uninterpreted schema regions.
Capacity refusals are typed; reply paging does not increase parser allowances.
The committed [Petstore fixture](https://github.com/morluto/rea/blob/b7c8caedd4166e6032c49024cdc6fb1154f1a2df/tests/fixtures/openapi/README.md) is an
original document from the published application, replayed offline through
public CLI/MCP tests. These check advertised schemas, complete/selected parity,
retained paging, partial references, zero requests to a loopback reference
server and unchanged source bytes. They do not establish runtime endpoint
behavior or full OpenAPI conformance.
## GraphQL SDL and operation documents
Select `format: "graphql-sdl"` for SDL. Optionally supply `operations_path` to
one local document containing operations and fragments. Both MCP paths are
absolute; CLI accepts relative spellings:
```sh
rea inspect-api-contract ./schema.graphql --contractFormat graphql-sdl \
--operationsPath ./queries.graphql --detail summary \
--view '{"kind":"graphql-contract","collection":"references","limit":20}' \
--evidence_output ./graphql.evidence.json --json
```
Collections are `types`, `fields`, `definitions`, `references`, `fragment-spreads`
and `diagnostics`. Each supports `fields`, `offset` and `limit` (default 64).
Exact batch filters include type/field/definition/reference IDs, type names,
`field_keys` such as `Query.viewer`, owning type names, binding/spread states
and diagnostic codes. Filters intersect; an empty array selects nothing.
Identity, source and applicable uncertainty fields always remain. Retained pages
and bundle-based queries use the same `graphql-contract` views without rereading
SDL or rebuilding its schema model.
Types and fields retain individual declaration occurrences, including extensions,
list/non-null wrappers, implemented interfaces and union member names. Operation
and fragment definitions own field references with aliases and parent-reference
IDs. Each reference links to a supplied SDL field occurrence, remains `model-only`
for a library-provided field such as `__typename`, or records an exact unknown
reason. Rejected schema construction keeps declarations but cannot supply trusted
bindings. Missing or duplicate fragment names remain explicit. Fragments are
visited once in their declared type scope and linked by spread edges; cycles
are never recursively expanded.
The versioned `graphql-sdl-field-bindings-v1` profile records the GraphQL library
version. It performs syntax parsing and schema construction for field resolution;
it does not establish whole-schema/operation validity, spread applicability,
reachability, authorization or endpoint behavior. Arguments, defaults,
descriptions, directive behavior, enum value inventory, introspection JSON,
federation composition and version comparison remain outside this profile.
Every fact has a document role, SHA-256, UTF-16 start/end offsets and parser token
line/column. Offsets address decoded UTF-8 text, including an optional BOM, rather
than byte positions. The two files are independently verified snapshots, not an
atomic pair. Fact IDs identify source occurrences; `field_key` separately supports
exact name-based joins and does not assert equivalence across revisions. CLI
retains its auxiliary path spelling in `caller_input_paths.operations_path`,
outside semantic identity, as well as its primary `caller_input_path`.
Malformed operation text retains the SDL inventory with partial coverage and a
source-located diagnostic. A primary parser refusal or later resource/cancellation
failure retains known source identities and available interpreted Evidence;
syntax diagnostics do not quote source text. Complete Evidence contains facts,
not original document bytes. An exhausted page never upgrades source coverage.
Each file has the same configurable maximum of 32 MiB. Conservatively charged
facts share a 32 MiB allowance; text/AST allocation is admitted against available
heap. The parser admits at most 262,144 lexical tokens and 65,536 comments per
document; subsequent iterative AST admission limits traversal depth to 128.
These are REA resource policies, not GraphQL language limits. Very deep input can
be refused by the parser before AST admission. Selected replies reduce transport
and retained queries reuse immutable facts; they do not avoid the initial parse.
The committed SWAPI SDL is an unchanged public application fixture with provenance
in `tests/fixtures/graphql/README.md`. It supplies 53 types and 243 fields;
verification does not execute its API.
Large complete inventories can exceed the default MCP response budget even when
selected pages fit. Use `export_evidence_bundle` with a caller-selected local
path for complete retained Evidence, or CLI complete JSON/bundle publication.
A transport refusal does not erase a completed retained analysis. Narrow pages
and source coverage remain available on the same continuing connection.
+302
View File
@@ -0,0 +1,302 @@
# 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. Issuer matching preserves the encoded Name and also admits equivalent
ASCII PrintableString/UTF8String values for CN, C, L, ST, O and OU: case and
repeated/edge spaces are normalized, RDN sequence order is preserved, and
attribute order inside one multivalued RDN is ignored. Other name forms
require exact encoded identity; semantic equivalence remains unestablished.
`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 the exact carried DER (each is `null` otherwise). The parser's returned
DER must equal those bytes before readable facts are combined with their
fingerprint; embedded PEM text and trailing data cannot select a different
certificate. 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).
A single present block scheme with known signers agrees with itself;
v1 is compared separately below.
- `signer_change_declared_by_rotation` (declared): the signers differ, but
each appears in a lineage the APK declares. Read the two together: block
schemes with different signers covered by a declared lineage show `false`
/ `true`; differing signers without such a lineage show `false` / `false`.
A lineage alone does not establish a change between block schemes. 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. When v1 uses
the lineage's original key and v3 its newer key, this comparison reads
`true`. If v3 is the only present block scheme, agreement is `true` and
declared inter-block signer change is `false` even though rotation is
present. If v2 also uses the original key, agreement is `false` and the
declared inter-block change is `true`. `null` when
v1 or a populated 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. When identities or a lineage origin are unknown, limitations
explain why; absence is shown in the scheme records.
- `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. ZIP tail offsets, the central-directory size and signing
block spans used by the reader are checked against their containing sections,
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.
Signing blocks admit at most 4096 ID/value pairs; at most 4096 signing-relevant
META-INF entries are retained. These fixed counts, signer/lineage counts and the
report byte budget return nonretryable `resource_constraint` errors naming the
effective allowance. This release has no runtime override for them. Waiting for
an active scan cannot raise a fixed count, and complete CLI JSON cannot raise the
report budget. Occupied scan-queue capacity remains retryable after a scan finishes.
The whole-file reader allocates the selected snapshot and bounded read scratch;
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.
If the stable read completes but closing its descriptor fails or is cancelled,
the error retains `details.partial_observation` with the selected path, complete
file SHA-256 and byte count. These facts describe the acquired file; they do not
establish any decoded signing facts. Failed cleanup remains visible until the
provider releases the descriptor on a cleanup retry.
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
offset or scheme and reason. Zip64 tail damage includes a missing required
locator, an impossible locator disk/count pair, a fixed end record that does
not fit before its locator, or an incorrect end-record signature. These are
input errors, not evidence of an unsupported format. Coherent multi-disk
locator metadata and 64-bit offsets/counts/sizes beyond the reader's safe
integer range remain unsupported. The locator offset is disk-relative, so
multi-disk metadata is checked before applying local file bounds. This follows
the record fields in [PKWARE APPNOTE 6.3.10, sections 4.3.14–4.3.15](https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT);
it does not establish full validation of Zip64 versions or extensible data.
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.
+28 -4
View File
@@ -65,6 +65,7 @@ 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
@@ -92,9 +93,22 @@ still suppresses all string projection, including in this mode.
`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.
A change during snapshot acquisition reports retryable `artifact_changed`;
wait for a stable input before retrying. Host read permission failures report
`access_denied`, while a missing or non-regular selected path reports
`invalid_request` with the rejected `path` field.
- **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.
The `version: 2.9.0`-or-later header that Apktool 2.9+ and 3 emit selects
their 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
[2.9.0 metadata writer](https://github.com/iBotPeaches/Apktool/blob/b64e257c9b689eb9630ad0241f2fe4f4a749955b/brut.apktool/apktool-lib/src/main/java/brut/androlib/apk/VersionInfo.java#L38-L42),
[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
@@ -133,9 +147,14 @@ still suppresses all string projection, including in this mode.
## 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.
@@ -161,6 +180,11 @@ 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](../experiments/analysis-efficiency/apktool-string-tables.md).
[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.
If the selected APK changes while its private snapshot is copied, acquisition
returns `artifact_changed`; request-schema correction cannot recover a stable
snapshot. Launcher spawn permission denial is distinct from a missing launcher,
and configuration diagnostics retain the selected command and system error.
+78 -21
View File
@@ -101,8 +101,8 @@ limitation reports how many inventoried entries were left out. IPA projection
still lists every archive component, including `SwiftSupport/`.
`platforms` lists `macos` for application roots with `Contents/`. It lists `ios`
for shallow application roots that have an `Info.plist`. An application root
with neither, such as an empty inventory, has no platform.
for shallow application roots that have an `Info.plist` at their root. An
application root with neither, such as an empty inventory, has no platform.
## macOS bundle anatomy
@@ -149,6 +149,11 @@ directories rather than `Versions/Current`. A framework's `info_plist_path` is
because `Versions/Current` decides which one applies) or when its single
version directory has no `Resources/Info.plist`.
A `shallow` bundle's `info_plist_path` is its root `Info.plist`. Without one,
it is `Resources/Info.plist`: a flattened macOS framework keeps its binary,
`_CodeSignature/`, and `Resources/` directly in the `.framework`, like a single
`Versions/<version>/` directory without the `Versions/` wrapper.
AppleDouble sidecar files (`._*` and `__MACOSX/`) are inventory facts, but they
describe neighbouring files. They are excluded from roots, bundle roles, and the
anatomy components.
@@ -156,6 +161,10 @@ anatomy components.
The projection does not read plists. Use `inspect_plist` on a bundle's
`info_plist_path` or a launchd plist to read `CFBundleExecutable`, identifiers,
`SMPrivilegedExecutables`, `BundleProgram`, URL schemes, and usage descriptions.
MCP calls require an existing `open_binary` or `open_bundle_resources` session,
including when `inspect_plist.path` is explicit. For a bundle without a resolved
executable, open its resource session first. The CLI `rea inspect-plist <path>`
opens the selected artifact for its one invocation.
Use `inspect_signature` on an executable candidate for code-signing claims.
## Dylib load resolution
@@ -180,8 +189,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:
@@ -196,34 +218,69 @@ Resolution follows dyld:
already loaded image reuses it.
- Symlinks such as `Versions/Current` are followed inside the analyzed root.
An escape through `..` or an absolute link is `escapes-target`.
- Dyld path components are POSIX names. On Windows hosts, link text uses `\`
as its separator, and a dyld component that contains `\` or `:` cannot name
a file in the analyzed root, so its candidate is `absent`.
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
+123
View File
@@ -0,0 +1,123 @@
# Apple IPS crash reports
`inspect_apple_ips` / `inspect-apple-ips` answers whether one selected local
Apple `.ips` file is a crash report (`bug_type` string `"309"`), which process,
exception, and termination facts that report records, which threads and
`usedImages` rows it lists, and which documented crash classes its strings
match. It reads the file without executing a target and does not require Xcode,
atos, a disassembler, or pwntools.
```bash
rea inspect-apple-ips /absolute/path/to/report.ips --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_apple_ips",
"arguments": { "path": "/artifacts/report.ips" }
}
```
## Observations
The inline Evidence result contains the artifact path, SHA-256, and byte length.
`metadata` is the first-line JSON object. `bug_type` stays a string. The crash
body is parsed only when that string is `"309"`. Other types, including `"288"`
stackshots, return no crash result.
`process`, `exception`, `termination`, `faulting_thread`, `threads`, and
`images` reproduce documented body fields when present, including each image's
`name` and each frame's `symbolLocation` (as `symbol_location`, the byte offset
from the start of `recorded_symbol`). `termination` keeps the terminating
process as `by_proc` and `by_pid` and its `flags`; `process.parent_pid` keeps
`parentPid` next to `parent`; and `metadata` keeps `build_version` and
`platform`. Integers keep their
decimal literals and a hexadecimal form, including arm64e registers above
2^53. Frame roles follow the recorded frame index: row zero of the resolved
faulting thread is `faulting_instruction`, row zero of other threads is
`current_instruction`, and later rows are `return_address`. Each runtime
address uses that row's own image base and offset. The recorded `inline` flag
does not supply a missing offset or authorize borrowing another row's address.
When `faultingThread` is missing or does not resolve to a recorded thread
object, row-zero roles are `unknown`; the recorded lookup remains available.
A translated process records Rosetta's registers in a nested
`threadState.rosetta` object; each one is reported as `rosetta.<name>` and
counts toward the register budget. A [public ReportCrash `.ips` log](https://github.com/Mortennn/Dozer/issues/189)
shows `rosetta.tmp2`, `tmp1`, and `tmp0` beside ordinary x86 registers. The
test fixture quotes that register excerpt inside an authored report body; it
does not generate a new Rosetta crash during the test.
Observed JSON strings retain `""`; `null` means the field was missing or JSON
`null`. This includes image paths, frame symbols, process text, and the older
string `osVersion` form. Unlisted JSON keys and generic register names also
retain empty spellings. An empty image architecture remains recorded as `""`
while `uuid_unknown` stays true, since that spelling does not establish an
architecture. An empty UUID is malformed, so its exact spelling appears in
`coverage.type_mismatches` and its canonical UUID remains unknown.
`process.os_version` keeps the producer's dictionary (`train`, `build`,
`release_type`, `is_embedded`) or the older string form. Image UUIDs are uppercase 8-4-4-4-12; an all-zero UUID
or a missing or empty `arch` is `uuid_unknown`. Image `source` letters keep their
documented meaning (`P` process, `S` shared cache, `C` shared cache library,
`K` kernel, `U` kernel cache, `T` kernel text exec, `A` absolute). Keys the
documentation does not list remain in `unlisted_keys` or `unlisted_body_keys`.
Each frame's `image_index` refers to its entry in `images`. Image architecture,
UUID and `uuid_unknown` are retained once on that image. Resolve the reference
when using those facts; an absent or out-of-range index leaves the image
unknown. This avoids repeating image text for every stack frame.
A present value whose JSON type differs from the documented one is never read
as absent: it is listed in `coverage.type_mismatches` with its path, the
expected type, and the observed type, and `coverage.status` becomes
`partial`. A crash body without `threads` or `usedImages` is also `partial`.
Malformed image UUID strings likewise make coverage partial; their exact spelling
is retained in the mismatch's `recorded_value`, while the canonical image UUID
remains unknown. Both 32 hexadecimal digits and canonical hyphenated UUIDs are accepted.
Unresolved thread/image references also make coverage partial. A rejected
integer lookup keeps its exact decimal literal in `recorded_integer`, including
negative values and values above uint32. Valid uint32 lookup values remain in
`faulting_thread` or `image_index` even when the referenced row is unavailable.
`classification` matches only documented SIGKILL codes (compared by their
canonical hexadecimal form), `EXC_GUARD` subtypes,
pointer-authentication suffixes, and dyld missing-library strings. A report
that matches none of those rules is `unclassified`.
Opened Mach-O and dSYM UUID joins remain unobserved. Symbol names printed
inside the report are recorded claims and are not verified. Linux ELF cores
use the separate [recorded crash](recorded-crashes.md) workflow.
## Limits and failures
The selected file is limited to 1 MiB by default (16 MiB maximum). At most 256
threads, 8192 frames, 1024 `usedImages` rows, and 16384 `threadState`
register slots are examined by default (4096 / 65536 / 4096 / 262144
maximum). Budgets are checked before each row is parsed, and body members the
report does not interpret are syntax-checked without being kept. Fixed
allowances bound JSON nesting (64 levels), unlisted key names per object
(1024), and `termination.reasons` entries (256). Malformed metadata, a
truncated crash body, invalid UTF-8, a NUL in the text, and exhausted budgets
fail without partial success.
`max_integer_text_bytes` (`--max-integer-text-bytes` in the CLI) separately
bounds retained decimal and hexadecimal integer text, including every derived
runtime address and rejected lookup literal. Its default is 8 MiB of UTF-16 text storage and its maximum is
64 MiB. Each appearance counts, even when an object could be shared in memory
but would be serialized more than once. The parser reserves a conservative
upper bound before numeric conversion or address expansion, then releases the
unused reservation after each value. It reuses the parsed source integer when
the same image base contributes to many frames.
Raise this per-call allowance on the next call when more room is needed; no
restart is required. File-size and row budgets do not raise it. This allowance
is separate from JSON wire size, MCP response limits and the client's receive
buffer. An exhausted allowance reports a typed resource error without partial
success or truncated integer facts.
```bash
rea inspect-apple-ips /absolute/path/to/report.ips --max-integer-text-bytes 16777216 --json
```
This workflow does not execute a target, launch atos, or search for dSYMs.
+50 -10
View File
@@ -1,4 +1,6 @@
graph TD
%% Selected layer owners and dependency/wiring paths, not a directory or import inventory.
%% Current deep-provider candidates are wired in src/composition/binary.ts.
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 server adapter: stdio default, streamable HTTP opt-in<br/>config, session, process lifetime"]
@@ -6,7 +8,7 @@ graph TD
end
subgraph "Production Composition"
COMPOSITION["src/composition/<br/>fresh typed provider and session factories<br/>one-shot binding and independent optional loading<br/>static WASM/EVM descriptors: named contracts, factories, retryable lifetimes"]
COMPOSITION["src/composition/<br/>fresh typed provider and session factories<br/>one-shot binding and independent optional loading<br/>typed capability bindings: construction, availability and retryable lifetimes<br/>pure capability declarations feed provider catalogs"]
end
subgraph "Adapters"
@@ -19,22 +21,29 @@ graph TD
subgraph "Application Layer"
APP["src/application/<br/>Shared CLI/MCP workflows<br/>enhanced analysis and investigations"]
SESSION["SessionProviderRouter + BinarySession<br/>one immutable deep binding per target<br/>disjoint auxiliary routing"]
REGISTRY["AnalysisProviderRegistry<br/>sorted deep candidates<br/>deterministic selection, no fallback"]
REGISTRY["AnalysisProviderRegistry<br/>sorted deep candidates<br/>deterministic selection, no fallback<br/>ProviderInspectionScope: prerequisite observation lifetime"]
RECORDS["src/application/investigation/<br/>composed InvestigationRecords + narrow ports<br/>EvidenceLedger and Unknown ownership"]
SNAPSHOT["BinarySessionRecords + AnalysisSnapshotCache<br/>binary target/profile snapshots<br/>compatibility facade and post-commit notifications"]
LIFETIMES["CapabilityScope<br/>host-owned service close capabilities<br/>released peers removed; failed cleanup retained for retry"]
SNAPSHOT["BinarySessionRecords + AnalysisSnapshotCache<br/>binary target/profile snapshots and session file ownership<br/>record coordination and post-commit notifications"]
JS_RECON["src/application/javascript/ + src/artifacts/javascript/<br/>JavaScript artifact reconstruction<br/>directory/ASAR reads<br/>AST-only bundle projection"]
end
subgraph "Provider Layers"
PROVIDER_OWNERS["src/providers/<br/>shared deferred client and inspection owners<br/>exact cleanup retry and attributed failures"]
subgraph "Provider Layers (selected adapters)"
PROVIDER_OWNERS["src/providers/<br/>shared deferred client 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"]
KUNA["src/kuna/<br/>BYO Kuna deep analysis candidate<br/>discovery reads metadata; version probe on target open"]
IDA_MCP["External ida-pro-mcp<br/>GUI proxy or database supervisor<br/>upstream installation and engine lifecycle"]
KUNA["src/kuna/<br/>BYO Kuna decompiler, a deep analysis candidate<br/>discovery reads metadata only; version probe when a target opens"]
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"]
@@ -46,12 +55,19 @@ graph TD
FIRMWARE["src/firmware/<br/>Binwalk / Unblob adapters<br/>owned processes, report normalization, private extraction"]
ANDROID["src/android/<br/>Static APK adapter<br/>owned SDK stdio, pinned headless JADX, normalization"]
REFERENCE["src/reference/<br/>Filesystem reference-source reader<br/>bounded traversal, validation, errors"]
ANDROID_TOOLS["src/adb/ + src/apktool/ + src/jeb/<br/>BYO device, resource-decoding and JEB adapters<br/>owned adb and Apktool processes; JEB engine client over MCP"]
CAPTURE_TOOLS["src/captures/ + src/pcap/ + src/signals/ + src/sqlite/<br/>BYO tshark, pcap, sigrok and SQLite child inspection<br/>selected captures, recordings and databases; owned processes"]
NATIVE_READERS["src/go/ + src/rust/ + src/il2cpp/ + src/flutter/ + src/gpu/<br/>Bundled in-process binary metadata readers<br/>Go build info, Rust dependencies, IL2CPP, Dart build ID, GPU code objects<br/>src/compression/: shared codec-result helper for rust and gpu"]
FORMAT_READERS["src/model/ + src/python/ + src/erlang/ + src/electron/<br/>Bundled in-process format readers<br/>GGUF and safetensors headers, Python .pyc, Erlang BEAM, Electron fuses"]
RUNTIME["src/frida/ + src/cutter/<br/>Frida instrumentation sessions and live Cutter bridge client"]
end
subgraph "Provider Process Foundation"
PROCESS["src/process/<br/>owned process groups, private roots<br/>deadlines, bounded file reads and diagnostics, cleanup"]
WINDOWS["src/windows/<br/>Windows host boundary adapters<br/>package loader, opaque handles, owned jobs and private runtimes"]
WINDOWS_NATIVE["native/windows/src/<br/>Node-API implementation<br/>NTFS admission, protected DACLs and atomic Job Object assignment"]
FILESYSTEM["src/filesystem/<br/>owned file and directory handles<br/>stable reads, staged output, caller paths"]
end
subgraph "Contracts Layer"
@@ -60,7 +76,7 @@ graph TD
end
subgraph "Domain Layer"
DOMAIN["src/domain/<br/>Pure side-effect-free modules<br/>evidence, comparisons, application graphs"]
DOMAIN["src/domain/<br/>Pure side-effect-free modules<br/>format declarations, provider selection, Evidence, comparisons, application graphs"]
end
subgraph "Configuration"
@@ -71,8 +87,8 @@ graph TD
TESTS["tests/<br/>Vitest suite<br/>fixtures/ holds fake launcher, bridge, and CDP seams"]
end
subgraph "Evaluation"
EVALUATION["src/evaluation/<br/>Agent release evaluation<br/>session transcript scoring"]
subgraph "Development Evaluation"
EVALUATION["tests/evaluation/model-evals/<br/>Agent release evaluation<br/>session transcript scoring"]
end
DISPATCH --> CLI
@@ -84,6 +100,7 @@ graph TD
MAIN --> COMPOSITION
CLI --> COMPOSITION
SERVER --> COMPOSITION
COMPOSITION --> LIFETIMES
COMPOSITION --> SESSION
APP --> FIRMWARE
FIRMWARE --> PROCESS
@@ -101,6 +118,8 @@ graph TD
SERVER -->|narrow record ports| RECORDS
RECORDS --> DOMAIN
COMPOSITION --> WASM
COMPOSITION --> APKSIG
APKSIG --> DOMAIN
WASM --> PROCESS
WASM --> DOMAIN
COMPOSITION --> PWNTOOLS
@@ -109,6 +128,7 @@ graph TD
COMPOSITION --> HOPPER
COMPOSITION --> GHIDRA
COMPOSITION --> IDA
COMPOSITION --> KUNA
COMPOSITION --> AUX
IDA --> IDA_MCP
IDA --> CONTRACTS
@@ -131,14 +151,14 @@ graph TD
OBJDUMP --> DOMAIN
COMPOSITION --> RIZIN
RIZIN --> DOMAIN
CLI --> GDB
SERVER --> GDB
COMPOSITION --> GDB
GDB --> PROCESS
GDB --> DOMAIN
HOPPER --> HOPPER_BRIDGE
GHIDRA --> GHIDRA_BRIDGE
HOPPER --> PROCESS
GHIDRA --> PROCESS
KUNA --> PROCESS
GHIDRA --> PROVIDER_OWNERS
APP --> PROVIDER_OWNERS
PROVIDER_OWNERS --> PROCESS
@@ -156,6 +176,7 @@ graph TD
CONTRACTS --> DOMAIN
HOPPER --> DOMAIN
GHIDRA --> DOMAIN
KUNA --> DOMAIN
AUX --> DOMAIN
BROWSER --> DOMAIN
PROCESS --> DOMAIN
@@ -172,3 +193,22 @@ graph TD
APP -->|EVM interface port| EVM
EVM --> PROCESS
EVM --> DOMAIN
COMPOSITION --> KUNA
KUNA --> PROCESS
KUNA --> FILESYSTEM
KUNA --> DOMAIN
COMPOSITION --> ANDROID_TOOLS
ANDROID_TOOLS --> PROCESS
ANDROID_TOOLS --> DOMAIN
COMPOSITION --> CAPTURE_TOOLS
CAPTURE_TOOLS --> PROCESS
CAPTURE_TOOLS --> DOMAIN
COMPOSITION --> NATIVE_READERS
NATIVE_READERS --> DOMAIN
COMPOSITION --> FORMAT_READERS
FORMAT_READERS --> DOMAIN
COMPOSITION --> RUNTIME
RUNTIME --> DOMAIN
PROCESS --> FILESYSTEM
AUX --> FILESYSTEM
APP --> FILESYSTEM
+118
View File
@@ -0,0 +1,118 @@
<svg xmlns="http://www.w3.org/2000/svg" width="960" height="263" viewBox="0 0 960 263" role="img" aria-labelledby="title desc">
<title id="title">REA showcases</title>
<desc id="desc">Three illustrations. A brick-breaker screen with a speaker on each side stands for the DX-Ball sound-pan showcase. A browser window joined by a wire to a clipboard stands for the Notion clipboard-bridge showcase. A ring of 24 dots with one orange dot around a dark centre stands for the TH04 bullet-ring showcase.</desc>
<rect width="960" height="263" fill="#11111b"/>
<rect x="43" y="16.5" width="262" height="235.5" fill="#181825"/>
<rect x="354" y="14.5" width="258" height="237.5" fill="#181825"/>
<rect x="664" y="16.5" width="260" height="235.5" fill="#181825"/>
<g id="dx-ball">
<rect x="108.5" y="54" width="140" height="160" fill="#000000" fill-opacity="0.45"/>
<rect x="106.85" y="52.1" width="136" height="157" fill="#181825" stroke="#cdd6f4" stroke-width="3.7"/>
<rect x="110.9" y="55.9" width="128.2" height="149.6" fill="none" stroke="#6c7086" stroke-width="2.2"/>
<rect x="112.6" y="58.5" width="28.4" height="14.3" fill="#000000" fill-opacity="0.45"/>
<rect x="145.6" y="58.5" width="28.4" height="14.3" fill="#000000" fill-opacity="0.45"/>
<rect x="178.6" y="58.5" width="28.4" height="14.3" fill="#000000" fill-opacity="0.45"/>
<rect x="211.6" y="58.5" width="28.4" height="14.3" fill="#000000" fill-opacity="0.45"/>
<rect x="112.6" y="77.39999999999999" width="28.4" height="14.3" fill="#000000" fill-opacity="0.45"/>
<rect x="145.6" y="77.39999999999999" width="28.4" height="14.3" fill="#000000" fill-opacity="0.45"/>
<rect x="178.6" y="77.39999999999999" width="28.4" height="14.3" fill="#000000" fill-opacity="0.45"/>
<rect x="211.6" y="77.39999999999999" width="28.4" height="14.3" fill="#000000" fill-opacity="0.45"/>
<rect x="111.8" y="57.900000000000006" width="26.2" height="11.9" fill="#45475a" stroke="#cdd6f4" stroke-width="2.4"/>
<rect x="144.79999999999998" y="57.900000000000006" width="26.2" height="11.9" fill="#45475a" stroke="#cdd6f4" stroke-width="2.4"/>
<rect x="177.79999999999998" y="57.900000000000006" width="26.2" height="11.9" fill="#45475a" stroke="#cdd6f4" stroke-width="2.4"/>
<rect x="210.79999999999998" y="57.900000000000006" width="26.2" height="11.9" fill="#45475a" stroke="#cdd6f4" stroke-width="2.4"/>
<rect x="111.8" y="75.0" width="26.2" height="11.9" fill="#45475a" stroke="#cdd6f4" stroke-width="2.4"/>
<rect x="144.79999999999998" y="75.0" width="26.2" height="11.9" fill="#45475a" stroke="#cdd6f4" stroke-width="2.4"/>
<rect x="177.79999999999998" y="75.0" width="26.2" height="11.9" fill="#45475a" stroke="#cdd6f4" stroke-width="2.4"/>
<rect x="210.79999999999998" y="75.0" width="26.2" height="11.9" fill="#45475a" stroke="#cdd6f4" stroke-width="2.4"/>
<ellipse cx="173.4" cy="157.2" rx="8.4" ry="8.2" fill="#000000" fill-opacity="0.45"/>
<ellipse cx="171.9" cy="154.5" rx="7.3" ry="7.2" fill="#585b70" stroke="#cdd6f4" stroke-width="2"/>
<rect x="165.5" y="149.5" width="3.2" height="2.6" fill="#a6adc8"/>
<rect x="145.5" y="195.2" width="57.6" height="12.4" fill="#585b70" stroke="#cdd6f4" stroke-width="2.2"/>
<rect x="144.5" y="204" width="59.3" height="6.5" fill="#cdd6f4"/>
<g transform="translate(0 0)">
<path d="M61.5 117.5H69.6L78 108.5V141.2L69.6 132.8H61.5Z" fill="#585b70" stroke="#cdd6f4" stroke-width="2.3" stroke-linejoin="miter"/>
<path d="M78.1 106V143" stroke="#cdd6f4" stroke-width="3.4" fill="none"/>
<g fill="none" stroke="#89b4fa" stroke-width="2.2" stroke-linecap="butt" stroke-dasharray="2.6 1.4">
<path d="M84.8 118.2Q87.6 125 84.8 131.8"/>
<path d="M90.2 112.4Q94.5 125 90.2 137.6"/>
<path d="M95.8 108.2Q101.5 125 95.8 141.8"/>
</g>
</g>
<g transform="translate(190 0)">
<path d="M61.5 117.5H69.6L78 108.5V141.2L69.6 132.8H61.5Z" fill="#585b70" stroke="#cdd6f4" stroke-width="2.3" stroke-linejoin="miter"/>
<path d="M78.1 106V143" stroke="#cdd6f4" stroke-width="3.4" fill="none"/>
<g fill="none" stroke="#89b4fa" stroke-width="2.2" stroke-linecap="butt" stroke-dasharray="2.6 1.4">
<path d="M84.8 118.2Q87.6 125 84.8 131.8"/>
<path d="M90.2 112.4Q94.5 125 90.2 137.6"/>
<path d="M95.8 108.2Q101.5 125 95.8 141.8"/>
</g>
</g>
</g>
<g id="clipboard-bridge">
<rect x="390.3" y="83.6" width="100.6" height="82.6" fill="#000000" fill-opacity="0.45"/>
<rect x="387.7" y="81.2" width="97.8" height="79.7" fill="#181825" stroke="#cdd6f4" stroke-width="2.9"/>
<rect x="389.2" y="82.7" width="94.9" height="13" fill="#45475a"/>
<path d="M387.7 97H485.5" stroke="#cdd6f4" stroke-width="3.1"/>
<rect x="394.7" y="86.6" width="4.6" height="5" fill="#fab387"/>
<rect x="402.7" y="86.6" width="4.6" height="5" fill="#f9e2af"/>
<rect x="410.8" y="86.6" width="4.6" height="5" fill="#a6adc8"/>
<rect x="398" y="107.4" width="10.6" height="9.4" fill="#7f849c"/>
<rect x="412.4" y="107.9" width="16.8" height="6" fill="#7f849c"/>
<rect x="434" y="107.9" width="42.4" height="6.2" fill="#7f849c"/>
<rect x="412.2" y="117.3" width="64" height="2.6" fill="#6c7086"/>
<rect x="412.2" y="124.9" width="55.3" height="3.6" fill="#6c7086"/>
<rect x="412.2" y="133.2" width="50.3" height="3.8" fill="#6c7086"/>
<rect x="412.2" y="142" width="13.2" height="3.4" fill="#6c7086"/>
<rect x="430" y="142" width="14.4" height="3.4" fill="#6c7086"/>
<rect x="449" y="142" width="27.6" height="3.2" fill="#313244"/>
<rect x="412.2" y="150" width="12.6" height="2.9" fill="#6c7086"/>
<path d="M455.2 162V187H476M497 187H508.2V163.2H523" fill="none" stroke="#89b4fa" stroke-width="3" stroke-linejoin="miter"/>
<rect x="477.5" y="178.3" width="23" height="21" fill="#000000" fill-opacity="0.45"/>
<path d="M484.7 173.2V177" stroke="#cdd6f4" stroke-width="2.2"/>
<rect x="476.4" y="177.5" width="20.2" height="18.1" fill="#585b70" stroke="#cdd6f4" stroke-width="2.2"/>
<rect x="481.4" y="178.6" width="4.6" height="16" fill="#313244"/>
<rect x="477.2" y="191" width="4.6" height="4.4" fill="#f9e2af"/>
<rect x="481.6" y="183.8" width="3.4" height="3" fill="#fab387"/>
<rect x="525.4" y="114.6" width="60.6" height="75" fill="#000000" fill-opacity="0.45"/>
<rect x="524.7" y="112.5" width="56.4" height="69.4" fill="#45475a" stroke="#cdd6f4" stroke-width="3"/>
<rect x="531.6" y="118.6" width="42.3" height="53.4" fill="#1e1e2e"/>
<rect x="531.6" y="172" width="42.3" height="8.4" fill="#45475a"/>
<path d="M531.6 175.8H575" stroke="#cdd6f4" stroke-width="1.8" stroke-opacity="0.8"/>
<path d="M547.4 103.3H556.6V106.2H559.6V109.2H564.9V120.3H540.1V109.2H545V106.2H547.4Z" fill="#585b70" stroke="#cdd6f4" stroke-width="2" stroke-linejoin="miter"/>
<rect x="549.4" y="108.4" width="2.8" height="1.9" fill="#181825"/>
<rect x="536.6" y="131.8" width="32.3" height="3.4" fill="#7f849c"/>
<rect x="536.6" y="138.8" width="32.3" height="3.4" fill="#7f849c"/>
<rect x="536.6" y="145.8" width="32.3" height="3.4" fill="#7f849c"/>
<rect x="536.6" y="152.8" width="26.8" height="3.4" fill="#7f849c"/>
<rect x="536.6" y="159.8" width="14.6" height="3.4" fill="#7f849c"/>
</g>
<g id="bullet-ring">
<circle cx="783" cy="74.5" r="3.8" fill="#89b4fa"/>
<circle cx="798" cy="72" r="3.8" fill="#89b4fa"/>
<circle cx="812.5" cy="74.5" r="3.8" fill="#89b4fa"/>
<circle cx="769.5" cy="81" r="3.8" fill="#89b4fa"/>
<circle cx="826.5" cy="81" r="3.8" fill="#89b4fa"/>
<circle cx="756.5" cy="90" r="3.8" fill="#89b4fa"/>
<circle cx="839" cy="90" r="3.8" fill="#89b4fa"/>
<circle cx="746.5" cy="103" r="3.8" fill="#89b4fa"/>
<circle cx="849" cy="103" r="3.8" fill="#89b4fa"/>
<circle cx="740.5" cy="117" r="3.8" fill="#89b4fa"/>
<circle cx="854.5" cy="117" r="3.8" fill="#89b4fa"/>
<circle cx="738" cy="134" r="3.8" fill="#89b4fa"/>
<circle cx="856.5" cy="133.5" r="3.8" fill="#89b4fa"/>
<circle cx="741" cy="149" r="3.8" fill="#89b4fa"/>
<circle cx="854" cy="149" r="3.8" fill="#89b4fa"/>
<circle cx="747.5" cy="163" r="3.8" fill="#89b4fa"/>
<circle cx="848" cy="163" r="3.8" fill="#89b4fa"/>
<circle cx="757.5" cy="175" r="3.8" fill="#89b4fa"/>
<circle cx="770" cy="184" r="3.8" fill="#89b4fa"/>
<circle cx="826.5" cy="184" r="3.8" fill="#89b4fa"/>
<circle cx="838.5" cy="175" r="3.8" fill="#89b4fa"/>
<circle cx="784" cy="191" r="3.8" fill="#89b4fa"/>
<circle cx="798" cy="192.5" r="3.8" fill="#89b4fa"/>
<circle cx="812" cy="190.5" r="3.8" fill="#89b4fa"/>
<circle cx="833.5" cy="109" r="4.1" fill="#fab387"/>
<circle cx="797.4" cy="133" r="10.9" fill="#cdd6f4"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

+118
View File
@@ -0,0 +1,118 @@
<svg xmlns="http://www.w3.org/2000/svg" width="960" height="263" viewBox="0 0 960 263" role="img" aria-labelledby="title desc">
<title id="title">REA showcases</title>
<desc id="desc">Three illustrations. A brick-breaker screen with a speaker on each side stands for the DX-Ball sound-pan showcase. A browser window joined by a wire to a clipboard stands for the Notion clipboard-bridge showcase. A ring of 24 dots with one orange dot around a dark centre stands for the TH04 bullet-ring showcase.</desc>
<rect width="960" height="263" fill="#fafbfb"/>
<rect x="43" y="16.5" width="262" height="235.5" fill="#f4f6f5"/>
<rect x="354" y="14.5" width="258" height="237.5" fill="#f4f6f5"/>
<rect x="664" y="16.5" width="260" height="235.5" fill="#f4f6f5"/>
<g id="dx-ball">
<rect x="108.5" y="54" width="140" height="160" fill="#000000" fill-opacity="0.27"/>
<rect x="106.85" y="52.1" width="136" height="157" fill="#f4f6f5" stroke="#191a1d" stroke-width="3.7"/>
<rect x="110.9" y="55.9" width="128.2" height="149.6" fill="none" stroke="#a5a6a9" stroke-width="2.2"/>
<rect x="112.6" y="58.5" width="28.4" height="14.3" fill="#000000" fill-opacity="0.27"/>
<rect x="145.6" y="58.5" width="28.4" height="14.3" fill="#000000" fill-opacity="0.27"/>
<rect x="178.6" y="58.5" width="28.4" height="14.3" fill="#000000" fill-opacity="0.27"/>
<rect x="211.6" y="58.5" width="28.4" height="14.3" fill="#000000" fill-opacity="0.27"/>
<rect x="112.6" y="77.39999999999999" width="28.4" height="14.3" fill="#000000" fill-opacity="0.27"/>
<rect x="145.6" y="77.39999999999999" width="28.4" height="14.3" fill="#000000" fill-opacity="0.27"/>
<rect x="178.6" y="77.39999999999999" width="28.4" height="14.3" fill="#000000" fill-opacity="0.27"/>
<rect x="211.6" y="77.39999999999999" width="28.4" height="14.3" fill="#000000" fill-opacity="0.27"/>
<rect x="111.8" y="57.900000000000006" width="26.2" height="11.9" fill="#5a636d" stroke="#191a1d" stroke-width="2.4"/>
<rect x="144.79999999999998" y="57.900000000000006" width="26.2" height="11.9" fill="#5a636d" stroke="#191a1d" stroke-width="2.4"/>
<rect x="177.79999999999998" y="57.900000000000006" width="26.2" height="11.9" fill="#5a636d" stroke="#191a1d" stroke-width="2.4"/>
<rect x="210.79999999999998" y="57.900000000000006" width="26.2" height="11.9" fill="#5a636d" stroke="#191a1d" stroke-width="2.4"/>
<rect x="111.8" y="75.0" width="26.2" height="11.9" fill="#5a636d" stroke="#191a1d" stroke-width="2.4"/>
<rect x="144.79999999999998" y="75.0" width="26.2" height="11.9" fill="#5a636d" stroke="#191a1d" stroke-width="2.4"/>
<rect x="177.79999999999998" y="75.0" width="26.2" height="11.9" fill="#5a636d" stroke="#191a1d" stroke-width="2.4"/>
<rect x="210.79999999999998" y="75.0" width="26.2" height="11.9" fill="#5a636d" stroke="#191a1d" stroke-width="2.4"/>
<ellipse cx="173.4" cy="157.2" rx="8.4" ry="8.2" fill="#000000" fill-opacity="0.27"/>
<ellipse cx="171.9" cy="154.5" rx="7.3" ry="7.2" fill="#4d5863" stroke="#191a1d" stroke-width="2"/>
<rect x="165.5" y="149.5" width="3.2" height="2.6" fill="#aeb1b4"/>
<rect x="145.5" y="195.2" width="57.6" height="12.4" fill="#525c65" stroke="#191a1d" stroke-width="2.2"/>
<rect x="144.5" y="204" width="59.3" height="6.5" fill="#191a1d"/>
<g transform="translate(0 0)">
<path d="M61.5 117.5H69.6L78 108.5V141.2L69.6 132.8H61.5Z" fill="#56616b" stroke="#191a1d" stroke-width="2.3" stroke-linejoin="miter"/>
<path d="M78.1 106V143" stroke="#191a1d" stroke-width="3.4" fill="none"/>
<g fill="none" stroke="#274b77" stroke-width="2.2" stroke-linecap="butt" stroke-dasharray="2.6 1.4">
<path d="M84.8 118.2Q87.6 125 84.8 131.8"/>
<path d="M90.2 112.4Q94.5 125 90.2 137.6"/>
<path d="M95.8 108.2Q101.5 125 95.8 141.8"/>
</g>
</g>
<g transform="translate(190 0)">
<path d="M61.5 117.5H69.6L78 108.5V141.2L69.6 132.8H61.5Z" fill="#56616b" stroke="#191a1d" stroke-width="2.3" stroke-linejoin="miter"/>
<path d="M78.1 106V143" stroke="#191a1d" stroke-width="3.4" fill="none"/>
<g fill="none" stroke="#274b77" stroke-width="2.2" stroke-linecap="butt" stroke-dasharray="2.6 1.4">
<path d="M84.8 118.2Q87.6 125 84.8 131.8"/>
<path d="M90.2 112.4Q94.5 125 90.2 137.6"/>
<path d="M95.8 108.2Q101.5 125 95.8 141.8"/>
</g>
</g>
</g>
<g id="clipboard-bridge">
<rect x="390.3" y="83.6" width="100.6" height="82.6" fill="#000000" fill-opacity="0.27"/>
<rect x="387.7" y="81.2" width="97.8" height="79.7" fill="#f4f6f5" stroke="#191a1d" stroke-width="2.9"/>
<rect x="389.2" y="82.7" width="94.9" height="13" fill="#5a636d"/>
<path d="M387.7 97H485.5" stroke="#191a1d" stroke-width="3.1"/>
<rect x="394.7" y="86.6" width="4.6" height="5" fill="#c3693b"/>
<rect x="402.7" y="86.6" width="4.6" height="5" fill="#be9a53"/>
<rect x="410.8" y="86.6" width="4.6" height="5" fill="#b3b3b4"/>
<rect x="398" y="107.4" width="10.6" height="9.4" fill="#666671"/>
<rect x="412.4" y="107.9" width="16.8" height="6" fill="#666c74"/>
<rect x="434" y="107.9" width="42.4" height="6.2" fill="#666c74"/>
<rect x="412.2" y="117.3" width="64" height="2.6" fill="#7a7c82"/>
<rect x="412.2" y="124.9" width="55.3" height="3.6" fill="#7a7c82"/>
<rect x="412.2" y="133.2" width="50.3" height="3.8" fill="#6d7279"/>
<rect x="412.2" y="142" width="13.2" height="3.4" fill="#6d7279"/>
<rect x="430" y="142" width="14.4" height="3.4" fill="#6d7279"/>
<rect x="449" y="142" width="27.6" height="3.2" fill="#e8e8e8"/>
<rect x="412.2" y="150" width="12.6" height="2.9" fill="#6d7279"/>
<path d="M455.2 162V187H476M497 187H508.2V163.2H523" fill="none" stroke="#274b77" stroke-width="3" stroke-linejoin="miter"/>
<rect x="477.5" y="178.3" width="23" height="21" fill="#000000" fill-opacity="0.27"/>
<path d="M484.7 173.2V177" stroke="#191a1d" stroke-width="2.2"/>
<rect x="476.4" y="177.5" width="20.2" height="18.1" fill="#525c65" stroke="#191a1d" stroke-width="2.2"/>
<rect x="481.4" y="178.6" width="4.6" height="16" fill="#323435"/>
<rect x="477.2" y="191" width="4.6" height="4.4" fill="#be9a53"/>
<rect x="481.6" y="183.8" width="3.4" height="3" fill="#c3693b"/>
<rect x="525.4" y="114.6" width="60.6" height="75" fill="#000000" fill-opacity="0.27"/>
<rect x="524.7" y="112.5" width="56.4" height="69.4" fill="#545e69" stroke="#191a1d" stroke-width="3"/>
<rect x="531.6" y="118.6" width="42.3" height="53.4" fill="#ededec"/>
<rect x="531.6" y="172" width="42.3" height="8.4" fill="#545e69"/>
<path d="M531.6 175.8H575" stroke="#191a1d" stroke-width="1.8" stroke-opacity="0.8"/>
<path d="M547.4 103.3H556.6V106.2H559.6V109.2H564.9V120.3H540.1V109.2H545V106.2H547.4Z" fill="#545e69" stroke="#191a1d" stroke-width="2" stroke-linejoin="miter"/>
<rect x="549.4" y="108.4" width="2.8" height="1.9" fill="#f4f6f5"/>
<rect x="536.6" y="131.8" width="32.3" height="3.4" fill="#706f74"/>
<rect x="536.6" y="138.8" width="32.3" height="3.4" fill="#706f74"/>
<rect x="536.6" y="145.8" width="32.3" height="3.4" fill="#706f74"/>
<rect x="536.6" y="152.8" width="26.8" height="3.4" fill="#706f74"/>
<rect x="536.6" y="159.8" width="14.6" height="3.4" fill="#706f74"/>
</g>
<g id="bullet-ring">
<circle cx="783" cy="74.5" r="3.8" fill="#274b77"/>
<circle cx="798" cy="72" r="3.8" fill="#274b77"/>
<circle cx="812.5" cy="74.5" r="3.8" fill="#274b77"/>
<circle cx="769.5" cy="81" r="3.8" fill="#274b77"/>
<circle cx="826.5" cy="81" r="3.8" fill="#274b77"/>
<circle cx="756.5" cy="90" r="3.8" fill="#274b77"/>
<circle cx="839" cy="90" r="3.8" fill="#274b77"/>
<circle cx="746.5" cy="103" r="3.8" fill="#274b77"/>
<circle cx="849" cy="103" r="3.8" fill="#274b77"/>
<circle cx="740.5" cy="117" r="3.8" fill="#274b77"/>
<circle cx="854.5" cy="117" r="3.8" fill="#274b77"/>
<circle cx="738" cy="134" r="3.8" fill="#274b77"/>
<circle cx="856.5" cy="133.5" r="3.8" fill="#274b77"/>
<circle cx="741" cy="149" r="3.8" fill="#274b77"/>
<circle cx="854" cy="149" r="3.8" fill="#274b77"/>
<circle cx="747.5" cy="163" r="3.8" fill="#274b77"/>
<circle cx="848" cy="163" r="3.8" fill="#274b77"/>
<circle cx="757.5" cy="175" r="3.8" fill="#274b77"/>
<circle cx="770" cy="184" r="3.8" fill="#274b77"/>
<circle cx="826.5" cy="184" r="3.8" fill="#274b77"/>
<circle cx="838.5" cy="175" r="3.8" fill="#274b77"/>
<circle cx="784" cy="191" r="3.8" fill="#274b77"/>
<circle cx="798" cy="192.5" r="3.8" fill="#274b77"/>
<circle cx="812" cy="190.5" r="3.8" fill="#274b77"/>
<circle cx="833.5" cy="109" r="4.1" fill="#c3693b"/>
<circle cx="797.4" cy="133" r="10.9" fill="#1c222a"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 8.7 KiB

+258 -3
View File
@@ -8,9 +8,14 @@ ordinary binary inspection use the same modular capability.
## Bring your own engine
Initial real verification covers **Linux x64, ELF64 x86-64 little-endian
EXEC/DYN/REL**. Provide an absolute Python executable whose environment already
contains unchanged pwntools 4.15.0, pyelftools 0.33 and Unicorn 2.1.2:
Real verification covers **Linux x64** hosts and **EXEC/DYN/REL** objects in
these profiles: ELF64 x86-64, ELF64 little-endian AArch64, ELF32 i386, ELF32
little-endian ARM and ELF32 little- or big-endian MIPS. Other machines, classes
and byte orders, including big-endian ARM, return `unsupported_target`. Other
hosts, including Linux arm64, return `capability_unavailable` naming the
detected host before any target is read. Provide an absolute
Python executable whose environment already contains unchanged pwntools 4.15.0,
pyelftools 0.33 and Unicorn 2.1.2:
```sh
REA_PWNTOOLS_PYTHON=/absolute/isolated-env/bin/python \
@@ -29,6 +34,16 @@ the selected object as a host process, or requests runtime library resolution. T
uses isolated mode and an owned cache. Exact upstream profiles are recorded in
[upstream provenance](https://github.com/morluto/rea/blob/main/third_party/pwntools/README.md).
The adapter skips pwntools kernel configuration and version discovery: those
memory-content scans supply none of this report's fields. Ordinary `IKCFG_ST`
data is not interpreted as a kernel-config request, and virtual zero-filled
LOAD tails are reported from their headers without materializing them for that
scan. GOT/PLT inference and the advertised static mitigation indicators still
use the pinned upstream implementations. Header-table file ranges are checked
before dependency seeks. Structural inventory and the convenience-map engine
still parse the snapshot separately; eliminating that remaining duplication
requires preserving the upstream GOT/PLT and mitigation facts.
## Interpreting results
- Artifact path, SHA-256 and size identify original selected bytes. All parsing
@@ -90,6 +105,12 @@ one section or symbol, or a stable page without re-running the decoder. CLI
one-shot invocations pass portable inline Evidence JSON; an MCP session uses
the retained `evidence_id`. Page `limit` is required and bounded by the
measured MCP stdio budget documented on the tool contract.
An empty retained section or symbol collection has no valid item index. Its page
view can still return an empty page.
Malformed authenticated layout input remains rejected with the owning schema's
safe field diagnostics under `source.normalized_result`, including the field
path and type or value reason. Evidence authentication and decoder validation
remain separate boundaries.
Over MCP, `inspect_binary_layout` with `"detail": "summary"` retains the
complete layout Evidence in the session and returns only its summary view.
@@ -170,6 +191,31 @@ 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.
A kernel or container OOM killer ends Ghidra with `SIGKILL` and prints no
`OutOfMemoryError`. When a process or protocol failure records
`exit_signal: "SIGKILL"` and no `stop_requested`, REA reports the signal
instead of generic `rea doctor` advice. The process supervisor records
`stop_requested: true` once REA begins stopping Ghidra, before it sends any
signal, so REA's own SIGKILL is never reported as an outside kill.
On Linux with cgroup v2, REA also reads its own cgroup's `memory.events` before
launch and again after the kill, locating the group through the cgroup2 mount
in `/proc/self/mountinfo`. `memory_cgroup` records the group path,
`oom_kill_before`, `oom_kill_after` and `memory_max`. If the count rose, REA
returns `resource_constraint` with `resource: "memory"` and `cgroup_memory_max`
in `reported_limits`. The count covers every process in REA's cgroup and its
child groups, which can include processes outside REA's process tree when the
group is shared, so a rise shows that the kernel OOM killer ended a process in
that cgroup or a child group, not which process it chose. `memory_max` is the
limit in bytes, `"max"` when the group sets none, or `null` when it could not be
read; a limit on a parent group is not read. The limit also covers Ghidra's
native decompiler processes, whose number follows the available CPUs, so the
advice does not recommend a larger Java heap. When the session had already
completed an operation, the advice keeps the instruction to reopen the target
with `open_binary`. Without a cgroup v2 memory group, or when the count did not
rise, the failure stays `execution_failure` and names the signal as a possible
out-of-memory kill.
## Portable PE resources
`inspect_pe_resources` / `inspect-pe-resources` inspects an explicit PE32 or
@@ -193,6 +239,14 @@ reported. Directory headers, entries, data entries and payloads retain original
file offsets and lengths. Each opaque payload has its own SHA-256; code page and
reserved data-entry fields are preserved.
Every `directories[]` row retains its identity `path` and the matching incoming
`entry_locations`: both are empty for the root, then contain the type and
optionally the name. Empty branches keep these identities even when they have
no payload leaves. Shared directory bytes produce a row for each incoming path,
with the original directory range and each path's own entry locations. These
rows are included in the metadata budget. A present empty root remains distinct
from an absent resource data directory (`directory: null`, `directories: []`).
RT_GROUP_ICON records expose original image descriptors and RT_ICON candidates.
A reference resolves only when an icon with the same numeric ID and language
exists. Missing references, other-language candidates and declared-size mismatch
@@ -215,3 +269,204 @@ 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. Complete delivery remains the default. For a small initial
MCP reply, select `detail: "summary"`; this retains the complete Evidence and
returns artifact identity, table coverage and row counts. Use its
`normalized_result.parent_evidence_id` with `inspect_analysis_view`:
```json
{
"source": {
"kind": "retained-evidence",
"evidence_id": "ev_<parent digest>"
},
"view": {
"kind": "native-exports",
"collection": "exports",
"names": ["selected_name"],
"fields": ["name"],
"offset": 0,
"limit": 32
}
}
```
Names match exactly and preserve every same-name version or alias. Optional
`indices` selects original row positions, intersecting a name filter when both
are supplied; empty filters select nothing. `source_index` always remains and,
with the parent ID, identifies the original export or issue row. Fields default
to the complete row; an empty field list returns only its identity. ELF `name`
selection also returns raw-byte and encoding qualifications. Fields from the
other format are rejected. `collection: "issues"` pages the complete diagnostic
records, with optional original indices. Offset and limit default to 0 and 64
and apply to matching rows in original order.
View `coverage` describes the selected page; `summary.coverage` or
`exports.coverage` preserves source-table coverage. An exhausted or empty page
does not make a partial source complete. Retained views never reopen the file.
The CLI uses the same projector and requires a durable bundle for a summary:
```sh
rea inspect-native-exports ./libexample.so --detail summary --evidence_output ./exports.bundle.json --json
# Pass the summary's parent ID and the same view as JSON to inspect-analysis-view,
# together with --evidence-bundle ./exports.bundle.json.
```
- 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`,
`binding` and `visibility` (`"default"` or `"protected"`; `"unknown"` in older
historical Evidence where visibility was not recorded). 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`. With section headers, a missing
`SHT_DYNSYM` section is also `unsupported` when a `PT_DYNAMIC` segment is
observed, including a directory containing only the inactive null section.
Enumerating dynamic symbols from that segment is not implemented. A present
section directory with neither a dynamic symbol table nor a dynamic segment
reports a complete empty inventory. 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.
+62
View File
@@ -0,0 +1,62 @@
# Binary hardening
Use `inspect_binary_hardening` to read compile-time and link-time hardening from
an explicit local ELF, PE, or Mach-O file. No disassembler, pwntools install, or
target execution is required. Universal Mach-O files return one image per slice.
```sh
rea inspect-binary-hardening /absolute/path/to/program --json
rea inspect-binary-hardening /absolute/path/to/program --max-file-bytes 1048576 --json
```
MCP accepts `{ "path": "/absolute/path/to/program" }`. CLI paths may be relative
to the current directory; MCP paths must be absolute.
Each image has one row per property: `property`, `state`, `confidence`, `basis`,
`location`, `note`, and `next_probe`. States are `enabled`, `disabled`,
`partial`, `unknown`, and `not_applicable`. `confidence` is `observed` for a
single header or import, and `derived` when the state combines several fields,
such as ELF PIE or full RELRO. An image's `architecture` is its CPU family, or
null when REA has no family for it. `machine` and `slice.architecture` keep the
exact identity, such as `arm64e`.
ELF rows cover PIE (`ET_DYN` with `PT_INTERP` or `DF_1_PIE`), NX (`PT_GNU_STACK`;
a missing header is `unknown` and the architecture default is only noted), RELRO
(`PT_GNU_RELRO` plus `BIND_NOW`), stack protector and FORTIFY undefined dynamic
imports, CET and AArch64 BTI/PAC GNU property notes, `DT_RPATH`, `DT_RUNPATH`, and
whether a `SHT_SYMTAB` section is present. Only an undefined dynamic import
counts. Imports come from the table `DT_SYMTAB` declares. A `SHT_DYNSYM` section
only supplies the symbol count when `DT_HASH` and `DT_GNU_HASH` leave it open,
and only if it names the same symbol and string tables. Without that count the
import rows are `unknown` and give the reason. A missing `__stack_chk_fail` or fortify import is `unknown`. That
absence does not establish that the protection was left out of a static or
stripped build. A present import is a sign that some code uses the protection,
not proof that all code does. NX, RELRO, `DT_RPATH`, and `DT_RUNPATH` are
`not_applicable` when `e_type` is neither `ET_EXEC` nor `ET_DYN`, and `unknown`
for a linked image with no readable program headers.
PE rows read `DllCharacteristics` for dynamic base, high-entropy VA, NX
compatibility, force integrity, no SEH, and guard CF. `cfg_instrumented` is the
load-config `GuardFlags` linker claim when `Size` covers that field. An
unreadable load configuration, including a TLS failure in that parser, leaves
only `cfg_instrumented` `unknown`. Authenticode and debug rows report certificate
and debug directory presence, not signature validity or debug content. A
directory whose address and size disagree is `unknown`.
Mach-O rows read `MH_PIE`, `MH_ALLOW_STACK_EXECUTION`, `MH_NO_HEAP_EXECUTION`,
an `__RESTRICT` segment, `LC_CODE_SIGNATURE` presence, encryption from the
existing encryption reader, the arm64e subtype, and a stack-protector import.
`hardened_runtime` is `unknown` with `next_probe` `inspect_signature`.
CodeDirectory flags stay with that tool.
The report states presence of build-time features. It does not state
exploitability. `inspect_binary_layout` is unchanged.
The default read budget is 64 MiB (`max_file_bytes`, ceiling 512 MiB). A changed
file is refused. A malformed header, a Mach-O segment or encryption command that fails admission, or a program, section,
or segment payload declared outside the file returns no partial report, except that one bad slice in a universal Mach-O leaves the other slices. A symbol table or
symbol name over its budget leaves the import rows `unknown` and keeps the
header rows. A GNU property note over its budget, or a compressed note section
that may hold one, leaves the CET and BTI/PAC rows `unknown` and keeps the header
rows. A dynamic-table budget still fails the ELF report. The image is never
executed.
+299 -49
View File
@@ -12,17 +12,51 @@ For request/response bodies and traffic debugging, see
[Fiddler Everywhere captures and MCP](web-network-captures.md#reading-fiddler-captures-saz).
Inspect native SAZ or HAR 1.2 offline with REA, or connect Everywhere's separate MCP server.
HTTP discovery and browser WebSocket inputs require an explicit port. An
explicit default `:80` remains valid and is retained in the canonical endpoint;
omitting the port remains invalid. HTTP `/json/list` discovery preserves usable
siblings when optional metadata is malformed: attachment becomes unknown and
row/field diagnostics appear in `limitations`. Target identity and socket
authority are validated independently before selection and connection.
## Shipped surfaces
- `list_browser_targets` / `rea list-browser-targets` discovers all eligible page targets at the endpoint by default, with an optional exact-origin filter.
- `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 observed in-scope registrations and their 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. An omitted optional `inputSchema` is a valid parameterless registration, represented by null schema and digest. 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
parent/child structure independently. Node value lengths are comparable only
when both nonempty snapshots declare the same `node_value_length_unit`.
Missing or different units leave length comparison unknown; independent node
or frame structural differences can still prove change. Empty inventories have
no value lengths to compare. Frame IDs and sibling enumeration order
are capture-local and do not establish a change; duplicate IDs, missing parents,
and cyclic ancestry remain unknown. A complete frame difference can still prove
change when DOM nodes are incomplete, and vice versa. Partial DOM or metadata
@@ -31,6 +65,57 @@ 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.
Page network and console coverage starts independently at the first observed
event from that domain or its successful enable acknowledgment. The capture's
lifecycle start is earlier and does not establish domain readiness. Events
received before the enable reply remain part of the observation.
Worker output is a final target-inventory snapshot. `attached` describes any
debugging client, not an REA-owned observing session. The selected page-session
collector does not collect worker-session requests, console events, or scripts,
and workers that disappeared before the snapshot may be missing; worker
coverage is therefore attach-limited. Missing ownership metadata is reported as
`unattributed_target` with unavailable worker attribution, separately
from a proven foreign owner excluded as `out_of_target_scope`. A shared origin
alone establishes neither relationship.
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.
IndexedDB schema fingerprints with `projection_version: 2` include the observed
database version and object-store definitions. A missing, malformed, or imprecise
version leaves the schema fingerprint incomplete while retaining observed store
facts. Historical fingerprints without this marker covered object stores only:
matching historical hashes cannot establish unchanged database versions, and a
historical hash is not compared with a version-bearing hash. Recapture both
sides for a complete IndexedDB version comparison; other proven storage changes
remain reportable.
Script comparison groups observations by reported URL and compares unordered
content samples. Producer inventory keys include optional metadata, so a changed
key cannot by itself prove script removal or addition. Missing module, length,
language or source-hash facts remain unknown; shared known values and complete
retained source digests can still prove a group change. Partial inventories use
the same omission rule as repeated HTTP samples. WebMCP dropped-tool counts also
prevent equality, and two incomplete owner inventories cannot prove tool
correspondence merely from their different retained registrations.
- `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.
@@ -74,6 +159,11 @@ 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.
Passive page and Inspector capture enable the `Debugger` domain. A target's
`debugger` statement can then pause execution; these observation paths do not
suppress pauses with `Debugger.setSkipAllPauses` or resume the target. Their
lack of explicit pause commands does not guarantee uninterrupted execution.
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.
@@ -109,14 +199,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
@@ -168,7 +255,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
@@ -245,7 +334,11 @@ read as registration state only after domain availability is confirmed.
Discovery is attach-only: the command needs a reachable loopback CDP endpoint
and a target ID from `list_browser_targets`, and it never navigates, reloads,
or evaluates the page. Observe the page in the state you want to measure;
registered tools reflect the document as loaded.
registered tools reflect what the protocol delivered. If an in-scope subframe
already exists when discovery attaches, `webmcp_tools` remains attach-limited:
Chromium replay may omit its older registrations. Later live registrations
retain their frame ownership but do not establish recovery of those missing
descriptors. REA does not reload, evaluate, or invoke tools to fill that gap.
Accessibility names/descriptions, console primitive text, JSON body shapes, WebSocket shapes, script content, and storage key names are selected independently:
@@ -287,30 +380,102 @@ are reported in the result.
Call `list_browser_targets` first because target IDs are browser-instance-specific. REA rechecks the selected target's current type and origin immediately before attaching. Without an origin filter, discovery lists all eligible pages; an individual capture defaults to the selected page's current origin.
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`.
An observed navigation also takes precedence over failures from stale
document reads, such as a script ID invalidated by reload; cancellation remains
cancellation. Retry after navigation settles, or refresh target discovery if
the selected page was replaced. Origin-scope refusals from page inspection,
runtime observation, screenshots, and WebMCP discovery explain the permitted
HTTP(S) origin scope and direct the caller to select a permitted document or
explicitly revise `allowed_origins`. Page capture also preserves an observed
main-frame scope violation when a later document read fails. An unresolved
initial main frame does not establish an origin violation; its bounded startup
retries retain a distinct refusal.
Screenshot capture requires a valid main-frame identity. Malformed identity
produces a protocol failure; a readable child frame cannot substitute for the
main frame.
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. A redirect row's optional
`redirect_response` contains the prior response `url`, CDP-reported `status`,
and `scope`. The URL has transport credentials removed; URL and status are
withheld unless its origin is approved. Missing or malformed response URLs have
null scope and add `invalid_protocol_value` timeline coverage; a valid URL with an
unsupported protocol has `unsupported` scope. Outside-policy and unsupported
responses mark the timeline as policy-filtered with `disallowed_origin` or
`unsupported_url`. Missing status remains null rather than zero. A supplied
non-numeric status is also null and records `invalid_protocol_value` coverage;
the other valid response facts remain available. Historical rows may omit this
field; omission does not establish that no prior response existed. Response
headers and bodies are discarded. The row timestamp belongs to the next request,
not an exact prior response time. 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. Session
`load_failed` rows require a previously observed main-frame `Document` request;
subresources, child-frame requests and failures without admitted request ownership
are excluded. Redirects reuse their request identity, and completed/failed requests
release it. Active request identity metadata uses the existing 8 MiB retained-event
allowance; exhaustion fails the observation explicitly instead of guessing scope.
## Data minimization
The Link projection parses repeated CDP header field lines independently and
retains their declarations in order, including derived untrusted agent hints.
Commas inside URI targets or quoted parameters remain part of those values.
Response metadata parses repeated CDP header field lines independently. The
Link projection retains their declarations in order. `csp.directives` lists the
directives of every CSP policy in order, across field lines and comma-separated
policies, without recording policy boundaries, so a directive name can repeat
across policies. As in CSP3, a repeated name within one policy is ignored.
Single-valued policy fields combine their lines as one comma-separated value,
as RFC 9110 specifies. 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`.
Console entries, WebSocket frame summaries, and selected request/response JSON
shape outputs share one 8 MiB retained-observation allowance per capture. Shape
inference admits expanded path bytes before allocating those paths, including
escaped and Unicode property names. Replacing or removing a request's shapes
refunds their retained charges. A refused body shape preserves network metadata
and any captured peer shape with `resource_budget_exhausted` coverage; a refused
WebSocket frame is counted as dropped rather than reclassified as non-JSON text.
The allowance has no runtime override. Protocol messages and JSON input parsing
remain subject to their separate transport admission; this output allowance does
not bound the whole process heap.
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`.
- DOM snapshots retain node types, node names, value lengths, and attribute names, but not text or attribute values. `dom.node_value_length_unit` is `utf16-code-units` for web observations and applies to every node's `node_value_length`; it counts JavaScript string code units, not Unicode code points or UTF-8 bytes. Electron observations declare `utf8-bytes` instead. Historical captures without the unit leave it unknown; reading them does not infer a unit or convert their stored lengths.
- 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. Base64 response bytes must be valid UTF-8 before JSON interpretation; invalid bytes leave the response shape unavailable and preserve any observed request shape and network metadata. Producer-decoded strings and genuine U+FFFD characters retain their reported meaning. 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,
@@ -325,14 +490,18 @@ 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.
- 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`. Source locations are CDP's zero-based lines and UTF-16 columns. A negative column, which Chrome reports on the first line of some inline scripts, is kept as reported and does not locate a column in the source URL; an invalid line or a non-integer column is `null` (WebMCP `registration_source` is `null`) and 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.
@@ -341,6 +510,11 @@ Frames, resources, scripts, events, and workers outside the selected origin scop
Retained workers include validated opener-target and parent-frame IDs when CDP
provides them; these relationships do not expand the exact-origin boundary or
prove static module ownership.
Workers without either relationship are withheld with an `unattributed_target`
exclusion and `workers` in `unavailable_sections`: a same-origin URL does not
establish which page owns a worker. A supplied relationship to another target or
frame remains `out_of_target_scope`. Missing ownership also remains unknown when
the worker URL is empty; withheld target details are not returned.
## Completeness and limits
@@ -355,8 +529,17 @@ 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 a 20-second deadline across the capture. An oversized source is
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
@@ -364,19 +547,25 @@ 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. `max_heap_mb` defaults to 1024 MiB V8
old-space; `analysis_timeout_ms` defaults to 30000 per script, including startup
and transfer. A failed or timed-out script becomes an explicit unknown while
completed findings and later scripts remain available. `total_analysis_timeout_ms`
defaults to 300000 across script analysis; expiry returns a typed timeout with
partial Evidence and identifies unstarted scripts. These controls are also
available as CLI flags with hyphenated names. Capture and optional source-map
fetching have separate budgets.
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.
Exhausted result capacity stops further script launches and returns a typed
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
@@ -386,8 +575,10 @@ 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. These bounds do not establish an aggregate host-memory limit
across other REA operations or external processes.
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
@@ -400,13 +591,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
@@ -415,22 +624,63 @@ 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.
failed fetches retain the last response URL observed within the approved origins;
`effective_url` is `null` when no response was observed. query parameters are
A fully read response that fails UTF-8, JSON, mapping, or structural validation
retains `artifact: null`, empty original-source, module-edge, and mapping
arrays, and an explicit failure limitation. Invalid content is `invalid`;
decoder or structural capacity refusal is `fetch_failed`. Its optional
`response_identity` records the complete body's SHA-256 digest, byte count,
and `fetch-response-body-bytes` representation without retaining malformed
text or charging that text to the 32 MiB output budget. These are bytes
delivered by Fetch after HTTP content decoding and before UTF-8 decoding, not
HTTP wire bytes. The identity is absent when the body was not fully read, and
older observations may omit it. A response whose text cannot fit the existing
output budget is likewise `fetch_failed` with text omitted and completed-body
identity; JSON parsing and source-map decoding are not attempted. Successful and partial maps keep their
full text artifacts. Their original text, including an initial BOM or XSSI
line, remains distinct from the parsing view that removes that framing.
Collection reads the body once and does not raise its response or output limits.
When fetching is selected, source-map `excluded` facts preserve the capture
ledger's reasons and counts for declarations rejected before download. No
disallowed URL details are retained or fetched. `requested` and `processed`
count the admitted fetch requests; exclusions are counted separately. Excluded
declarations make the collection unavailable, or partial when maps were retained.
An empty collection with no declarations remains `not_requested`.
If the caller cancels while later source maps are being fetched, the failure's
partial Evidence retains the captured page and every completed map item. Its
`normalized_result.kind` is `web-bundle-acquisition` and
`analysis_status` is `not_performed`: no script-analysis result is claimed.
`source_maps.requested` remains the admitted request count,
`source_maps.processed` counts completed items, and any unprocessed requests
have an explicit unknown-results limitation. Capture exclusions remain in the
same collection. The cancellation error keeps the caller's exact reason;
an already observed response resource or cleanup failure keeps its own error
category and cleanup owner.
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.
Failed fetches retain the last response URL observed within the approved origins;
`effective_url` is `null` when no response was observed. Query parameters are
preserved, and both URLs count toward the inline evidence budget.
Current fetches emit a string or null; historical observations may omit
`effective_url` without changing their stored content or Evidence identity.
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
The browser provider deliberately does not expose generic CDP commands, `Runtime.evaluate`, WebMCP invocation, navigation, input, downloads, page closure, or browser closure. Screenshot capture is the sole pixel surface and returns an image only when requested. REA disables the domains it enabled, detaches the target session, and closes only its own WebSocket.
Discovery and attachment errors remain primary if closing REA's socket also
fails. The cleanup failure is reported alongside the original command failure;
it cannot replace the operation diagnostic.
This feature is not a browser sandbox or network containment mechanism. The attached page and browser continue running with their existing privileges and may make external requests independently of REA. CDP gives deep access to the selected browser profile, so use a dedicated profile, include only origins you intend to inspect, and treat other same-user processes as outside this boundary.
+455 -15
View File
@@ -9,23 +9,74 @@ liveness deadlines. `INPUT_JSON` may be inline JSON or a JSON file path.
The minimal request contains only browser launch/connect selection, `start_url`,
and at least one `actions` entry. Launch `headless` defaults to `true` and can
be set to `false` where the host supports a visible browser. Navigations follow
the destinations named by the scenario and the page's ordinary browser requests;
there is no separate origin allowlist or request-interception layer. Environment
be set to `false` where the host supports a visible browser. Launched Chromium
keeps its sandbox; on hosts that cannot provide one, such as root in a
container, set `REA_BROWSER_NO_SANDBOX=true` for the REA process. Termux on
Android always launches without it. Navigations follow
the destinations named by the scenario and the page's ordinary browser requests,
and [request steps](#request-steps) send only to the start URL's origin;
there is no separate origin allowlist. Request steps use a temporary dispatch
gate for cancellation; ordinary page requests retain their browser behavior. Environment
settings (including service workers blocked by default), empty storage, and a
final sanitized URL capture are supplied by default. Set
`environment.service_workers` to `allow` when the application requires them.
The connected service-worker policy owner retains its exact CDP transport
through discovery, attachment and restoration. Failed admission preserves its
primary diagnostic when socket cleanup also fails; later policy cleanup retries
that retained transport and clears cleanup uncertainty only after close succeeds.
For attached browsers, `block` rejects captures when the selected page or its
frames are controlled by a service worker or already have a registration,
including after navigation. Existing caller registrations are never removed.
including after navigation. Policy checks bracket each artifact capture, including
the final step. If an external worker takes control while artifacts are being
captured, REA rejects the capture and retains the completed artifacts in
`details.diagnostics.collected_artifacts`; they do not establish policy-compliant
completion. Existing caller registrations are never removed.
Navigation can run destination code before a newly discovered worker is rejected;
that rejection does not roll back browser effects. REA removes its owned
future-document scripts, restores live frame methods, then disconnects.
Add secret declarations, storage seeds, and additional artifact or event
capture only when needed. Action, secret, and storage counts are not capped.
Duration, action, and navigation timeouts remain fixed provider-owned liveness
limits. `capture.limits` controls retained event and network-content budgets;
it does not change those liveness deadlines.
capture only when needed. Missing environment-backed secrets fail as input errors before browser acquisition, naming the declaration and required variable. An explicitly set empty string is a resolved value. Action, secret, and storage counts are not capped.
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. It also bounds the initial
`start_url` navigation through the load event; a redirect or replacement
navigation cannot leave startup waiting indefinitely. 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
action 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.
## Validate a scenario before running it
`validate-browser-scenario INPUT_JSON` checks one scenario against the same
`browserScenarioSchema` refinement that `capture_browser_scenario` admits, and
stops there. `INPUT_JSON` may be inline JSON, a JSON file path, or `-` for
standard input.
```bash
rea validate-browser-scenario ./browser-scenario.json --json
```
A valid scenario reports `{"valid":true,"actions":<count>,"secrets":<count>}`
and exits 0. An invalid one exits 1 and reports the same field paths capture
would report, so a correction made here is the correction capture needs:
duplicate or reserved step IDs, duplicate secret identities and
environment-variable names, undeclared secret references, request-step
violations, and unknown fields.
Validation performs no browser discovery, launch, attachment, or navigation and
constructs no provider. It does not read secret environment variables, so a
scenario whose declared variables are unset still validates; the missing-value
failure belongs to capture. A schema-valid scenario does not prove that the
executable or target is available on the host.
## Minimal launch example
@@ -149,6 +200,12 @@ 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.
CDP connections leave Playwright's default-context overrides disabled. The
selected page receives the declared scenario environment and temporary focus
emulation, so actions can complete while that page is in the background.
Its environment owner clears these overrides before disconnecting; other pages
keep their media settings. Connecting does not create a bootstrap page.
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,
@@ -182,7 +239,9 @@ 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
strings or declared secret references. Raw URL userinfo credentials are rejected.
strings or declared secret references; request step headers can also read a
cookie or local storage value from the page, and request steps and `goto`
destinations can send [saved values](#saved-values). Raw URL userinfo credentials are rejected.
Captured credential-header
values are not retained, and declared secret values are redacted automatically.
A secret may be declared solely to
@@ -199,8 +258,9 @@ validation.
Cookies are installed before the initial navigation. Local storage entries are
seeded once per browser storage partition; session storage entries are seeded
once per origin in each top-level browsing context. Seeding occurs before the
first document scripts for that area. An origin first reached by a later action
once per origin in each instrumented top-level browsing context. Seeding occurs
before the first document scripts for the selected page and its instrumented
frames. An origin first reached by a later action
receives its seed on that first visit. Application updates, deletions, and
empty values then persist across later navigations in the same area; a new
scenario starts with its own seeds. Partitioned frames can receive separate
@@ -208,11 +268,24 @@ local storage seeds even when they share an origin. The pre-script mechanism
uses CDP debugger instrumentation. The observer resumes only pauses whose top
frame belongs to its private seed script; page breakpoints and `debugger`
statements remain under the attached debugger client's control.
If Chromium leaves the private isolated world's origin empty, REA looks up
that exact frame's reported security origin before selecting a seed. It does
not infer the origin from a frame URL or serialized storage key. Unresolved
frame identity or security origin fails initialization rather than silently
running with an unknown initial state.
Popup discovery does not establish that its first script was paused by the
selected page's storage instrumentation. When non-cookie seeds are declared
and a popup is discovered, the capture retains completed observations and
marks `storage` coverage missing, with equality ineligible. This applies even
if popup lifecycle events were not selected. Popup first-script storage
initialization is not currently guaranteed.
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.
@@ -279,6 +352,263 @@ external browser without closing it. The request itself defines the operation;
there is no additional REA grant step. The browser and host still enforce their
own access and process rules.
## Request steps
A `request` step sends one HTTP request from the page with the page's own
`fetch`, so the browser attaches the session's cookies and the request is
recorded in the step's network events like any request the application makes.
Use it to record an application's API, including writes, when driving the UI
for each call would be slower or less exact than calling the endpoint the UI
calls. It is the only action that changes application state without a user
gesture, so run scenarios with write requests only against an instance whose
data you may change. This scenario creates one item:
```json
{
"browser": {
"mode": "launch",
"executable_path": "/absolute/path/to/chromium"
},
"start_url": { "url": "http://127.0.0.1:3000" },
"actions": [
{
"step_id": "create-item",
"action": "request",
"method": "POST",
"path": "/api/items",
"query": [
{ "name": "notify", "value": { "source": "literal", "value": "false" } }
],
"headers": [
{
"name": "X-CSRFToken",
"value": { "source": "cookie", "name": "csrftoken" }
},
{
"name": "Authorization",
"value": {
"source": "local_storage",
"key": "auth",
"json_pointer": "/token"
},
"prefix": "Bearer "
}
],
"json": { "title": "Scratch item 1" },
"expected_status": [201],
"timeout_ms": 10000
}
]
}
```
- `method` is `GET`, `HEAD`, `POST`, `PUT`, `PATCH` or `DELETE`. `json` is the
body, sent with `Content-Type: application/json` unless a header sets one; a
`GET` or `HEAD` step cannot carry it.
- `path` starts with one `/` and must resolve to the start URL's origin, which
validation checks before the browser starts. When the step runs, the page must
be on that origin, checked again in the page as the request is sent; add a
`goto` step first if an earlier action left it. A query in `path` is sent as
written (a fragment is not sent), and `query` entries are appended after it
in order and may reference declared secrets.
- Request steps follow no redirects: a redirect answer fails the step, and
nothing is sent to the location it names, though a write the redirect
answered may already have been applied. `expected_status` cannot name a 3xx
status. Manual redirects can be opaque to the page and may not produce a
response event; their status and `Location` can therefore be unavailable even
when network events and header values were selected. Such captures report
incomplete event coverage.
- Header values are literals, declared secrets, a cookie the browser holds for
the request URL (`{"source": "cookie", "name"}`, HttpOnly cookies included,
whose value the page's scripts can then see while the request runs), or a
local storage entry on the start origin (`{"source": "local_storage",
"key"}`, with an optional `json_pointer` to a string or number in a stored
JSON value). `prefix` is sent before the value. A value read from the page is
redacted like a declared secret, as `[REDACTED:page:<step_id>.<header>]`,
from records made after the step reads it; a response recorded earlier, such
as the login response that returned a token, keeps the value. A value read
again keeps its first marker, and a value shorter than 8 characters is not
redacted, since redacting it would rewrite ids, ports and hosts throughout
the capture. A value is redacted as stored and as `fetch` sends it, without
surrounding spaces and tabs. A missing cookie, key or pointer fails the step.
Reading a page value raises the estimated cost of redacting every later
body, so the bodies kept within `max_network_body_allocation_bytes` (1 MiB
by default) get smaller: after a 32-character `X-CSRFToken` value, a body over
about 47 KiB is kept as `unavailable`. Raise `capture.limits` when the
application's responses are larger.
- A header can send Basic credentials, `{"source": "basic_credentials", "parts":
[username, password]}`: the base64 of `username:password`, after a `prefix`
such as `"Basic "` or with none, as some APIs take encoded credentials in
their own header. Each part is a literal, a declared secret, a saved value, a
cookie or a local storage entry, so a header a frontend builds from a login
token can be sent too; the password is never a literal other than empty, since
Evidence records literals as written, and a username with a colon fails the
step, since Basic authentication ends the username at its first colon. The
encoded value is redacted however short, as
`[REDACTED:page:<step_id>.<header>]`, and parts read from the page are
redacted as page values are.
- Validation refuses a header name given twice in a step, header names a
page's `fetch` cannot set, such as `Cookie`, `Origin`, `Host` or `Sec-*`, and
`User-Agent`, which Chrome drops; it refuses line breaks and other ASCII
control characters in literal values and prefixes, and the step fails before sending
when a secret or page value holds them.
- The step completes when the whole response has been read. With
`expected_status`, any other status fails the step and the later actions are
recorded as `cancelled`; without it, any response completes the step. A
network error fails the step. `timeout_ms` covers reading the header values
and the request: a step whose time runs out, or that is cancelled, before
its request is sent sends nothing, but a request already sent can still reach
the application after the step fails.
An absolute deadline also covers browser evaluation queued behind page work.
A temporary route checks cancellation again at network dispatch and is removed
when the request evaluation settles. A page controlled by a service worker
is refused because its fetch can bypass that dispatch gate; use the default
blocked service-worker policy for request steps.
The request runs in the page's main world, so an application that wraps
`fetch` sees it, and it goes through the same network observation as other
page requests: select network content (bodies or header values) to record it.
Captures with request steps state in `limitations` that the steps sent their
declared requests through the page's fetch. A scenario without request steps
is unchanged.
## Saved values
A step can keep a string or number from a JSON response, by name, and a later
step can send it, so a scenario can read back, edit and delete an object whose
id the application assigns:
```json
{
"browser": {
"mode": "launch",
"executable_path": "/absolute/path/to/chromium"
},
"start_url": { "url": "http://127.0.0.1:3000/items" },
"actions": [
{
"step_id": "save-item-1",
"action": "click",
"timeout_ms": 10000,
"locator": { "kind": "role", "role": "button", "name": "Save" },
"save_response": {
"method": "POST",
"path": "/api/items",
"save": [{ "name": "item_1", "json_pointer": "/id" }]
}
},
{
"step_id": "open-item-1",
"action": "goto",
"timeout_ms": 10000,
"destination": { "url": "http://127.0.0.1:3000/items/{saved:item_1}" },
"wait_until": "load"
},
{
"step_id": "copy-item-1",
"action": "request",
"timeout_ms": 10000,
"method": "POST",
"path": "/api/items/{saved:item_1}/copies",
"json": {
"source_id": "{saved:item_1}",
"title": "Copy of {saved:item_1}"
},
"expected_status": [201]
}
]
}
```
- A `request` step saves with `save: [{name, json_pointer, secret}]`, from its
own response. A `click` or `press` step saves with `save_response: {method,
path, save}`, from a newly observed matching response while the step waits.
The recorder watches before the action and excludes requests already running,
such as an earlier autosave. Method/path matching does not prove that the UI
caused the request: use a path that distinguishes the intended operation from
background polling. Multiple observed matches before binding fail as ambiguous;
requests starting after this observation window cannot be ruled out. It matches the method and a path on the
start origin as the browser spells it, so a path may name `My List` or
`My%20List` (`{*}` matches one segment, and the query is ignored), and fails
the step unless a 2xx JSON response arrives before its `timeout_ms`; one still
being read when the step times out or is cancelled saves nothing. A pointer
that names no string or number fails the step, so later steps never send a
missing value, as does a response over 4,000,000 characters: a request step
stops reading and aborts its fetch there. A UI step reads a body only when
its decoded size is bounded at 12,000,000 bytes or less: its received size,
or the declared Content-Length of one served from cache, times 1032 for each
gzip or deflate coding, the most DEFLATE expands. Other codings and unknown
or zero sizes are refused before materializing the body; decoded text over
the character limit also fails. The UI action may already have applied.
- `{saved:name}` sends a saved value in a request step's `path`, including its
query, and in a `goto` destination's `url`, percent-encoded; a destination's
structured `query` values are sent as declared. A saved value that makes a
path segment `.` or `..`, alone or with the text beside it (including `%2e`),
fails the step, since the URL would drop it. In a request step's `json` body,
a string that is only `{saved:name}` becomes the saved value with the JSON
type it was saved with, so a saved number id is sent as a number, and
`{saved:name}` inside a longer string is filled as text. A header value or
Basic credentials part sends one as `{"source": "saved", "name"}`. Other
braces, such as `{id}`, are sent as written, and so is a percent-encoded
`%7Bsaved:name%7D` anywhere but a `goto` URL, whose parsed form spells the
braces that way.
- Validation refuses a saved value that no earlier step saves. A later save of
the same name replaces the value. An integer past 2^53, which JSON parsing
would round to another number, and a number that is not finite, such as
`1e400`, negative zero, or a value that underflows to zero, such as `1e-400`, fail the step
that saves it.
- A scenario keeps at most 4,000,000 characters of saved values at once,
counting distinct retained redaction spellings, including trimmed variants,
of values saved as secret. Replacing a binding releases its live value
charge, while secret spellings remain protected for the scenario lifetime.
A save past the budget fails before changing the binding.
- A value saved with `secret: true` is redacted however short, as a declared
secret is, as `[REDACTED:page:<step_id>.<name>]`. The selected source
response is protected before retaining its body or header values, including
when only headers were requested. If that body cannot be read safely, its
body and header values remain unavailable. Before publishing the capture,
the selected source transaction's earlier URL, request headers and request
body are also protected using the admitted values. Unresolved source values
withhold that transaction's affected metadata/content with an explicit
limitation. Before publication, retained events are rechecked
with the final marked-response values, including immediate follow-up requests
and their response bodies, console/page errors, WebSocket text and event URL/name fields. Learned redaction facts are independent of source-identity readiness. Unknown source values withhold only the selected
source transaction. Other saved values, such as ids, stay visible, since
environments model them. The environment build warns about saved values
named like credentials that are not marked secret
(`credential-named-saved-values`).
- Page-derived cookie/local-storage values and constructed Basic credentials
remain redacted only from observations made after they were read/built.
They do not retroactively rewrite earlier facts. The final event projection
borrows only declared and response-marked patterns; promotion of an already
held page value to an explicit response secret preserves its first marker.
Projection state is session-local, references admitted pattern/byte owners,
and is invalidated on admission or response classification changes.
- Screenshots are withheld as `missing` from the start when the scenario plans
any response save marked `secret: true`, page-derived or Basic authentication
header values, or whenever nonempty sensitive values
are already known. Raw pixels cannot be safely text-redacted, so the provider
does not request pixels in those cases. This also protects earlier steps and
partial failures without retaining an image to scrub later. Other requested
artifacts remain available; affected screenshot/aggregate completeness is
partial and is not eligible for equality.
- Saved URLs and expanded request JSON are admitted before building their expanded strings or
serializing. Its allowance is the smaller of Node's string limit and
one sixteenth of current V8 available heap bytes, allowing for the expanded
tree, serialization, Playwright transport and browser-side copies. Size
accounting includes JSON escaping and repeated references; each distinct
saved value's size is measured once per request. An oversized request fails
before browser dispatch; the complete request envelope, including repeated
header values, is checked as well. URL encoding uses a conservative
nine-characters-per-UTF-16-unit allowance before encoding a saved value.
Reduce the payload/repetitions, or raise the parent
Node heap and restart; this is a conservative allocation estimate, not a
measurement or guarantee of browser/process-family memory.
- Evidence records a request step's empty `save` list as absent, so a scenario
that saves nothing records the same parameters and `scenario_sha256` as
before saved values existed, and REA versions without saved values read its
captures.
## Network evidence
`capture.events: ["network"]` retains request, response, completion, failure,
@@ -319,6 +649,18 @@ and unfinished-request metadata. Select content independently:
All three options default to `false`. Any selected network content also enables
network metadata observation; listing `network` in `events` is then optional.
Each observed request object receives a capture-scoped `transaction_id`.
Each `request` event carries `observed_at`, the wall-clock time REA observed the
request start. Playwright reports the browser's own start only once a request
ends, so each `request-finished` or `request-failed` event carries `started_at`,
the browser's wall-clock start, and `elapsed_ms`, its time from that start to
the end of the response, when the browser reports them. The environment views
time scenario records from these, as HAR entries are timed: the browser's start
where it reported one, else `observed_at`, which trails it by up to tens of
milliseconds while REA reads other responses. A duration is retained only from a
unique terminal event that reports it; missing or ambiguous durations stay
omitted, while an explicitly reported zero remains zero. Captures made before these fields existed lack them and
are read in record order. REA versions before them refuse captures that carry
them. Scenario comparisons ignore all three (`ignore_network_request_timing`).
Requests sharing a URL remain distinct. Redirects link to the preceding
transaction only when it was observed; a null predecessor does not establish
that no earlier hop existed. Request events require both transaction fields;
@@ -352,8 +694,16 @@ Proxy-Authorization, Cookie, and Set-Cookie values are null with
and their standard URI/form or JSON-escaped representations are replaced without
decoding unrelated binary bytes. Other encodings are not interpreted. Digests describe the
retained representation after redaction.
The selected body-byte limit also bounds that retained representation after
secret expansion, before base64 serialization. Credential header values are
discarded before retained-header allocation accounting; their names and null
projections still count. The transport/API has already materialized incoming
bodies and headers, so these retained limits do not bound producer-side input
allocation.
Only completed responses are read. At the end of the final step, observation
Only completed responses are read. The producer-observed terminal state is
recorded independently of whether its event metadata fit the retention budget. An omitted completion event cannot turn a
finished transaction into an unfinished one. When the final step ends, observation
stops; requests still in flight emit `request-unfinished`, and their observed
responses have explicitly unavailable bodies. Already-admitted content reads
drain before browser cleanup. At most 16 reads run at once; queued work is
@@ -373,6 +723,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
@@ -390,8 +758,8 @@ and compared as an unordered collection after caller rules apply, because
concurrent responses arrive in any order and can land in a later step; a
redirect retains the complete predecessor ancestry, including each hop's own
request, response outcome, and selected network content. Each transaction's
capture-local IDs, sequence/index fields, and content source-sequence anchor
are removed. Caller rules normalize retained strings before hashing. The
capture-local IDs, sequence/index fields, content source-sequence anchor and
request times (`observed_at`, `started_at`, `elapsed_ms`) are removed. Caller rules normalize retained strings before hashing. The
`preserve_redirect_transaction_ancestry` built-in rule fingerprints each hop
as canonical SHA-256 of `{ events, redirected_from }`: `events` is the sorted
list of its normalized event SHA-256 digests, and `redirected_from` is its
@@ -405,6 +773,18 @@ 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. The
`ignore_network_request_timing` built-in rule likewise joins the commitment, so
normalization digests differ from comparisons made before it, though captures
with and without request times compare alike.
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.
@@ -417,7 +797,67 @@ selection. Captures with different selections report event evidence as unknown
with a recovery instruction, even when no selected family emitted an event.
Captures written before this field existed omit it, so their event evidence is
also unknown.
Retained-capture readers admit that omission without deriving a selection from
observed events; other artifact families remain comparable. Current capture
producers still require and publish the selected event families. Manifest,
environment and script-export readers share the retained-capture admission
rules and preserve the same missing fact.
Network normalization also commits the removal of capture-local transaction IDs,
attribution to the requesting step, and unordered transaction comparison. These
built-in rule identities participate in the normalization digest.
## Summary delivery and retained views
`capture_browser_scenario` accepts `detail: "summary"` to retain the complete
capture in the MCP session and return its summary. The default remains
`complete`. Delivery detail does not change scenario parameters or identity.
The summary reports each step's status, error, URL and captured/missing/truncated
section names. It omits `not_requested` sections and repeated `before_url` values.
The accessibility tree comes from the first unsuccessful step, or the final
step on success; when that step captured no tree, its nearest earlier captured
tree is identified explicitly. Event and network counts cover retained records,
with capture omissions reported separately.
For the one-shot CLI, save the complete Evidence before selecting a summary:
```bash
rea capture-browser-scenario scenario.json --detail summary --evidence_output /absolute/path/scenario-evidence.json --json
```
Use the returned `normalized_result.parent_evidence_id` with
`inspect_analysis_view` source `{kind: "retained-evidence", evidence_id: "..."}`.
For a CLI bundle, use an `evidence-file` source with its absolute path and the
parent Evidence ID. Views read the recorded capture; they never replay actions.
- `{kind: "summary"}` returns the same overview.
- `{kind: "browser-scenario", collection: "steps", step_indices: [2], artifacts: ["accessibility"], offset: 0, limit: 1}` returns selected recorded steps and artifacts.
- `{kind: "browser-scenario", collection: "events", event_kinds: ["response"], transaction_ids: ["request-2"], offset: 0, limit: 20}` pages selected events with original item indices.
Omitted filters select all; empty arrays select none. Pagination counts matching
retained rows. Exhausting a page does not establish capture completeness or
current application state.
Transaction pages use `{kind: "browser-scenario", collection: "transactions", limit: 20}`.
Their stable identity is the parent Evidence ID plus `transaction_id`; each row
keeps original event indices. Metadata events are returned by default. Select
`event_kinds: ["network-content"]` for recorded bodies and header values, or
`event_kinds: []` for identity and indices alone. A `step_indices` filter selects
a whole transaction when any retained event belongs to a selected step; this
is receipt attribution, not proof that the action caused the request.
For a large retained tree, request `{kind: "browser-scenario", collection: "text",
step_index: 10, artifact: "accessibility", offset: 0, limit: 2048}`. `dom` is also
supported. Offsets and limits count UTF-16 code units. The full artifact digest
and byte size remain attached to each page. Missing, truncated and unrequested
text stay explicit; source truncation counts are preserved without treating an
exhausted page as a complete capture.
Network summary counts include every retained transaction identity, including
those whose request event was dropped. Missing request events, conflicting
metadata and observed request failures have separate counts. `transactions` is
`null` when network observation was unrequested or its legacy selection is
unknown and there are no retained transactions; an observed empty inventory is
`0`. Full capture omissions remain available and each page carries source
completeness, dropped-event counts and omitted-transaction accounting.
+62
View File
@@ -0,0 +1,62 @@
# Bun standalone executables
`inspect-artifact` inventories embedded Bun module graphs in Mach-O `__BUN,__bun`,
PE `.bun`, ELF `.bun`, and appended trailers. `extract-artifact` writes their stored
module contents through the existing safe output tree. Neither operation executes
the executable or decompiles bytecode.
```sh
rea inspect-artifact ./hello --json
rea extract-artifact ./hello --json
```
In MCP, open the executable with `open_binary`, then call `inspect_artifact`.
Use `detail: "summary"` for large graphs and `inspect_analysis_view` to page retained
Evidence. The root metadata's `entry_point_logical_path` is the reader-owned
location of its entry module. Use it as an inventory `logical_paths` filter,
then read selected node fields with the returned content identity. This also
works for names mapped to indexed extraction paths. The complete Evidence stays
retained; no source-name normalization is needed in the caller.
The root reports the carrier, layout, module count, entry point, raw flags and
compile exec-argv. Each module retains its source name, loader and encoding values,
entry-point status, sizes and SHA-256. Sourcemaps, bytecode and module-info regions
are separate children. Source names such as `/$bunfs/root/main.js` remain in
metadata; extraction uses a relative path. Unsafe or non-UTF-8 names receive an
indexed path and retain their source spelling or bytes. Virtual names deeper
than 256 logical path parts also use an indexed path with an explicit limitation,
preventing unbounded path-tree work while retaining the source fact. Duplicate names remain
separate occurrences; ordinary extraction collision rules apply.
The recognized record shapes are the 36-byte Bun 1.3.3 layout and the 52-byte
Bun 1.3.9 layout. These labels identify structural layouts, not a detected compiler
version. Bun does not store an explicit revision identifier: unknown table sizes
and ambiguous known layouts are unsupported. A future layout that is byte-for-byte
compatible cannot be distinguished. Invalid pointers and truncated claimed sections
are malformed. An absent trailer without a named section does not establish a Bun
graph. Unknown enum values remain raw, with a null name.
Sourcemaps are stored Bun regions, not necessarily standalone JSON source maps.
Extracted contents retain their original encoding. Use JavaScript analysis only on
compatible source files; bytecode is retained as evidence, not decoded.
Module parsing reads exact metadata ranges rather than loading stored asset
bodies or whole pages around small pointers. A cache retains at most 256 KiB
and 1,024 ranges for one reader preparation. Cumulative uncached metadata reads
are limited to 256 MiB, checked before allocation or I/O. The module table,
decoded strings and combined logical path tree have separate admission checks;
at most 250,000 distinct path prefixes are admitted before inventory construction.
Stored bodies are range-streamed for hashing and extraction. Header/suffix observations are reused
only for the same admitted file state, with selected-path checks before publication.
See [limits and recovery](design/limits-and-recovery.md) for admission ceilings.
Synthetic tests cover the carriers and malformed declarations. Run
`node scripts/verify-bun-standalone.mjs` after `npm run build:cached` for local Bun
verification, and add `--docker` to compile a Linux fixture with `oven/bun:1.4.0`.
Bun and Docker are fixture producers only and are not installed by REA.
Layout references: [Bun 1.3.3 serializer](https://github.com/oven-sh/bun/blob/bun-v1.3.3/src/StandaloneModuleGraph.zig)
and [Bun 1.3.9 serializer](https://github.com/oven-sh/bun/blob/bun-v1.3.9/src/StandaloneModuleGraph.zig).
The current 52-byte record order can also be checked in the
[Bun 1.4.4 Rust serializer](https://github.com/oven-sh/bun/blob/bun-v1.4.4/src/standalone_graph/StandaloneModuleGraph.rs).
+107
View File
@@ -0,0 +1,107 @@
# Capability composition and ownership
REA keeps domain contracts and shared workflows separate from provider adapters
and CLI/MCP translation. A capability can have several provider identities; a
provider can contribute operations to several capabilities. Those associations
are source facts, while host availability is an observation.
## Source declarations and runtime bindings
`src/composition/capabilityDeclarations.ts` contains static provider identities,
named contract-group references and precise operation subsets. Complete groups
refer to `TOOL_CONTRACT_GROUPS` in the existing contract owner; family definitions
and provider catalogs read the same arrays, so a new group operation is included
without updating a copied provider list. Metadata imports only the group ID type
and pure metadata leaves; it must not load factories, schemas, engines or
generated catalogs. Catalog generation resolves groups and subsets against the
canonical contracts and rejects unknown groups/operations and conflicting
identities. New provider provenance must come from the implementation
that actually produces the Evidence, including format-specific capture adapters
and derived workflows.
`src/composition/capabilityServices.ts` binds typed service factories to those
capability keys. Each runtime entry owns construction, cleanup and any available
host/configuration facts or live probes. A service entry requires a close
capability; entries adapting managers retain their existing `closeAll` operation.
The host maps entries without erasing their individual service/port types. There
is no generic JSON operation dispatcher.
These two declarations have different loading needs. Static facts can be read
without loading a schema or provider graph. The full runtime composition is
materialized only when the MCP runtime needs it. One-shot CLI adapters import the
selected factory rather than constructing a complete capability host. CLI and MCP
reuse the same provider/service factories and application workflows, while
keeping their respective argument translation and lifetime.
## Resource lifetime
`CapabilityScope` registers a module's actual close capability when its instance
is composed. There is no independent server provider-close list to update.
Concurrent shutdown calls join the same cleanup attempt. A successful peer is
removed from pending cleanup; failed peers remain owned and are retried on the
next close. Synchronous throws and rejections without a reason remain failures,
and cannot prevent other peers from attempting cleanup. Shutdown seals further
ownership registration.
The host distinguishes these lifetimes:
- Host-owned service instances are released by that host.
- Caller-owned optional observations remain borrowed by SDK server instances;
their process-level owner closes them, including discarded discovery probes.
- CLI invocations use their existing finalizers to join cleanup and preserve the
original observation, cancellation and cleanup errors.
- Binary engines, bundle readers and investigation records retain their existing
owners. The `BinarySession` transition/drain barrier still coordinates target
changes, reset and disposal. A module refactor must not create independent
barriers that allow late work to repopulate reset records.
An application workflow may borrow a shared resource scope, such as byte entropy
using the host's artifact scope. Its wrapper does not acquire a second ownership
claim over the borrowed scope.
## Availability and registration
Module bindings provide host support, selected configuration and precisely typed
live availability probes through `CapabilityAvailabilityFacts`. Constructors do
not run these probes or launch engines. Session discovery invokes the existing
provider-owned probes only for advertised families; live failures remain fresh
observations. Caller policy, selected environments and injected providers retain
their existing precedence.
MCP registration remains in the MCP adapter. It consumes composed services,
record ports, admission and logging; it must not add fallback constructors that
can escape the host owner. Named contracts bind handlers and schemas. The complete
tool-family registrar map is checked by TypeScript, and catalog order never
selects a handler or schema.
## Extending a capability
1. Add or reuse the domain contract and application workflow, with explicit
Evidence provenance, coverage, unknowns and caller choices.
2. Put concrete construction in `src/composition/` and keep its default factory
lazy. Reference adapter-owned pure identity metadata from both the provider
and its static capability declaration.
3. Add the runtime binding if a host-owned service is needed. Bind cleanup and
availability/probes there, preserving borrowed versus owned resources.
4. Add the CLI/MCP argument adapters and their named registrations. A one-shot
command uses the shared selected factory and existing finalization helpers.
5. Run source-boundary, discovery, SDK-schema and relevant public workflow checks.
Exercise partial cleanup, retry, cancellation and caller-owned peers at the
applicable production seam; use actual applications/providers for routing and
complete Evidence verification. Recording ports do not prove real providers.
The constructor boundary guard covers the centralized Frida, Cutter, GDB and
Rizin constructors without rejecting legitimate type-only injected interfaces.
Other settled pure/provider boundaries continue checking type imports too. The
shared parser records module-edge erasure once. Whole-declaration type imports
are erased; inline type bindings retain their runtime module edge under
`verbatimModuleSyntax`. Static dynamic imports remain runtime edges too.
Tests belong with their semantic owner and must be
found exactly once; provider roots need the corresponding adapter discovery lane.
This is an internal composition contract. External plugin distribution, automatic
provider installation and a universal provider interface are separate concerns.
The [initial verification report](verification/capability-composition-2026-11-01.md)
records the executed public workflows, cleanup boundaries, resource measurements
and remaining provider qualification gaps for this composition increment.
+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.
Loaded 100 of 6367 files, more files were not shown because too many files have changed in this diff. Show more