* 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
Ghidra projected the bridge's invalid_regex_pattern code through an inline
copy of the refusal that RegexMatcher's invalidRegexPatternError already owns
for Kuna, Hopper and IDA. Ghidra now calls that owner, passing the bridge's
own message, which already names the pattern and Java's syntax error, as the
user message. The owner accepts error options so Ghidra keeps its cause and
diagnostics.
Problem: code that Ghidra auto-analysis never reaches, such as DOS MZ
handlers stored only in runtime-filled dispatch tables, has no Ghidra
function. procedure_* lookups then returned a plain procedure_not_found
whose remediation pointed only at list_procedures, and
resolve_containing_procedure returned not_in_procedure with no caveat.
Neither outcome named the supported recovery: declaring the entry as a
function seed in REA_GHIDRA_SEED_FILE.
Fix: one Ghidra-owned recovery, ghidraFunctionSeedRecovery(source), names
the seed mechanism without claiming that the address holds code. It
follows the session source, because seeds apply only to a fresh import:
selected projects and owned checkpoints name the setting to unset first.
The procedure_not_found remediation appends it. A not_in_procedure
resolution now carries ghidraNotInProcedureLimitation, attached through
the same absence-caveat path as the empty-reference caveats and read from
the domain containing-procedure parser. outside_segments and found
results are unchanged. The installation guide's seed section says where
the recovery appears.
Validation: src/ghidra/GhidraProvider.behavior.test.ts fails on the base
(4 failed, 291 passed: both procedure_not_found rows lack the setting,
the selected-project row lacks the fresh-import seed recovery, and
not_in_procedure has no seed limitation) and passes after the fix
(295 passed). npm run check:fast passes. Real Ghidra, including seeding
real-mode DOS linear addresses, was not exercised.
Refs morluto/rea#3767
* refactor(ghidra): drop the unreleased checkpoint v1 reader and a one-time pattern proof
The Ghidra checkpoint manifest v1 shape and its upgrade to v2 arrived in the
same commit (c07ab4b994, first released in 6.9.0), and every release writes
v2, so no released build produced v1. A v1 manifest is now refused as
malformed and left unchanged, like any other unknown manifest.
portableSchemaPatterns.test.ts proved two advertised patterns equal the
look-ahead regexes they replaced by enumerating 90,000 strings. The lasting
invariant (no look-around in advertised patterns) is asserted catalog-wide by
advertisedPatternsVMode.test.ts for the default and full input profiles. The
current accept/refuse contract for dylib roots and the reserved process
environment name moves to the owning schema tests as small tables.
Refs #10515
* test(process): retain malformed environment name coverage
* fix(ghidra): commit analysis profiles to the bridge script sources
Snapshot reuse for Ghidra depends on the analysis profile digest, but the
profile committed only to hand-bumped semantic markers. A bridge change that
forgot to bump its marker left earlier snapshots admissible (#3664).
Add ghidraBridgeSourcesDigest, a SHA-256 over every packaged bridge source by
name, and commit it as bridge_sources_sha256 in every Ghidra profile, for new
imports and existing projects alike. A changed script now changes the profile
digest without anyone remembering a marker. Existing snapshots are not reused
once, after upgrading.
GhidraRawProfileDigest.test.ts pinned literal digests that every bridge or
marker change had to re-record; it now checks the committed parameters and
that the digest is the profile owner's digest of them. A new test checks that
the source list covers every packaged .java file.
* fix(ghidra): frame bridge digest metadata as a tuple
Fifteen domain schema files each declared z.string().min(1), eight of them
as boundedTextSchema although it bounds nothing. They now share
nonEmptyTextSchema from src/domain/textSchemas.ts; the one trimming copy in
reconstructionCoverage.ts is renamed trimmedTextSchema.
Three private UTF-8 prefix truncators (gdb recent MI records, rizin
diagnostics, environment replay) and src/process/boundedUtf8Text.ts are
replaced by utf8HeadExcerpt in src/domain/utf8HeadExcerpt.ts, which encodes
only the retained prefix and returns a slice of the source text.
The parser's work budget was a fixed 64 Mi units, so a real 26 MB .NET 8
NativeAOT image (bflat 8.0.2) stopped frozen-string recovery at 4,433 of
6,685 strings and reported partial. Give each image a base budget plus 8
units per admitted source byte, bounded by the source-byte ceiling, and
record the scaling in the analysis profile (contract revision v11). With it,
that image completes with all 6,685 frozen strings using 71.6 M units.
Only the budget scaling of #4559 is taken: main already resolves the
base-type closure in linear time.
Fixes#4445
Supersedes #4559
Co-Authored-By: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
Keep the bridge cursor-mismatch code check without copying its message, and
let the pcap relative-tool diagnostic use the shared child deadline, which it
does not test.
Co-Authored-By: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
A cursor for another target, profile, pattern, mask, bounds, or document
was reported as invalid input and pointed at schema repair. Name the
binding, attribute the issue to the cursor, and leave the differing
component unknown.
Fixes#8366
Supersedes #8420
Co-Authored-By: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
* fix(ghidra): prevent loader relocations from constant folding
Ghidra imports loader relocation destinations with their file bytes. A
destination in a non-writable loaded block can be populated by the
runtime loader instead, for example a copy-relocated musl `stderr` in
RELRO, so the decompiler folded the on-disk zero and showed
`fprintf(NULL, ...)`.
Before initializing the decompiler, mark each loaded, initialized,
non-writable block containing a relocation destination volatile in the
ephemeral database, and add a limitation to procedures that read such a
destination. ELF shared objects and PIEs are included; relocatable
objects and core files are not. Permissions and source FileBytes are
unchanged.
Sessions without database-mutation authority (Windows P0 and existing
caller projects) record the limitation but do not mark blocks volatile,
and the analysis profile versions the two behaviors separately.
A real Linux Ghidra ELF probe runs in the Ghidra bridge CI lane.
Fixes#4177
Squashed from #4198 and rebased onto main. Rebase adaptations: the
read-only gate, the verifier renamed to verify-ghidra-loader-relocation
so it stays out of the manual-verifier glob, docs moved to
docs/testing-lanes.md and docs/providers/ghidra-provider.md.
Co-Authored-By: gensokyo-dev <gensokyo-dev@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(ghidra): await installation inspection in the relocation verifier
inspectGhidraInstallation became async on main after #4198's base, so the
verifier asserted on a pending Promise. Also wait for provider output to
close before reading it, like the sibling Ghidra verifiers, and keep the
workflow reference inside the docs site.
Co-Authored-By: gensokyo-dev <gensokyo-dev@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(ghidra): address relocation review findings
- Version the existing-project analysis profile with the read-only
relocation semantics, since those sessions now add the limitation.
- Inspect the installation through the verifier helper that owns the
file-reader resources.
- Build the fixture with -fPIC: with -fPIE, GCC 12 emits a copy
relocation of rea_external_value into writable .bss, so the probe's
GOT check could not hold.
- Run probe cleanup through withVerifierCleanup so a cleanup failure
does not hide the probe failure, and keep the workspace when owned
Ghidra cleanup is incomplete.
- Link the workflow with a full GitHub URL, per the docs guide.
Co-Authored-By: gensokyo-dev <gensokyo-dev@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* test(ghidra): reach the external data through the GOT with a weak extern
Building the whole fixture with -fPIC also turned the reader's direct
load of rea_loader_pointer into a GOT-relaxed lea plus two loads, and
the real-Ghidra lane then reported that the baseline no longer folds
the file constant. Keep -fPIE and declare only rea_external_value weak:
GCC and clang must reach a possibly absent symbol through a GOT entry,
which lands as R_X86_64_GLOB_DAT inside GNU_RELRO (checked with readelf
for GCC 12.2 and clang/lld).
Co-Authored-By: gensokyo-dev <gensokyo-dev@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(ghidra): protect only relocations Ghidra could not apply
Running the real-Ghidra lane locally (Ghidra 12.1.4, JDK 21, GCC 12)
showed that marking every non-writable block that holds a relocation
volatile also marks full-RELRO .got. External calls then decompile as
opaque pointer calls (`(*pcVar2)(param_1)` through
`PTR_rea_external_...` instead of `rea_external(param_1)`), which the
probe's GOT-stability check caught.
Ghidra applies GLOB_DAT and RELATIVE relocations, so those bytes already
hold the loader's value and folding them is correct. The #4177 case is
a copy relocation, which Ghidra imports as UNSUPPORTED with the file's
zero in place. Protect only destinations whose relocation status is not
APPLIED or APPLIED_OTHER.
The fixture now reads a provider `const` object that the linker copies
into .data.rel.ro, the #4177 shape. The probe requires that relocation
to be unapplied and requires applied GOT relocations to stay
non-volatile. Ghidra 12.1.4 already shows this x86-64 read by name
without protection, so the probe records whether the baseline folds and
compares before and after only when it does. The profile value becomes
volatile-unapplied-relocation-blocks-v1.
Co-Authored-By: gensokyo-dev <gensokyo-dev@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Arslan Basharat <arslanbasharat414@gmail.com>
Co-authored-by: gensokyo-dev <gensokyo-dev@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Ghidra's Non-Returning Functions - Discovered analyzer can mark a
returning callee, such as an imported operator new thunk, as
non-returning. Ghidra then clears the fallthrough at each call site and
refits the callers, so a constructor decompiles to a few plausible
lines. REA reported the terminal call with the FunctionManager flag but
did not say the result omits the code after it or how to compare.
That analyzer bookmarks each entry it marks with the Analysis comment
"Non-Returning Function Found" (Ghidra 11.0 through 12.1.4 source); the
name-list Known analyzer writes "Non-Returning Function Identified".
When a terminal call's callee, or the function a thunk forwards to,
carries the Discovered bookmark, the call's limitation now says the
flag was inferred, and the function adds one address-free limitation
that its body and pseudocode end at that call and omit the following
code if the callee returns, with the REA_GHIDRA_DISABLED_ANALYZERS
comparison. A flag without the bookmark keeps an unknown origin.
The function-boundary observation contract moves to v5 so snapshots
from earlier bridges are not replayed without the qualification.
Refs #10365
Co-Authored-By: gensokyo-dev <gensokyo-dev@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Ghidra profile fixture simulates a Linux host, so posix.isAbsolute
refused the C:\ install directory a Windows test run supplied. The
fixture now owns a POSIX-shaped install directory with shared ownership
and cleanup, used by the DOL, MIPS, PowerPC, RISC-V and language
override suites.
Squashed from #9284 onto current main.
Co-Authored-By: gensokyo-dev <gensokyo-dev@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Explicit deadlines at or below the project deadline only shortened it:
a literal 30-second value also disabled the coverage scaling, which timed
out wide-capacity regressions on main. Workloads above the built
deadline use the long-running tag. Deadlines derived from an operation
budget remain.
Co-Authored-By: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* docs(native): specify Windows ReFS filesystem authority
* docs(native): plan ReFS filesystem support
* feat(native): admit ReFS filesystem identities
* feat(native): support ReFS filesystem authority
* feat(native): advertise ReFS Windows support
* docs(native): format ReFS support documents
* docs(native): record ReFS verification tasks
* docs(native): record ReFS draft pull request
* docs(website): correct remaining ReFS translations
* fix(docs): correct Windows native guide link in ReFS plan
---------
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
A malformed migrated launch seed stays on the config and the operation view reports configuration_invalid unless a snapshot override is active. Kuna, regex, GDB, and Rizin keep the deadline or heap accepted when the work started, and reattributed resource errors copy the structured limit.
Callers read update_mode to see whether update_analysis_settings can change a limit, and pending_restart only when a configured value is not active in this process.
* fix(native)!: enforce paired Swift literal coverage in shared dossiers
BREAKING CHANGE: Retained function dossiers containing only one Swift inventory are rejected. Regenerate observations through a canonical provider; both-absent unsupported observations remain valid.
* fix(contracts): advertise paired Swift literal coverage
* fix(providers): reject malformed UTF-8 bridge responses
* fix(providers): release a drained NDJSON frame's backing buffer
A response that ends on its newline left an empty view of the whole
concatenated allocation. Provider clients keep the line buffer for the
session, so that frame stayed resident. Replace an exhausted tail with
an empty allocation and keep a real unconsumed tail.
---------
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
* test(ghidra): keep the bridge context contract out of an empty suite
Source tests collect src/ghidra/GhidraFunctionValues.test.ts directly.
The empty describe failed that file with "No test found in suite" even
though every shard passed. Assert the bridge bindings at file level.
* test(ghidra): retire superseded source-text context assertions
Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
---------
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: N0zoM1z0 <161784452+N0zoM1z0@users.noreply.github.com>
Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
* docs(desktop): clarify gzip scrub limits
* fix(setup): own desktop build process cleanup
* fix(doctor): compare desktop settings with resolved defaults
* fix(desktop): refuse a build runtime whose export takes no task ends
Several tasks pass --task-end to the build computer's export, so a runtime
packed before it now fails at the start of the capture with the repack fix,
as a runtime without synthetic seeds already did, rather than at export.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* style(desktop): format the noisy-record draft files with the pinned oxfmt
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* test(desktop): give the root-array draft test its own describe
The records describe grew past the 150-line lint limit with it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(doctor): inspect client-recorded desktop runners
* fix(doctor): reuse matching runner probes
* fix(setup): propagate cancellation to desktop actions
* fix(desktop): rewrite a download's launch keys written with spaces around =
The desktop entry parser accepts `Exec = notes %F` and an indented
`TryExec =notes`, so the install script now matches those keys the same way
instead of leaving the original program in place. Adds the feature's tasks.md.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(desktop): keep why a measured run failed in its solve record
A solve that returned an outcome dropped the runner's error, so a run that
failed to deploy, solve or verify was retried with no reason recorded. The
record, the measure result and difficulty.json now carry it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(setup): stop Hopper on cancel, never report a cancelled setup ready, and keep a failed action's client setting
- The Hopper install gets setup's signal: Linux already honoured it, and the
macOS installer now stops its downloads and stops before mounting or copying.
- runSetup checks the signal after the final doctor and close, so a
cancellation there raises instead of returning ready.
- A failed pack or agentenv install no longer writes its archive or runner to
client configs, so the setting the client already had stays.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(setup): keep the completed setup result in a cancellation after cleanup
A cancellation during the final doctor or host close carried only the applied
actions, dropping each client's result and backup path and the doctor report.
It now carries the whole result as setup_result, as doctor's cancellation
carries its report.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* test(desktop): move launch execution to boundary suite
* fix(desktop): redact build schema failure output
* refactor(desktop): normalize schema failure once
* fix(desktop): omit unsafe schema parser snippets
* fix(desktop): preserve draft change and selected path evidence
* test(desktop): group retained dconf note regressions
* fix(desktop): respect archive entry format in draft checks
* test(desktop): separate unowned-file draft note cases
* fix(desktop): preserve explicitly selected journal documents
* fix(xml): avoid scanning selected ancestors per tag
* fix(desktop): exclude covered text runs from draft counts
* fix: preserve text when drafting desktop checks
* fix: restore nested desktop state aliases
* refactor: share UUID recognition across draft checks
* fix: retain multiline XML attribute context
* fix: retain relationship identifiers in task drafts
* docs: clarify desktop draft identifier handling
* test(desktop): type formatting run cases as pairs
* fix(desktop): carry document format into selector evaluation
* test(desktop): separate markup boundary regression group
* fix(desktop): propagate formats through draft selectors
* fix(desktop): switch selector format within archive entries
* test(desktop): remove duplicate seed filename case
* fix(setup): retain diagnostics when cancellation overlaps failure
* refactor(setup): keep command cancellation at its owner
* test(setup): verify cancellation at the application boundary
* test(desktop): pass JSON format in exported index parity
* docs(desktop): clarify task checks before scratch export
* fix(desktop): respell selected paths in file URLs
* test(desktop): group captured document spelling cases
* test(desktop): run installer scripts at boundary
* style(desktop): remove trailing blank line
* fix(desktop): bound draft preview serialization
* fix(desktop): restore selected state aliases
* fix(desktop): respect entry format in draft noise
* test(desktop): match exported alias evidence at its JSON boundary
* fix(desktop): retain app state logs in exported seeds
* fix: align desktop numeric seed comparisons
* test(desktop): keep one draft exclusion fixture import
* test(desktop): retain log eligibility across draft test groups
* fix(doctor): resolve empty registered desktop runner
* style: fix desktop launch test formatting
* style: format native command path schema
* fix(desktop): map selected external launch paths
* style(desktop): format seed document regressions
* test(desktop): group launch URL regressions
* fix(desktop): omit redundant nested state aliases
* fix(desktop): compare added free text as a whole
* fix(desktop): check recompressed seeds as shipped
* docs(desktop): clarify recovered seed format
* test(desktop): group shipped gzip seed checks
* fix: retain quoted identifier text in task drafts
* fix: recognize Unicode XML in desktop state readers
* fix(desktop): bound nested draft checks
* fix(desktop): explain missing arrays in truncated views
* fix(desktop): find XML type references in archives
* test(desktop): remove duplicated capture assertions
* fix(desktop): retain nested omitted text boundaries
* style(desktop): format draft value imports
* test(desktop): group model and draft value checks
* fix(desktop): map captured launch paths once
* refactor: remove unused whole-text guard state
* refactor: stop passing unused text children
* test: align free-text checks with whole-note policy
* fix: validate recompressed desktop archive seeds
* style(desktop): format archive seed regression
* fix(desktop): retain placed-item sibling guards
* fix(desktop): preserve archive paths and bound doctor reads
* fix(doctor): refuse non-files before descriptor admission
* fix(setup): track remaining cleanup resources across retries
* test(desktop): specify XML format in heap pairing fixture
* fix(setup): skip Hopper launch after cancelled copy
* fix: preserve task measurement limits and cleanup
* fix(desktop): retain reported solve sandbox cleanup
* test(desktop): check retained log seed admission
* fix(doctor): query agentenv for model endpoint
* fix(doctor): preserve uncertainty for empty endpoint override
* fix(desktop): redact runtime schema errors
* fix(desktop): normalize captured file URLs
* fix(desktop): report seeded files by capture path
* fix(desktop): retain startup readiness failure stage
* fix(desktop): retain JSON date checks
* fix(desktop): preserve archive formatting checks
* fix(desktop): preserve explicitly selected journal files
* fix(desktop): report build command transport limits
* docs(desktop): clarify unreadable dconf versions
* test(desktop): remove duplicate history regression
* fix(desktop): align XML names with exported reader
* fix(desktop): create selected state alias targets
* fix(desktop): inspect supported runtime archives
* fix(desktop): keep ordinary text checks exact
* fix(desktop): verify registered build host paths
* fix(desktop): retain record identity in draft checks
* test(desktop): preserve text reference numbers
* fix(desktop): bound download install input reads
* fix(desktop): match placed notes by whole text
* test(desktop): match multiple task export results
* fix(desktop): retain failed measurement file closes
* fix(setup): retain completed actions on cancellation
* test(setup): retain cancelled installer result
* fix(desktop): scrub XML independently of reader compatibility
* fix(desktop): preserve file aliases and reader diagnostics
* style(desktop): format review fixes
* fix(desktop): inspect archives with registered runner
* test(desktop): verify registered archive interpreter
* style(desktop): format capture review fixes
* style(desktop): format review changes
* feat(desktop): target agentenv-framework 0.9.1464
Desktop bundles, setup's --install-agentenv and the doctor's exact-version
check now target 0.9.1464, which keeps an exported app's data/get call,
holds the computer fixture above boot dialogs, leaves an unresolved setting's
build unverified rather than failed, and mounts lock packages instead of
copying them into the image. The web environment lane stays on 0.9.1431.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(desktop): retain per-solve sandbox cleanup roots
* test(desktop): expect the solve cleanup helper among the shipped agentenv helpers
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat(desktop): clean up a stopped solve's sandboxes through agentenv 0.9.1482
agentenv-framework 0.9.1482 lists the local sandboxes under a root
(local_sandbox_ids) and rebuilds one killed while it wrote its port map for
teardown. The solve cleanup helper now terminates each sandbox agentenv
lists, keeping its Docker and host-process checks, and reports anything else
left under the root. Desktop bundles, setup's --install-agentenv and the
doctor's version check target 0.9.1482.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(desktop): verify a killed solve's cleanup once its sandbox root is empty
A solve's sandboxes are all local under its private root, and agentenv now
keeps a sandbox's folder until its teardown is confirmed. So when the helper
ends without a report and REA's cleanup empties that root, the cleanup is
verified: a cancelled solve ends as a cancellation and a solve past its
deadline as a failed run, rather than as cleanup the measure cannot vouch for.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat(desktop): target agentenv-framework 0.9.1485
0.9.1485 keeps a local sandbox's folder until its teardown is confirmed, which
the solve cleanup's verified result relies on. (0.9.1484 failed to publish.)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(desktop): record a solve stopped before it reports as failed, not as a refused request
A helper killed from outside or stopped at its deadline left no report, so the
measure refused with invalid input and told the caller to fix its model
endpoint. A helper a signal ended now gives a failed solve of unknown cost,
which the measure records and stops on; one that exits on its own without a
report still ends the measure as a solve that could not run.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(desktop): check verifier behavior after selector changes
* test(windows): verify rejected writes during cleanup by effect
* fix(desktop): preserve selected alias target kinds in exports
* fix(cli): retain caller capture paths in environment builds
* fix(ghidra): classify oversized endpoint records consistently
* test: align model and Evidence expectations with current contracts
* fix(desktop): reconcile upstream sandbox cleanup recovery
* fix(docs): link cleanup qualification as repository evidence
---------
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* fix(cli): record the caller HAR path in environment-build evidence
environment-build inspected a HAR without the caller path that
inspect-web-network-capture records, so the published evidence.json
omitted caller_input_path and failed the byte-for-byte portable check.
Also update three stale portable expectations that already fail on main:
the no-reader verifier digest matches the #9185 selector parent, uncapped
IDA callee pages report complete discovery, and a CLI refusal of a
malformed artifact inspection includes the undelivered source closure.
* fix: restore portable docs, alias, offset, and Windows cleanup checks
The witness page linked task anchors that were never written, so any
docs-touching PR failed the route check. A selected state directory was
restored as a dangling symlink by a duplicated helper. An empty
safetensors file reports the extent as outside the file before offset
conversion. Windows cleanup refusal uses the runtime's in-progress message.
* fix(cli): keep the caller's HAR spelling in environment-build evidence
inspect-web-network-capture records the path the caller typed. The
one-shot build had already absolutized that path, so a relative capture
disagreed with the three-step bundle. The byte-for-byte check now runs
from the capture directory and requires caller_input_path grafana.har.
* fix(desktop): create alias targets only for directory selections
A missing settings file was created as a directory because the helper
treated any absent path as a directory. The capture's selected path kind
now travels with the alias, and only a directory alias creates its target.
An absent file alias stays a file the app can create.
* fix(desktop): keep legacy alias kind unknown
Captures written before selected_paths.kind leave the alias unknown.
Defaulting those to a directory makes an omitted settings file a
directory, so a later write through the alias fails with EISDIR.
* fix(desktop): keep alias kind a file, directory, or unknown
The export plan was widening a missing selected path kind to string.
Record file or directory only when the capture says so, and unknown otherwise.
* fix(desktop): narrow a missing alias kind to unknown
The selected path kind was widened to string, so the export plan could
not keep the file, directory, or unknown distinction.
* test(desktop): expect alias kind in the rendered app module
The export now records whether a selected alias is a directory. The
rendered STATE_ALIASES tuples include that kind.
* fix(ghidra): reject an oversized Windows TCP endpoint as invalid
The POSIX read maps a record past the compact endpoint bound to
"Ghidra TCP endpoint is invalid". The Windows lease reported that same
bound as a generic observation failure, so an expanded endpoint failed
the Windows lane with a different message.
* fix(desktop): create a legacy alias when a child proves it is a directory
A capture written before selected path kind can still nest one alias
inside another. That parent is a directory. A legacy leaf stays unknown
so a missing settings file is not created as a directory.
* fix(desktop): restore selected aliases when the app owns no files
An empty state directory does not become an owned path, so the class
was ownless and never called _ensure_state_aliases. The ownless class
now restores aliases whenever the export recorded some.
* fix(desktop): create the parent of a missing file alias
A file alias must stay a file. When its parent directory was not seeded,
the symlink was dangling and a write through it failed with ENOENT.
* ci: retrigger checks after an unrelated Windows timeout
The Windows lane timed out in webModuleCli, which this diff does not touch,
and the rerun API is unavailable.
* test(evidence): preserve complete CLI refusal diagnostics
---------
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: gensokyo-dev <gensokyo-dev@users.noreply.github.com>
* fix(call-path): keep computed-call targets out of complete callee coverage
Ghidra reported complete callee coverage when a computed call had one
ReferenceManager target, so trace_call_path, build_call_path and the
function comparisons turned the static candidate into a definite negative.
The bridge now keeps those targets as observed callees and marks callee
coverage partial, naming the computed call sites. IDA callee reads cannot
classify their call sites, so their callee coverage is partial too. The
Ghidra call-coverage profile revision refuses older snapshots.
A source-owned x86-64 bridge probe in the ghidra-bridge-sources job checks
the slot, register and direct-call controls against real Ghidra.
Fixes#8277
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(ci): match the observed call coverage probe marker
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
* feat(windows): inspect Shell Link (.lnk) shortcut files
Add inspect_windows_shortcut / inspect-windows-shortcut to read one
local Shell Link without resolving or executing the target. The report
keeps header flags and FILETIMEs, IDList items, LinkInfo volume and
network names, StringData, ExtraData including Tracker droid facts
labelled derived, and derived observations such as argument length
and a minimized ShowCommand.
* fix(windows): tighten Shell Link string reads and coverage
- Decode counted StringData over its full CountCharacters payload.
- Locate LinkInfo string terminators before charging max_string_bytes
and before decoding; an unterminated string is a coverage issue.
- Mark an IDList without a TerminalID and an out-of-range
LinkInfoHeaderSize as partial coverage.
- Report arguments_present and argument_length as null when
HasArguments is set but the field could not be read.
- Derive target_extension from the joined base path and suffix.
- Keep ISO dates for FILETIME and UUIDv1 timestamps before 1970.
- Retain the completed file snapshot on parse failures.
- Reject an empty CLI path before normalization.
- Select the shortcut contract from its family in focused tests.
- Fixture StringData no longer writes a NUL into the counted payload.
* style: format docs index and Shell Link boundary type
Apply oxfmt to the two files format:check reports so Static checks passes.
* fix(windows): reject network share paths for Shell Link reads
inspect_windows_shortcut is advertised as offline, but the Windows path
schema admits UNC paths, and lstat on one can contact an SMB server and
offer the caller's credentials.
Reject UNC, extended UNC, and other host-prefixed device paths in the
provider before any filesystem call. Extended drive paths such as
\\?\C:\x stay allowed. A mapped network drive cannot be told from a
local drive by its path, and the docs say so.
* fix(windows): decode Unicode Shell Link file-entry names
File-entry items with bit 0x04 of the type byte, such as 0x36, store a
UTF-16LE name, but the parser always read it as Latin-1. The read stopped
at the first zero byte, so report.exe became r and an IDList-only
shortcut reported the wrong target_extension. Read those names as
UTF-16LE.
* fix(windows): read volume labels by exact Shell Link discriminator
VolumeLabelOffset 0x14 selects VolumeLabelOffsetUnicode and is not itself a
label offset, and that Unicode field exists for no other value. The parser
treated any offset of 0x14 or more as having both, so a Unicode-only label such
as "OS" also produced a false ANSI label "O", and an ANSI label stored past 0x14
had its own bytes read as a Unicode offset.
Branch on the exact value in parseVolume, the one place that interprets it, and
report a VolumeID too short to hold the selected field. The fixture wrote both
encodings for a Unicode volume, which hid the false label, so it now writes one.
* fix(windows): limit network share rejection to Windows hosts
POSIX hosts read repeated leading slashes as one local root, so an
accepted selection such as ///tmp/shortcut.lnk is a local file there.
Apply the host-prefixed path check only on Windows, and take the
platform through the provider constructor so the Windows path forms
stay covered on every test host.
* fix(windows): compare shortcut icons only against full paths
A stored IDList filename or relative path cannot show that an icon at a
full path is elsewhere, so the comparison reported a false difference.
Report null unless the icon and a recorded target are both full paths,
and assert the missing TerminalID issue by code and offset instead of
its message.
* fix(windows): keep unread ExtraData blocks unknown
A walk that stopped before or inside ExtraData still reported the blocks
that HasExpString and HasExpIcon expect as absent, though they were never
checked for. Report a block as absent only after the TerminalBlock shows the
section was read in full. Without one the block stays unknown and coverage
stays partial.
* fix(native): ignore LNK network provider type unless ValidNetType is set
CommonNetworkRelativeLink NetworkProviderType is only meaningful when the
ValidNetType flag (0x2) is set. Return null otherwise.
* fix(native): accept any ExtraData TerminalSize below 4 as the terminal
MS-SHLLINK requires TerminalSize to be less than 4, not exactly 0. Treat
1, 2 and 3 as the terminal block instead of a malformed block.
* fix(native): derive shortcut target facts from the stored environment target
Include the EnvironmentVariableDataBlock literal target among the target
candidates for target_extension and the icon comparison. Variables are not
expanded and the target is not opened.
* fix(windows-shortcut): keep unexpanded variable paths unresolved in icon comparison
A stored environment target such as C:\Users\%USERNAME%\app.exe could be
compared as different from C:\Users\Alice\app.exe. Variable-bearing paths
are never expanded, so the icon comparison now returns null unless a fixed
target matches the icon exactly.
* test: drop redundant ok assertion before typed error guard
The testing guide asks for the typed error to be thrown before asserting a
successful Result value, and says a preceding .ok assertion adds no coverage.
* fix(windows): point out-of-range LinkInfo offsets at their field
A LinkInfo, VolumeID or CommonNetworkRelativeLink offset that points
past its structure was reported at base + offset. With an offset such
as 0xffffffff that position exceeds uint32, so the report failed its
schema and the service discarded the whole partial result.
Report such issues at the offending offset field instead, which keeps
every issue offset and coverage.failing_offset inside the file. Cover
each LinkInfo offset field in the parser tests and the
LocalBasePathOffset case through the stable filesystem service.
* fix(windows): require RFC 4122 variant before deriving Tracker UUID facts
A version nibble of 1 alone does not identify a version-1 UUID. Also require the clock_seq_hi_and_reserved top bits to be 10, so other GUID layouts keep their raw value and derive no MAC or timestamp.
* fix(windows): ignore DeviceNameOffset fields unless ValidDevice is set
MS-SHLLINK 2.3.2 says DeviceNameOffset and DeviceNameOffsetUnicode are
ignored when the ValidDevice flag is clear. parseNetwork read both
regardless, so garbage in those fields became device-name facts or
malformed_structure issues. Read the flags once and only read the device
name fields when ValidDevice is set.
* fix(windows): keep unsupported Tracker headers as raw data
Check TrackerDataBlock Length (0x58) and Version (0) before decoding.
A block with any other header is retained as raw bytes with a
malformed_structure issue, so it no longer yields MAC and timestamp
facts under complete coverage.
* fix(windows): compare shortcut icon paths by normalized location
Collapse "." and ".." segments and repeated separators in the icon
comparison as text only, never opening a path. ".." stops at the drive
or UNC share root, and \\?\ and \\.\ paths stay verbatim.
Join LocalBasePath with CommonPathSuffix as the target and use the bare
base only when the suffix is empty, as the network share branch does.
Read the selected shortcut with lstat and open only, so a rejected
symlink's destination is never resolved.
* fix(windows): keep the UNC share in the root after repeated separators
normalizePath took the host and the next split part as the UNC root, so
\\server\\share\..\app.exe kept an empty share and let .. remove the
real share. Drop empty parts before picking the root so paths on
different shares stay different.
* fix(cli): parse windows shortcut limits as decimals
The four inspect-windows-shortcut limit options now go through
cliDecimalField like the other inspection commands, so values such as
0x10 are rejected and cliDecimalArguments.test.ts passes.
* fix(native): reject linked parents of windows shortcuts
A parent directory symlink or junction pointing at a UNC share was
followed by the first lstat, since the network check reads only the
path text and the symlink rejection covers only the final component.
On Windows, lstat each ancestor from the drive root down and reject
the path at the first link, before anything follows it.
* docs(native): move Windows shortcut guide and add its feature spec
Move the Shell Link guide to docs/guides/native/windows-shortcut.md and
list it under Native binaries in the site navigation, keeping the
/windows-shortcut public route. Update README, translated README, CLI
page and authored skill links to the new physical path.
Add docs/specs/native/windows-shortcut/ with spec.md, plan.md and
tasks.md, as required by docs/development/documentation.md.
* fix(native): admit the windows shortcut parent walk
On Windows the ancestor link walk ran before the provider's resource
admission, so close() could finish while the walk kept calling lstat.
Run the walk inside the same admission as the read, and check the
caller's signal before each ancestor lstat.
* fix(native): walk the effective parents of windows shortcuts
The ancestor link walk checked every prefix of the path as written, so
C:\alias\..\real\a.lnk was refused when C:\alias is a directory link,
although Windows removes alias\.. before opening the file. Walk the
ancestors left after removing . and .. components, keeping the
caller's spelling, never climbing above an absolute root, and leaving
\\?\ paths as written since Windows does not normalize them.
* test(native): assert the linked parent as a structured fact
The parent link regression matched the phrase "junction, which" in the
refusal message, so a wording edit would fail it. Report the linked
ancestor as observed.linked_ancestor on the input issue and assert that
fact instead of the explanatory text.
* test(native): bound the shortcut close gate and release it in finally
Race the parent walk lstat acknowledgement against inspection settling
with awaitAcknowledgement, then release the gate and join the inspect
and close work in finally so a failure reports its cause instead of
waiting for the Vitest timeout.
* fix(native): walk Windows shortcut parents lazily
windowsAncestors built and kept every parent prefix before the first
lstat, so a path with thousands of components allocated memory
quadratic in its length. It now keeps only the normalized components
and yields each parent from a running prefix as the walk reaches it.
---------
Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
* fix(doctor): probe providers with the selected client's registration environment
rea doctor read provider settings only from the terminal running it. A client
registration that carries GHIDRA_INSTALL_DIR or REA_JADX_MCP_CONFIG under its
own env block therefore looked unconfigured: doctor answered not_configured
while the server that client starts inherited the settings and worked. This is
the recovery command the docs point agents at, so the false negative sent them
back to re-apply fixes that were already in place.
When the caller names exactly one client, read that registration's effective
environment and hand it to the provider checks. Terminal variables that
providers resolve outside server config (PATH, SYSTEMROOT) stay inherited, and
running doctor without --client still reports this terminal. A registration the
loader cannot read keeps its own registration check as the answer instead of
being re-reported as four missing providers.
Fixes#9102
* fix(doctor): keep setup's terminal settings and a failed close on the host
Greptile found two regressions in the selected-client environment read.
Provider probes now start from the terminal and overlay the selected
registration, instead of replacing the terminal with the registration
alone. `rea setup --client` passes its own client selection through the
same doctor call, so on a first run with GHIDRA_INSTALL_DIR exported and no
registration yet, the replacements made setup report providerConfigured:
false and offer Hopper over the selected BYO provider. The registration
still wins for every value it records.
The registration read now runs inside the caller's resource scope and, when
the close of the configuration file fails, retains the returned owner there
so host.close() retries it. Dropping that owner left an unretired handle
with no way to report the unfinished cleanup.
* fix(doctor): leave a registration's expression settings unknown
Greptile found the previous fallback filling a name the selected client
records as a Cordis expression with the terminal's value. `dsh` keeps such
a value as `!!js ...`, which is not a literal; the parser reports the name
in `unresolvedSettings`, and merging the terminal into it then made probes
certify a setting the client owns and this process cannot read.
Unresolved names are now removed after the overlay, and an unreadable,
malformed, or unresolvable registration returns no environment at all, so
the caller keeps the terminal instead of a partly-known one. Provider checks
report those settings as unconfigured, which is the honest answer for a
chain that resolves outside REA.
dsh's missing registration still inherits the terminal, which is the case
`rea setup` depends on.
* fix(doctor): fold registration settings by platform identity
`selectedProviderSettings` overlaid a selected client's registration on the
terminal with an object spread. On Windows a registration can record
`ghidra_install_dir` while the terminal exports `GHIDRA_INSTALL_DIR`; the spread
kept both spellings, and since provider checks read the canonical uppercase
names, doctor either certified the terminal's install or reported the setting as
unconfigured. An expression the registration marks unresolved was skipped under
one spelling while its terminal value survived under the other.
Merge the two layers through `mergeEnvironmentOverrides` and fold the result
with `snapshotEnvironment`, so the registration's value wins regardless of how
it spells the name and the provider sees the names it reads. Fold the unresolved
names the same way before deleting them, and export the helper so the two
platform identities can be covered directly.
* style(doctor): wrap the long import in the runtime test
oxfmt wants the three imported names on their own lines; the previous
commit left the single-line form, which fails `npm run format:check`.
* test(codebuddy): construct the registration command like its sibling clients
`npxRegistrationCommand` was removed in c77735fa ("fix(setup)!: bind managed
registrations to selected installation"). The CodeBuddy boundary test landed
after that refactor and still imported it, so `npm run typecheck` failed on
every branch that descends from merge fb841a95 — main included, where the
main push CI is still queued.
Use `currentClientRegistrationCommand()` the way the kimi, lmStudio and other
client configuration tests already do.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* fix(doctor): retain failed registration file cleanup
* fix(doctor): preserve unknown client provider readiness
* fix(doctor): scope Kuna readiness to its environment
* test(doctor): respect Kuna host support
* fix(doctor): read a registration's unresolved settings only where the probe needs them
Every setting an expression made unresolved blocked readiness, even one the provider never consults for the configuration the client supplied. With a literal JAVA_HOME and jar path, JADX resolves Java by absolute path and does not read PATH, yet a dsh registration with an expression-backed PATH skipped the probe and reported the provider unknown. Ghidra is the same when both GHIDRA_INSTALL_DIR and JAVA_HOME are explicit.
Decide per provider which unresolved setting the probe actually reads, and skip only those. Kuna's readiness list also omitted HOME and XDG_CACHE_HOME, which its cache root falls back to, so an unresolved cache input could let the probe check a fallback directory and report available without knowing the client's real cache.
* fix(doctor): stop probing a registration expression as a literal
Two findings from the review, both about handing a probe a value REA cannot interpret.
REA_KUNA_CACHE_DIR made settingAffectsProbe ignore every unresolved Kuna setting, so an expression-backed REA_KUNA_COMMAND or REA_KUNA_JOBS silently fell back to a default and the provider could report ready. Limit the exception to HOME and XDG_CACHE_HOME, the two cache-location defaults.
HOME and XDG_CACHE_HOME were also added to KUNA_DOCTOR_SETTINGS, which registrationEnvironment copies wholesale, so setup persisted ambient values a child already inherits. Split the readiness inputs from the settings setup writes.
Qwen expands $VAR and ${VAR} in a recorded env value from the environment it runs with. REA cannot predict that, but it was handing the literal to the probes, so a registration appending to PATH made JADX search for a literal ${PATH} entry. Treat such values as unresolved for that client so the affected provider reports unknown instead of probing the literal text.
* fix(registration): recognize a client's env expression at the shared reader
Doctor decided which Qwen settings were expressions after the shared registration reader had already parsed them as literals, so doctor and setup/status held two interpretations of the same file. AGENTS.md requires normalizing once at the owning boundary, so move the recognition into ClientRegistrationEnvironment and let every consumer read the facts it returns.
The reader now takes the client name, marks a variable-expanding client's $VAR and ${VAR} values as unresolved, and reports those names through unresolvedSettings. Doctor drops its own parser; setup and status pick up the same facts, which also keeps an expression-valued timeout out of the resolved budget instead of trusting the literal.
* fix(doctor): preserve unresolved typed settings
* style(cli): wrap the native data type path description
The `inspect-native-data-type` argument description line is 82 columns, over the repository's 80-column `.oxfmtrc.json` limit, so `npm run format:check` fails on main and on every branch merged from it. It came in with #9465 and the following commits to this file did not touch it. Reformat the one call the formatter objects to; no behaviour change.
---------
Co-authored-by: 2SinceJuly <202121119+2SinceJuly@users.noreply.github.com>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
* fix(ghidra)!: distinguish skipped overlays in byte search
BREAKING CHANGE: Byte search results require skipped_overlays; loaded overlays no longer appear in skipped_not_loaded. The Ghidra profile changes so prior snapshots are not replayed.
* fix(cli): explain overlay byte search scope
* test(ghidra): exercise overlay search cursor handoff
* test(ghidra): avoid freezing byte search help wording
* test(ghidra): classify search start as an address bound
* docs(ghidra): match the raw lane's overlay coverage
The raw Ghidra lane checks a manually created overlay block through MCP.
Say that, and do not claim an overlapping-load ELF, CLI search, or snapshot
cursor handoff.
---------
Co-authored-by: Cursor Agent <cursoragent@cursor.com>