* refactor(errors): require a reason for every analysis cancellation
Add cancelledBy, the one constructor for AnalysisCancelledError. Its
reason argument is required: an aborted signal, an earlier cancellation,
a reason carried from an inner failure, or an OwnerCancellation when REA
closed the work's owner itself.
Closed adb, apktool, firmware, sqlite, gdb, web-bundle analyzer and
binary-session record owners now report OwnerCancellation.ownerClosed
instead of an absent reason. Firmware's predecessor wait returns its
cancellation as a Result, removing an unreachable reason-less fallback.
An architecture guard rejects new direct constructions, with a counted
per-file baseline that can only shrink.
* refactor(errors): share one aborted-signal predicate with cancelledBy
* refactor(process): return a departing caller's reason from a shared startup wait
* refactor(providers): build decompiler-provider cancellations with a required reason (#10587)
Migrate every AnalysisCancelledError construction under src/ghidra,
src/ida, src/cutter, src/jeb and src/kuna to cancelledBy and lower the
guard baseline from 671 to 572. Closed Kuna target commands report
OwnerCancellation.ownerClosed instead of an absent reason, and Kuna and
JEB close abort their lifetimes with that typed reason.
* fix(errors): read the reason of any signal-shaped cancellation source
* test(ci): cover the counted-baseline comparison behind the architecture guard
* refactor(contracts): derive the public tool catalog from one contract-group list
toolContracts.ts and toolFamilies.ts each imported and listed the same
contract groups. toolContracts.ts now owns one ordered group object;
TOOL_CONTRACTS is its flattened values in the same catalog order, and
toolFamilies.ts reads TOOL_CONTRACT_GROUPS from it instead of re-importing
every group. The static catalog-source lint rule learns the
Object.values(groups).flat() composition so positional-contract enforcement
still discovers every group without executing modules.
* fix(lint): reject computed tool catalog groups
The doctor cancellation fixture wrote its readiness JSON with
writeFileSync, which truncates and then writes. The test polls only for
the file's existence, so a read between the two steps parsed an empty
file and failed with "Unexpected end of JSON input" (main run
43604752864, PR run 43606296871).
Write to a pending path and rename it into place, as ghidraJavaDoctor and
darwinProcessRunTokenReader fixtures already do. Apply the same fix to the
two sibling fixtures that poll for a PID file's existence and then parse
it: jadxConfiguration and runnerCancellation, where an empty read becomes
PID 0.
* fix(cli): validate plist and dylib-root options with their contract fields
inspect-plist --relative-path and trace-dylib-resolution --root were
declared as bare strings on the CLI. --schema under-advertised them, and an
empty or NUL value reached the operation contract, whose refusal named the
contract field (path, roots) instead of the CLI option.
Declare both options with the contract's own field schemas
(localPathStringSchema; dylibResolutionInputSchema.shape.roots), so the CLI
refuses them first and names --relative-path or --root.
Fixes#9764Fixes#9823
* fix(mcp): observe SDK initiated cleanup failures
BinaryTargetResolver kept a private copy of StableFileHash's descriptor read
loop because it must hash the same descriptor it classified, admitted with
symlinks rejected, while hashStableFile opens its own descriptor and follows
symlinks. The loop is now hashStableRegularFileDescriptor, which takes the
admitted descriptor and the caller's symlink setting; readStableFile and the
resolver both use it. The resolver keeps symlinks: reject and maps a change
to its existing BinaryTargetError and cancellation to AnalysisCancelledError.
* fix(setup): name client configuration syntax from one reader table
Each client format now maps to one reader description (syntax, strict or
JSONC, BOM and empty-file handling, strict-JSON repair). The document
parser, the strict-JSON rewrite decision and uninstall's diagnostic all
read it, so uninstall no longer re-derives a syntax name that called
Goose's YAML and JSONC-admitting clients "JSON".
* test(hopper): retry cleanup after confirmed application exit
* feat(errors): type refusals at the producer with a total recovery
A resource refusal without authored text fell back to resource-kind advice:
a fixed parser ceiling reported as memory told the caller to add host
memory, an upstream IDA reply ceiling pointed at REA's MCP transport, and a
fixed Kuna text ceiling reported as capacity was marked retryable. An
ambiguous selector was reported as invalid input that pointed at the schema.
Add AnalysisRefusal with three variants built from facts producers already
own: fixed_ceiling (FixedAllowance, now with limit, unit and narrowing),
adjustable_budget (AnalysisSettingLimit) and ambiguous_selector (new
AnalysisInputError option). Each variant is attributed to the error's
operation and projected under details.refusal. Presentation is a total
switch over the variants, and authored text still takes precedence.
Migrate the first producers. GraphQL nesting, Playwright trace input, the
Kuna function text and the IDA upstream reply become fixed ceilings. jadx
overloads and Kuna ambiguous names become ambiguous selectors, replacing the
two Kuna-specific ambiguity texts. Seven occupied admissions declare
measuredExhaustion. The rea/typed-resource-refusal lint rule rejects new
untyped resource constraints, and the remaining 24 are counted in a baseline
that can only shrink.
* fix(errors): honor refusal recovery ownership
Table-backed add-mcp agents now project their client-table row instead of
redeclaring format, display name and profile in a second table. The row
also carries add-mcp aliases and project documents. mcp add now applies
the read-scope rules setup already enforced: Gemini settings policy blocks,
the Claude Desktop MSIX write blocker and MSIX AppData redirection.
* refactor(javascript): classify implicit execution in one owner
Add javascriptSemanticExecutionPoints, a total classification of every
runtime Babel node type into the ways its evaluation can run code without
call syntax. The member-mutation escape walk, the accessor operand
collector and the catch-source reachability check consult it instead of
their own syntax lists. Kinds those collectors have never applied are now
named in exhaustive switches rather than absent.
A guard test fails when the installed @babel/types defines a node type the
table does not classify, and unknown node types classify as unsupported
syntax rather than inert.
* test(hopper): retry cleanup after confirmed application exit
* fix(javascript): classify enumeration and catch binding effects
* fix(javascript): retain catch uncertainty for identifier reads
A cancelled GDB startup whose process stop could not be confirmed retained a
capability error. The next cleanup retry then reported "This analysis is
unavailable for the current target" instead of the caller's cancellation.
Move Rizin's retention rule (the #5963 fix) into one shared
retainedDebuggerCleanupFailure in src/process/ownedDebuggerSession.ts and use
it from both session managers. The GDB command-cancel path now retains the
cancellation itself, so retry reports no longer depend on Set insertion order.
Use createNodeDiscoveryTempDirectory for the target-location and
runtime-observation fixtures too, so a TMPDIR containing `_` cannot make
them fail for the same reason as the cancellation test.
Refs #9996
The sibling sites are the ones #4484 switched.
Co-Authored-By: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
The changed-prefix regression rewrote its fixture at the same size within
milliseconds of creating it. Linux stamps mtime/ctime from a coarse clock,
so both writes can share every stat field the target guard compares, and the
classifier parsed the rewritten header without seeing a change. Age the
fixture's timestamps before the mutation so the rewrite is observable on any
filesystem while the test still requires a changed-input refusal.
Fixes#8685
Supersedes #8689
Co-Authored-By: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
The nesting guard ran on a child visit of GgufHeader#value, so an empty
innermost array never produced one and a 33rd frame parsed as success
while the reported limit stayed maximum_array_frames: 32. Charge the
frame when entering the ARRAY branch instead, so the bound counts the
advertised unit and no longer depends on a scalar child that is not a
frame.
Fixes#7312
Supersedes #7698
Co-Authored-By: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
inspect-native-exports treated any nonzero byte in .dynsym entry 0 as a
malformed table. That is right when the reserved slot holds a real symbol,
which was #7389, but some Android arm64 libraries set only st_name. Those
files were rejected before any export could be read.
Skip that entry, keep the inventory complete, and record the nonzero
st_name in limitations. Any other nonzero field still fails the table.
Fixes#9700
Supersedes #9752
Co-Authored-By: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
Charge the manifest split facts against the native inventory's retained JSON
allowance and refuse more than 64 split types or 256-character names as an
unknown, so a crafted manifest cannot overflow the response. Reports saved
before split facts existed still parse: the report schema treats absent
facts as optional while the live bridge contract requires them. The JADX
regression now decodes a generated binary manifest through
nativeDeclarations, with named and resource-ID-only attributes.
Refs #9756
Co-Authored-By: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
A base APK whose manifest requires ABI or density splits keeps its native
libraries in those splits, yet inspect_android_jni_bindings reported a
complete library inventory with zero libraries. The bridge's native
declaration inventory now also decodes the root binary manifest without a
session and reports its requiredSplitTypes and isSplitRequired facts, or why
they are unknown (bridge identity 11). When splits are required or unknown,
the library inventory is partial with an unknown that says so, and a binding
without a candidate is unknown instead of unmatched.
The binary manifest fixture from #9769 was used to check the bridge with the
real jadx-headless-mcp 0.7.1 JAR, including stripped attribute names that
resolve through framework resource IDs.
Fixes#9756
Co-Authored-By: 2SinceJuly <202121119+2SinceJuly@users.noreply.github.com>
Co-Authored-By: Phon <twilyt1@proton.me>
Co-Authored-By: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
Setup's result is agent-only, so incur hides it when stdout is a terminal and
no format was chosen. Only an interactive setup or a fully interactive TTY dry
run rendered a summary, so `rea setup --yes` printed nothing. Render the dry
run plan or the existing result summary under incur's own hidden-output
condition instead.
Fixes#10531
Supersedes #2185
Co-Authored-By: balkan16 <57933796+balkan16@users.noreply.github.com>
Co-Authored-By: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
When the macOS process-ownership helper's swiftc compile hit
REA_SWIFTC_COMPILE_TIMEOUT_MS, inspect-macho and other native commands
reported a non-retryable provider failure, while process capture
already treated the same fact as retryable. The helper's error now owns
that interpretation (compilerTimedOut and compileTimeout, with its
deadline, message and recovery). Native commands return a retryable
AnalysisTimeoutError naming REA_SWIFTC_COMPILE_TIMEOUT_MS, and process
capture reads the same fact.
Fixes#8936
Supersedes #8938
Based on #8938; the interpretation moves to the error's owner so both
consumers share it.
Co-Authored-By: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
A worker heap too small for REA's bridge stops the JVM before the protocol
handshake, so the launcher's retained stderr is the only evidence, and the
failure was reported as capability_unavailable with configuration advice.
When that stderr carries HotSpot's java.lang.OutOfMemoryError heap reasons,
report resource_constraint for memory with observed_max_heap_bytes null and
the same REA_JADX_HEAP_MIB remediation the bridge-reported heap failure uses,
now shared and stating -Xmx precedence. Other launcher stops are unchanged.
Refs #3517
Supersedes #3573
Co-Authored-By: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
The witness seed accepted its folder with a readdir check that follows
symlinks, treats a missing path as usable, and reports a regular-file
parent as "not empty". The record is later saved through planOutputTree,
so a seed could accept a folder the save then refused. The seed now
admits the folder with planOutputTree too, and returns its refusal.
Fixes#9291
The MCP boundary test is from #9292 by viniciusdsandrade; its parallel
admission helper is not taken, since planOutputTree already owns the
rule and export already uses it.
Co-Authored-By: Vinícius DS Andrade <95101505+viniciusdsandrade@users.noreply.github.com>
Co-Authored-By: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
environment-build/export ran an unclassified recursive mkdir of the
destination's parent before SafeOutputTree validated anything. A
trailing `..` therefore created directories before it was refused, and
a regular file in the parent path surfaced as unreadable_output instead
of refusing the selection.
SafeOutputTree.create gains a createParents option that admits the
root's own syntax first, then creates the parents, mapping EEXIST and
ENOTDIR to a destination refusal. The environment bundle writer uses it
instead of its own mkdir.
Fixes#7688Fixes#7571
The non-directory mapping comes from #7734 by HeadmasterEggy, and the
syntax-first check from #7588 by harshitethic; their tests are kept.
Co-Authored-By: Avocado <headmastereggy@gmail.com>
Co-Authored-By: Harshit Sharma <66710144+harshitethic@users.noreply.github.com>
Co-Authored-By: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
General provenance only knew a literal require, so a bundled Electron
external held in one binding and destructured from another
(`const s=n(K);const{BrowserWindow}=s`) lost its windows and IPC. The
Electron binding origin now follows stable lexical aliases, and stops at
a rebindable binding that direct eval may have changed.
Fixes#4490
Supersedes #6993
Squashed from #6993 and rebased onto main. Rebase adaptation: the walk
uses the caller-selectable precision.alias_selections budget (the fixed
SEMANTIC_ALIAS_SELECTION_LIMIT it used was removed by #9706) and records
alias-selections when it stops there; a test pins that an over-budget
chain is not claimed and the limit is reported.
Co-Authored-By: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
A successful batch_decompile item carried no item-level provenance:
only the batch Evidence aggregated limitations, so when a provider such
as Kuna answered repeated selectors from different sources, the caveat
did not say which occurrence it applied to. Successful items now keep
their own observation's limitations (exactOptional, so retained records
still parse), and the retained items view projects them.
The batch unavailable item also dropped the provider's typed
pseudocodeAbsence reason; it now carries pseudocode_absence.
Fixes#8350
Co-Authored-By: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
The receipt fixture replied with complete relationship coverage and unchanged
calls, which the verifier now rejects. Reply with the unknown caller/callee
coverage HopperProvider declares and the unknown calls dimension that
compare_functions derives from it.
Refs #8067
Co-Authored-By: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
`--schema` for JSON-input CLI commands showed only an opaque
`inputJson` string, so callers could not see the document the command
actually parses. The argument now carries the decoded document as its
contentSchema, built from the same contract each command or its service
parses with (the Evidence input frame, or the ledger and coverage
request schemas). `$schema` is stripped, and a schema JSON Schema cannot
represent is refused rather than advertised as {}.
Fixes#9195
Supersedes #9283
Squashed from #9283 (with morluto's validation test) and rebased onto
main; the coverage test now expects boundary_id's stable-identifier
pattern from #9919. #9214 by 1052326311 took the same approach.
Co-Authored-By: morluto <76467478+morluto@users.noreply.github.com>
Co-Authored-By: 1052326311 <65798732+1052326311@users.noreply.github.com>
Co-Authored-By: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
MCP passes progress to observeWebSession, but the CLI did not, so a
caller of `rea observe-web-session` never saw the armed observation
window it must wait for before acting. Three CLI commands also each
wrote their own copy of the same stderr progress line.
One cliProgress module now owns the stderr progress line and reporter.
observe-web-session uses it, as do web runtime, JavaScript application
and desktop capture progress. The CDP session also stops reporting
readiness once collection is no longer active, so a stopped capture does
not invite the external action.
Fixes#8141
Supersedes #8149
Based on #8149; the desktop capture copy now shares the line writer too.
Co-Authored-By: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
The approval prompt now discloses a client's manual completion step,
but the interactive TTY dry run still printed only the planned file
changes, so `rea setup --dry-run --client grok_bot` hid the Grok Bot
connector step. The dry-run renderer now prints the planned result's
remediation the same way.
Refs #7692
Co-Authored-By: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
With --token-count --full-output, TOON and YAML results were delegated
to Incur before the envelope-aware count branch, so Incur counted only
the selected data (13) instead of the emitted full document (31).
Keep that combination on the REA output boundary so the count covers
the envelope and metadata, matching the docs/cli.md contract. Bare
counts stay delegated to Incur and are unchanged.
Fixes#7271
Supersedes #7278
Co-authored-by: Saim Shafique <128062316+sx4im@users.noreply.github.com>
Co-Authored-By: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
inspect_adb_package matched a leading digit run in userId/appId
declarations and admitted it, so userId=10123abc became user_id 10123
with no numeric issue and complete coverage. A malformed declaration
before a valid one could also decide which id the caller received.
The decimal id must now end its token: Package Manager separates later
fields with whitespace, so attached text makes the declaration an
invalid_format numeric issue with its original line, coverage becomes
partial, and first-valid selection only considers well-formed values.
Whitespace-separated suffixes stay admitted as before.
Fixes#7409
Supersedes #7470
Co-Authored-By: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
Add a PE whose section stores every declared export slot, so the entry
allowance refusal is also shown for a structurally possible table, and use it
over MCP. Leave dispatch-test directory removal to its temporary fixture.
Co-Authored-By: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>