mirror of
https://github.com/morluto/rea.git
synced 2026-10-31 20:06:14 +00:00
+1








1ddee1089b
* 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 commitafd4d571f6, reversing changes made to317bf3fe23. * 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 <noreply@anthropic.com> * 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 <noreply@anthropic.com> * 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 <noreply@anthropic.com> * 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 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * fix(source-maps): resolve empty sourceRoot sources against the map URL (#8107) webpack 5 and tsc write "sourceRoot": "" for map-relative sources. Since5f702dd6cthe shared join added "/" for any non-null root, so these sources resolved from the URL root: rxjs's ../../../src/internal/Observable.ts under /npm/rxjs@7.8.1/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 <noreply@anthropic.com> * 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 <noreply@anthropic.com> * fix(captures): admit browser-serialized HAR request URLs (#8112) * fix(captures): admit browser-serialized HAR request URLs har-schema@2.0.0 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 <noreply@anthropic.com> * 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 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * 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 <noreply@anthropic.com> * 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 <noreply@anthropic.com> * 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 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * 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 <noreply@anthropic.com> * 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 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * 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 <noreply@anthropic.com> * 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 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * 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 <noreply@anthropic.com> * 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 <noreply@anthropic.com> * 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 <noreply@anthropic.com> * 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 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * 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 <noreply@anthropic.com> * 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 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * 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 <cursoragent@cursor.com> * 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 <cursoragent@cursor.com> * 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;50822e1effixed 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 <169027046+xu91102@users.noreply.github.com> Co-authored-by: Kaoru0822 <kanadekitauji@proton.me> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: ShuaixinHuang <1836656514@qq.com> Co-authored-by: morluto <76467478+morluto@users.noreply.github.com> Co-authored-by: Henglie <90338155+Henglie@users.noreply.github.com> Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: 吴佳浩Alben <35291372+XiaoBinGan@users.noreply.github.com> Co-authored-by: Entrpi <133157614+Entrpi@users.noreply.github.com> Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>