These fixtures' descendants exit on their own after 20 or 30 seconds. The
shorter explicit deadline is what proves cleanup stopped them first.
Co-Authored-By: gensokyo-dev <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Registries created directly with createTestProcessRegistry did not get the
processTest default, so a hung child there had no deadline once its explicit
value was removed; Vitest's test timeout does not settle the awaited run, so
shutdown was never reached. Derive the default in the registry from the
running test instead, using the real clock.
Co-Authored-By: gensokyo-dev <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
239 literal timeoutMs values on cli.run and processes.run only restated
a guess at host speed. On hosted runners with four workers, cliOutput's
3-second value failed while the command still produced correct output.
Deadlines that are the behavior under test, derived from an operation
budget, or that bound a regression's wait remain.
Co-Authored-By: gensokyo-dev <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A processTest child run without its own timeoutMs now gets the test's
remaining deadline less a reporting reserve (at least 5 seconds, or a
tenth of the deadline). A hung child still fails with its captured output,
without a fixed per-call value that assumes a host's speed.
Co-Authored-By: gensokyo-dev <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The handler regression added in #10390 listed tools under the full input
profile and matched every advertised schema before calling each tool. It
took about 14 s locally and passed the 30 s deadline on loaded CI runners.
advertisedPatternsVMode.test.ts already proves that no input or output
profile advertises the surrogate escape, so this case now checks only the
runtime refusal through the default-profile handlers, as the
relative-path case does.
Co-Authored-By: gensokyo-dev <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
rea mcp --http printed "Choose another --port or stop the process using
it" for every listener failure except a missing host allowlist, so a host
lookup failure, an unassigned address or a permission denial got port
advice. StreamableHttpListenError already classifies the native failure
and owns a recovery for each kind.
Startup now always prints that owned recovery behind one shared lead-in,
and the fixed message is removed. The native cause text stays out of
stderr as before.
Fixes#7346
Co-Authored-By: gensokyo-dev <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Planning for win32 refuses PTY capture before root admission runs, so the
cancellation cases now plan for Linux on any host. The ENOTDIR admission
case skips on Windows, which reports ENOENT for a path below a file and
admits it as a root the scenario may create.
Squashed from #9306 onto current main.
Co-Authored-By: gensokyo-dev <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
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 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The snapshot identity test writes its file and admits it at once with the
default ChangeTime mode. On Windows that ChangeTime is inside the 2 s drift
window, so admission keeps re-reading the descriptor until it settles. The
returned baseline is then a later observation, not the one the test
captured, and the descriptor stat runs more than once.
Admit the fixture as written. That is the documented mode for a file that
was just written, and it reads the descriptor once on every host. Settling
keeps its own coverage in ChangeTimeBaseline.test.ts.
The shared reader lifecycle case waits for a member's writable side to
finish before it reads anything. That only happens while the member fits
below the readable high-water mark. Node's default is 16 KiB on Windows and
64 KiB elsewhere, and the ESP flash fixture's first member is a 32 KiB
bootloader region, so both cases hung until the 30 s timeout on Windows.
Pick the first nonempty member that fits the default buffer and assert
that it fits the opened stream before waiting.
* refactor(cli): run one-shot commands through one shared owner
scripts/rea.mjs and the test CLI session worker each implemented the
one-shot command sequence: argument normalization, output admission,
configuration errors, version documents, the Amp scope refusal, and
result delivery. The worker copy had drifted: it never rendered
formatted version reports or the Amp refusal.
Move the sequence into src/cliOneShot.ts. Both callers supply a host
for their streams, exit and resource ownership. The launcher keeps
closed-pipe status, worker shutdown and process exit; the session
worker captures output per command.
The cliOutput bundle entry is superseded and removed. The dispatcher
test now replaces only the command runtime, so output admission runs
production code. A new parity test compares the file session with a
fresh process.
* test(cli): bundle the one-shot entry in the runtime bundle fixture
The test bounded each hdiutil and CLI step with its own 10-30 s process
deadline. On hosted macOS runners the test takes 32-45 s (about three
times a local run), and the busy-image CLI call, which verifies the whole
image before its attach is refused, exceeded 30 s and was killed in two
recent main runs. Remove those per-step deadlines; the project deadline
still bounds a hang.
* test(mcp): open stdio clients through the shared local-tools fixture
Twelve files built their own StdioClientTransport and Client for
`rea mcp`. Those clients never listed tools, so the SDK did not
validate structured results against advertised output schemas, and each
file repeated transport setup, stderr draining and close ordering.
Use connectLocalToolsMcp, which lists tools, validates replies,
silences logging, leaves deep providers unconfigured and owns cleanup.
Per-test clients register test-scoped cleanup; module-level clients
collect cleanup for afterAll.
* test(mcp): keep failed file-scoped MCP cleanup owned
The three module-level clients removed every cleanup callback before
running them, so a failed cleanup lost its only handle. Add
fileLocalToolsMcp, which connects before the file's tests and removes
each cleanup only after it succeeds, and use it in those files.
* perf(mcp): build advertised tool schemas once per contract
The MCP SDK reprojects every registered tool schema on each tools/list.
REA memoized that projection, but per wrapper, and each server built new
wrappers for every contract, so each new server reconverted the whole
catalog: about 280 ms per tools/list for 255 tools with the full input
profile. Each wrapper also registered a new schema in zod's global
registry, so heap grew with every server.
Contracts are immutable module values, so build each advertised input
and output wrapper once per contract. tools/list on later servers drops
to about 26 ms (full profile) or 48 ms (compact).
* test(mcp): load only the contract family under test
* docs(ci): move dated shard measurements into a verification record
The testing guide and testing-lanes guide mixed the October 2026 shard
budget and stale-history measurements into current rules. Move them to
docs/verification/ci-shard-scheduling-2026-10-20.md and link it from
the rules they justify.
* docs(ci): drop dated shard measurements instead of keeping a record
After REA stopped a Rizin debug session (frame or output limit, deadline,
cancellation, protocol failure) or the debugger exited, later commands
returned a generic invalid_request and status gave no reason. GDB
collapsed the same states the same way.
Both managers now keep a published session addressable until
close_*_session releases it, record the first reason it stopped, report
it as closed_reason in session status, and refuse later commands with
capability_unavailable naming that reason and the close-then-restart
recovery. An ID the server never returned, or that close released, still
fails as invalid_request with its own recovery. Retained stopped
sessions keep their slot, which the capacity recovery now says. A GDB
command deadline now records its reason like cancellation already did.
Fixes#8024
Co-Authored-By: gensokyo-dev <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Rizin exits 0 when its shell refuses a statement, so execute_rizin_command
reported completion_status complete with exit code 0 and an empty stdout
that read as a genuine empty result.
Rizin 0.6 through 0.9.1 log exactly four refusal lines from
librz/core/cmd/cmd.c: an unknown command, a wrong argument count, an
invalid statement (RZ_CMD_STATUS_INVALID, which also stops the remaining
statements) and a parse error. The Rizin command profile now classifies a
completed process's stderr against those lines; the shared one-shot
service reports completion_status "rejected" and a limitation that names
the refusal and says stdout holds only earlier statements' output. Other
ERROR lines come from commands that ran and stay diagnostics.
Fixes#10364
Co-Authored-By: gensokyo-dev <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
`cliIntegerArgument` piped a regex-guarded string into a number but never
published Zod's input view, so `--schema` advertised a bare integer for 15
fields while the parser accepted only decimal argv text. `environment-serve
--port` had the same gap through the transforming MCP_PORT owner.
Remove `cliIntegerArgument` and route every call site through
`cliDecimalField`, binding each field to its domain or contract owner where
one exists so bounds, defaults and optionality come from that owner. Export
the input-view publisher as `publishArgvInput` and apply it to `--port`,
which keeps the MCP_PORT grammar shared with `rea mcp --http`.
The decimal catalog test now requires every numeric field's advertisement
to admit its decimal argv token, covers repeated options and
string-advertised token grammars, and checks `--schema` output directly.
The string branch's missing numeric range (#9348) is unchanged.
Fixes#9344
Co-Authored-By: gensokyo-dev <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Passing runs of the macOS artifact lane took 10.2-14.9 minutes (median
13.6) across recent runs, and 4 of 12 runs that reached their steps were
cancelled at the 15-minute limit without a failing test. Allow 25
minutes so runner speed alone does not fail the lane.
Co-Authored-By: gensokyo-dev <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Probes in a source and a built project read each collected test's
deadline, including test, suite, and module long-running tags, so a
change in project inheritance or tag resolution fails directly. Both
the policy and the probes read one set of base deadlines.
Co-Authored-By: gensokyo-dev <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Four wide-input regressions used a short per-test deadline as their only
check against a slow but terminating implementation. Assert the same
budgets explicitly so the project deadline only detects hangs. The
composed-body test keeps its exact read count instead.
Co-Authored-By: gensokyo-dev <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
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 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Both projection modes read the same 130,000-callable analysis without
changing it, so analyze the source once.
Co-Authored-By: gensokyo-dev <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Source projects allow 30 seconds and built projects 120 seconds, both
doubled under coverage, and hooks share their project's deadline instead
of Vitest's 10-second default. A long-running tag covers bounded
workloads that need more.
Co-Authored-By: gensokyo-dev <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* test(javascript): give the wide fingerprint regression a capacity deadline
Each case runs about 22 s of CPU-bound projection on a typical hosted
runner. Its explicit 30 s deadline failed on slower runners at both two
and four Vitest workers, and it also overrode the suite's 60 s
coverage-mode allowance.
* test(ci): double test deadlines on hosted runners
Hosted CI jobs run four Vitest workers on a four-core runner. CPU-bound
capacity cases then share every core with other workers and their
subprocesses; three such cases exceeded the 30 s deadline at four
workers (16.8-22.7 s at two). The deadline only needs to bound a hang,
so hosted CI doubles it: 60 s, or 120 s with coverage. Local runs keep
30 s and 60 s.
* test(ci): check worker and deadline policy for local and hosted runners
The policy test loaded vitest.config.ts in a child that inherited the
parent's GITHUB_ACTIONS, so on CI it observed the hosted deadlines and
failed. Select the runner explicitly and check both: two workers and the
base deadline locally, four workers and twice the deadline on hosted CI.
* test(ci): retire loader-ownership history seeding
Both timing tiers now publish javascriptLiteralLoaderOwnership.test.ts's
own duration (fast 23.7s, full 26.6s), so the seeding returned its input
unchanged. Remove it and its tests.
* docs(ci): describe shard weighting without retired loader seeding
Hosted CI jobs own a four-core runner but the suite ran on two workers.
Other hosts keep the two-worker bound because they are shared.
Co-authored-by: Claude Fable 5.2 <[email protected]>
The advertised-pattern guard only inspected the default profile and only
rejected the `\0` and `\u0000` NUL spellings, so a `\uD800` range shipped
in 6.9.0 unnoticed. Reject any unescaped `\u` or `\0` escape, and run that
check for each input and output schema profile.
Co-Authored-By: gensokyo-dev <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
rea-agents 6.9.0 advertised `^[^\uD800-\uDFFF]*$` on every local path
input. RE2 has no `\u` escape and Rust's `regex`/`jsonschema` crates refuse
non-scalar code points, so strict MCP clients and gateways (DeepSeek, RE2
gateways, Open Design) rejected the complete tools request and no REA tool
could run.
Move the rule into one runtime check, `rejectUnpairedSurrogates`, which
advertises nothing and still raises the `invalid_format` regex issue, so
CLI and MCP corrections keep reason, expected value, and message. The
objdump disassembly symbol already enforced the same rule with its own
refinement; it now shares the owner and the correction shape.
Dropping the pattern also brings inspect_android_jni_bindings within the
compact profile's per-schema budget, so compact delivery now keeps its
DEX dimension pattern. The descriptor admission test follows the actual
presentation instead of assuming compact always omits it.
Fixes#10238Fixes#10348Fixes#10198
Co-Authored-By: gensokyo-dev <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): preserve own state keys in task witnesses
* test(desktop): preserve witness parse failure details
* fix(desktop): retain own column keys in task conditions
* fix(contracts): drop nested defaults from a root union sibling
Flattening a union of strict object groups lifts each group's properties
into one object. A default the scenario group carried inside
normalization.rules survived that lift even though the passive group
declares no normalization, so a client that fills nested defaults sent
{normalization: {rules: []}} and the runtime parser refused the call
with "Unrecognized key: normalization".
Remove defaults below a property that any group omits, not just the one
on the property itself. A property every group owns keeps its own
nested guidance.
* fix(contracts): keep a nested default a reference still targets
Dropping the nested default of a property a sibling group omits also removed defaults that references point into. A resource whose validation $ref targets its own default was advertised with that reference intact but its target gone, so the advertised schema no longer compiled in AJV or client projection.
Collect the paths into a default that references inside the branch target and keep those entries while stripping the rest. Other nested defaults are still removed, so the strict sibling group keeps refusing the object.
* test(contracts): type the root union default regression fixture
The fixture spread the canonical branch through a widened cast, so the repository typecheck rejected the spread and indexed access. Narrow the branch shape and guard the optional schema before collecting its referenced defaults.
---------
Co-authored-by: 2SinceJuly <[email protected]>
* fix(swift): classify private nested types by their own kind
Skip swiftc's file discriminator before the nominal marker so a private
nested enum, struct, or class is not filed under its enclosing type.
* ci: retrigger checks stuck in the Actions queue
The original workflow runs stayed queued while later runs on other
pull requests completed.
---------
Co-authored-by: morluto <[email protected]>
Admission compared table offsets with the class minimum, so a larger e_ehsize still parsed those header bytes as program or section entries.
Co-authored-by: Cursor <[email protected]>
CSP3 requires a complete base64-value, so truncated and illegal nonce
and hash tokens were being reported as real sources.
Co-authored-by: Cursor <[email protected]>
* feat(setup): support Cline as a client
* test(setup): cover Cline doctor and uninstall ownership
* docs(setup): document Cline support
* test(setup): verify Cline against an installed client
* test(setup): include Cline in the exhaustive client detection list
* docs(setup): note that a hand-written rea entry blocks Cline setup
* fix(setup): isolate Cline verification and await MCP discovery
* fix(verification): own and isolate Cline discovery
* fix(setup): preserve native Cline transport settings
---------
Co-authored-by: morluto <[email protected]>
cctools lipo prints CPU_TYPE_I386 and CPU_SUBTYPE_I386_ALL for i386 slices, so their CPU codes were unknown and every i386 slice carried unnormalized-text limitations.
Co-authored-by: Cursor <[email protected]>
Co-authored-by: morluto <[email protected]>
* fix(hardening): recognize the __chk_fail FORTIFY handler import
glibc exports __chk_fail as the shared failure handler that most FORTIFY
checks funnel into, and its dynamic import is a positive sign that the
binary uses FORTIFY. The ELF FORTIFY matcher accepted __fortify_fail and
__*_chk names, so an image whose only relevant import was __chk_fail was
reported as fortify: unknown with no evidence location.
* style(hardening): wrap the __chk_fail fixture call to the formatter's width
format:check fails on the current head because the new fixture call fits on one line at the file's previous width but exceeds oxfmt's 80-column limit with the longer fortifyName literal.
---------
Co-authored-by: 2SinceJuly <[email protected]>
Co-authored-by: morluto <[email protected]>
parseCaptureJson retained the strings jsonc-parser 3.3.1 builds fragment by
fragment around each escape, which V8 keeps as ropes several times larger
than their text. Escape-heavy HAR captures therefore exhausted the 192 MiB
decoder heap from about 15 MB, well inside the 32 MiB input budget.
Each string token is now decoded with JSON.parse from its accepted source
span, which yields the same value as one flat string. jsonc-parser reports
malformed strings through onError before the value callback, so JSON.parse
only sees tokens it already accepted. HAR, qlog and Playwright trace
decoding share this parser.
Fixes#10132
An unavailable session is a typed invalid_request. The CLI treats that
projection as an operation failure and exits 1.
Co-authored-by: Cursor Agent <[email protected]>
* fix(native): list ELF initializers of images with COPY relocations
inspect-native-initializers reported every init_array and fini_array slot
of a non-PIE executable as unresolved, and ordinary PIE executables as
incomplete, when the image carried a COPY relocation such as the one for
stdout.
The shared dynamic relocation reader had no write extent for COPY or TLS
descriptor relocations, so one such record made pointer coverage unknown.
It now sizes a COPY from the image's own dynamic symbol and a TLS
descriptor as two words. COPY sizes are accepted up to the file byte count
in total, so repeated records cannot multiply consumer work.
The section relocation fallback also treated a record whose destination
has no file bytes, as COPY into .bss does, as an unread table. A linked
image's record now affects only the slots it overlaps in memory.
Co-Authored-By: Claude Fable 5.2 <[email protected]>
Claude-Session: https://claude.ai/code/session_01YT2WLf5G9AnLKF3bZtNLwf
* test(native): cover a COPY relocation against symbol index 0
Co-Authored-By: Claude Fable 5.2 <[email protected]>
Claude-Session: https://claude.ai/code/session_01YT2WLf5G9AnLKF3bZtNLwf
* fix(native): size section COPY records and keep write extents opt-in
Address two review findings on the COPY relocation change.
The shared dynamic relocation reader yields COPY and TLS descriptor
records as writes only to a consumer that asks for whole extents. The
initializer walk asks. IL2CPP and the Android JNI overlay do not, so
both see these kinds as not decoded, exactly as before this branch.
IL2CPP finds unknown bytes by looking one pointer back from a read, so
a wider write of unknown value was read as stored zeros.
The section fallback takes a COPY record's span from the symbol its
table links to instead of assuming one word. A table counts as not
fully read when that size is not established, or when a record of
unknown extent writes memory that has no file bytes.
Co-Authored-By: Claude Fable 5.2 <[email protected]>
Claude-Session: https://claude.ai/code/session_01YT2WLf5G9AnLKF3bZtNLwf
* refactor(native): size section COPY records in one helper
Move the symbol lookup, the allowance and the unread-table note out of
the record loop, so the loop only asks for the extent a record is known
to write. Behavior is unchanged.
Cover the allowance across two section records, and keep the new test
code within the lint thresholds the base already meets.
Co-Authored-By: Claude Fable 5.2 <[email protected]>
Claude-Session: https://claude.ai/code/session_01YT2WLf5G9AnLKF3bZtNLwf
---------
Co-authored-by: thunderq10 <[email protected]>
Co-authored-by: Claude Fable 5.2 <[email protected]>
fix(cli): batch of small fixes for #9714, #9718, #9759, #9856 and #9809#9714: replace bare z.string().min(1) local paths in ADB CLI commands with localPathStringSchema so NUL bytes are rejected at the CLI boundary.
#9718: decode-android-resources path uses localPathStringSchema, matching apktoolInputSchemas.
#9759: inspect-sqlite-database CLI options now reject an empty column array and reject rowLimit/rowOffset/column/schemaScope=selected_table without --table via superRefine, mirroring inspectSqliteDatabaseInputSchema.
#9856 and #9809: shared snapshotSelectionOption is now localPathStringSchema.optional(), so every analyze/inspect/decompile/xrefs/trace/function/instructions/search --snapshot rejects NUL bytes.
Tests: tests/boundary/cli/overnightBatch1CliSchemas.test.ts (15 tests) proves empty/NUL input returns VALIDATION_ERROR before any provider runs and valid input is admitted.
Co-authored-by: morluto <[email protected]>
* feat(audit): report what a shipped app exposes to static analysis
Adds `audit_release_exposure` and `rea audit-release-exposure` to report
source maps, Electron fuse implications, extractable payloads, debug
information, build metadata, and credential markers. Checks carry stable
IDs, observations, statuses, and fixed remediation, while incomplete and
unassessed checks remain visible without implying a security verdict.
Credential findings include only rule IDs and locations. The CLI
supports `--fail_on` severity thresholds and exits 3 when an exposed
check meets the threshold.
* fix(audit): keep incomplete coverage unknown across text, fuses, Go flags and cancellation
Probe files with unrecognized names as text, keep undecided fuse slices
unknown, forward opaque container coverage, read paths from Go build flags,
and compare inventoried digests before accepting owner facts.
Keep typed cleanup failures and attach the partial report when a later owner
is cancelled. Pin the verifier's @electron/fuses version and move the tests
to the named contract family.
* fix(audit): match package members by source spelling and close remaining coverage gaps
Reopen package members by their source spelling and keep caller path
traversal in directory reads. Parse quoted and attached Go build flags, bind a
native image fuse read to its own digest, and keep reader resources and the
audit's partial report when cleanup or cancellation combine failures.
* fix(release-exposure): bind package members by ordinal and follow named map references
Package members were matched to container entries by sourcePath, which is
not unique in a PyInstaller executable, through a queue that only moved when
a member was reopened. Bind by container-relative path and duplicate ordinal,
the numbering inventory uses, so read order cannot change which bytes a
member gets.
A sourceMappingURL naming a file without a map extension was ignored. Local
targets are now examined as maps whatever their name, and a target already
scanned as plain text leaves coverage unknown.
* fix(release-exposure): match data: source map scheme case-insensitively
URL schemes are case-insensitive, so an inline declaration such as
//# sourceMappingURL=DATA:application/json;base64,... was routed to the
local-reference path, which ignores scheme URLs. Its embedded sources were
silently dropped and source_map_sources reported not_observed.
* fix(release-exposure): normalize declared map targets and count unreached ones
A sourceMappingURL that decodes to a decomposed name (cafe%CC%81.json)
never matched the inventoried member, whose logical path is NFC, so the
map was scanned as ordinary text and its sourcesContent went uncounted.
Map-target lookups now use an NFC key on both the record and lookup
side; the declared URL is left as written.
A declared map target that is never reached is now counted as a skipped
map, so the source-map checks read as unknown coverage instead of
not_observed.
* fix(release-exposure): recover indexed leaves after any map format failure
An indexed map whose one leaf has malformed mappings (such as AA) threw a
format failure that only recovered leaves for unsupported sections, so a
valid sibling leaf with sourcesContent was dropped and
javascript.source_map_sources read unknown instead of exposed. Leaves are
now recovered for any format failure, with the envelope parse guarded so
invalid JSON still falls through to the unparsed count.
A nested package that inventory recognized but did not expand was probed
as a plain binary and left the source-map checks at not_observed. It now
marks coverage unknown and records a limitation.
Reader tests assert the javascript.source_map_sources status for the NFC
map-name cases as well.
* fix(release-exposure): count an unexpanded nested package as a readable member
An unencrypted nested package that inventory recognized but did not
expand is still a member its parent package already read. Count it as
readable so packaging.extractable_payload reports exposed, while its
own contents keep coverage unknown.
* fix(release-exposure): bound the indexed leaf fallback by decoder limits
The fallback walk after a map format failure expanded every section and
built a full ancestor path per leaf, so a map refused for nesting depth
could still allocate far past the decoder budgets. Walk only to the
64-level section depth and the shared record budget, keeping leaf maps
without materializing section paths.
* fix(release-exposure): collect UNC roots from Go flag paths
A flag such as -I\\buildhost\sdk\include matched none of the path roots
in GO_FLAG_PATH, so a network path that was the only one in complete
build info left go.build_paths at not_observed. Accept \\host\share
roots alongside the slash-rooted, dot-relative and drive-letter forms.
* fix(release-exposure): keep Electron undecided without positive proof
The desktop owner names Electron only from an ASAR layout, so an asar: false
build has no candidate. A complete scan with no candidate was reported as
not_electron, which outranks unresolved and safe fuse outcomes and hid them
as not_applicable.
Leave the family unknown unless the fuse holder rules Electron out: an app
bundle with no Electron Framework, or a directory with complete coverage
whose native images were all read and hold no sentinel. A directory with no
native image is not proof, so its fuse checks now report unknown.
* fix(release-exposure): decode wide text members before scanning
A member's kind bypassed the NUL guard, so a UTF-16 plist was decoded as
UTF-8, matched nothing and still counted as scanned. A directory holding
only that member reported text.credential_markers as not_observed.
Decode by byte order mark at one boundary instead. UTF-16 is scanned in
either byte order, and inline maps read the same text. UTF-32 and NULs no
mark explains count as skipped, whatever the member's kind. A binary
plist keeps its raw scan. Unnamed members with a mark now reach the
scanner, and the plist decoder shares the same mark detection.
* fix(release-exposure): follow file: URL source map declarations
The scheme guard in noteLocalMapReference returned on every URL scheme, so
a declaration such as file:///release/debug.json was dropped with the remote
ones. A member without a .map name was then scanned as ordinary text and
javascript.source_map_sources reported not_observed.
Convert file: URLs with fileURLToPath and record them in mapTargets through
the same step as relative declarations. A target that is never reached, or
that was already scanned as text, counts as skipped, and so does a malformed
file: URL. A file: URL names one absolute location, so a .map target outside
the inventory stays unassessed rather than being trusted by its name. Remote
schemes stay ignored.
* fix(release-exposure): match backslash-relative Go flag paths
GO_FLAG_PATH accepted dot-relative roots only with a forward slash, so
`-I..\sdk\include` and `-I.\sdk` produced no path and go.build_paths came
back not_observed. Accept either separator after the dots.
The CLI acceptance test now expects unknown for electron.run_as_node on a
directory with no native images, matching the service.
* fix(release-exposure): keep fuses unknown on incomplete inventory
A directory inventory that stops at an acquisition limit never reports
the images it did not reach, so the native image sample has no omission
and safe fuses on an included helper settled to not_observed even though
scan coverage was unknown.
Treat unknown coverage like a truncated sample until the Electron
executable is accounted for. A selected native file is that executable,
so it keeps its decided fuse result.
* fix(release-exposure): decode binary plists before scanning
A binary plist stores each string as ASCII or UTF-16BE. Scanning the raw
bytes as UTF-8 missed any string the encoder kept in UTF-16, such as one
with a non-ASCII character, and still counted the member as fully
scanned, so text.credential_markers read not_observed.
Decode through the plist owner and scan its strings, dictionary keys and
data. A plist that cannot be decoded keeps its raw bytes in the scan and
counts as skipped, so coverage stays unknown.
* fix(release-exposure): keep UTF-8 members that hold a NUL
Any NUL in a member's first 512 bytes made the decoder give up, so a script
with a literal NUL in a string never reached the credential or inline-map
checks. An exposed marker in it read as unknown, and --fail_on high exited 0.
Validity alone cannot tell wide text from UTF-8, since ASCII in UTF-16 is
valid UTF-8. Treat a header that is an eighth or more NUL bytes as wide text
and anything not UTF-8 as undecoded, and scan the rest.
The docs now separate BOM-marked UTF-16, which is scanned, from wide text
with no byte order mark, which is not.
* fix(audit): scan every string a binary plist stores
The plist scan read only the decoded value, which drops objects the root no
longer references and entries a later duplicate key replaced. Those bytes
still ship, but the audit marked the scan complete, so a credential stored
there read as not observed and --fail_on high exited 0.
Read strings, keys and data from the object table instead. A stored object
that cannot be read leaves the member unassessed, and a total byte budget
bounds table entries that overlap to repeat the same text.
* fix(audit): keep packaged native images from ruling out Electron
The applicability check only looked at the "native images" omission. An
Electron executable inside a compressed .NET or PyInstaller member is recorded
under "packaged native images", so a directory whose on-disk hosts had no fuse
sentinel read as not Electron and all four fuse checks as not applicable.
Treat that omission as an unread image too.
* fix(audit): keep readable plist text when one object is unreadable
Throwing on one unreadable stored object discarded every string already
decoded, so a UTF-16BE credential beside a truncated unused object was lost:
the raw bytes that replaced it cannot match NUL-separated text.
Return the decoded text with complete false instead, and keep the raw bytes
in the scan beside it so ASCII in the unread object is still seen. The member
stays unassessed. Text past the budget ends the same way.
* fix(audit): read escaped inline maps and extensionless SEA entries
A data URL may percent-escape its base64 payload, so a map such as one padded
with %3D did not decode and the audit counted no embedded source map. Decode
the escapes before reading the base64.
A directory walk classed a SEA entry by its code path, so a main script whose
code path names no script was never scanned as JavaScript and its inline map
went unseen. Class it by the role the SEA metadata gives it.
* fix(audit): check plist text against the budget before decoding it
decodeBinaryPlistText decoded each stored object before comparing its size
with the remaining budget, and it keeps going after an object is rejected so
later text is still read. A plist under the input limit could repeat a large
range through overlapping table entries and force gigabytes of decoding that
the audit then discarded.
Separate finding a stored payload from decoding it, so a range past the
remaining budget is rejected without allocating its text. Earlier text and
the incomplete result stay as they were.
* fix(audit): count a packaged PE with an unread header as a native image
A package member whose PE signature lies beyond the 4 KiB probe recorded only
a PDB failure. The image was neither read nor listed under packaged native
images, so a directory whose on-disk hosts had no fuse sentinel read as not
Electron and all four fuse checks as not applicable.
Count it as a packaged native image, the omission that keeps the question
open, and keep probing it for text as before.
* fix(audit): keep cleanup-incomplete errors from composed readers
A desktop, fuse, Go or Rust failure became a string on that check, so an
error with unfinished cleanup lost its resources and live bindings while the
audit reported success, and the MCP handler does not close the service after
each request.
Treat unfinished cleanup like cancellation: stop the remaining owners and
return the error with the partial audit. When the owner attached its own
partial, keep the error and replace the partial, so cleanup stays attributed.
* fix(audit): keep safe fuses unknown past a packaged native image
The fuse check counted sampled-out images as possibly unread but not native
images inside a package. A complete directory scan with a safe-fuse image on
disk, no ASAR hint, and an unopened packaged image reported the four Electron
checks as not_observed, although the Electron executable could be that image.
The fuse check, the Go and Rust checks, and the Electron rule-out each decided
this for themselves and had drifted apart. Move the decision to one domain
predicate, nativeImagesMayBeUnread, and use it in all three. A hinted
executable that was read still settles the check.
* fix(audit): settle fuses only on an identified host, and read Bun and HTML maps
A retained helper set electron_executable_included because the rank marks
every .exe or extensionless sibling of resources/app.asar. With more than four
siblings, or an inventory that stopped early, safe fuses on a helper reported
not_observed while the host went unread. Rank stays a sampling hint. The check
now settles only on an executable named as the host, or when inventory was
complete and every executable that could be the host was sampled.
A Bun sourcemap region is named .bunmap, so its role was lost and a complete
audit reported no source maps. Carry the region role from the reader's metadata
into the member view, in the expanded and unexpanded paths, and treat it as a
JavaScript map so an unparsable region stays unresolved, not a debug companion.
An HTML member was scanned as text but its inline scripts were never checked
for source maps. The HTML owner now returns each body, the reader examines it
like a script file, and bodies past the owner's limit count as skipped.
* fix(audit): charge map reads to the count before opening the member
An oversized or unreadable .map returned before readSourceMap counted it, so
the allowance of 32 never ran out. An unexpanded .NET or PyInstaller package
could then decompress every listed map member, about 1 MiB each, up to the
4,096 entry cap.
The count bounds reads, so examineMap now reserves the slot before it opens the
member. A map past the count is skipped unopened, and a linker map past it is
still settled from its bounded prefix. An inline data: map is already in memory
and keeps its charge at the point it is parsed.
* fix(audit): examine scripts HTML loads whatever they are called
isJavaScriptMember knows a script by its suffix or an assigned kind, and the
reader dropped the script references the HTML owner parses. An extensionless
main loaded by <script src="main"> was scanned as ordinary text, so a complete
inventory reported javascript.source_map_sources not_observed with shipped
sources in its inline map.
The reader now resolves each JavaScript script reference against the page and
its base href, and a local target without a JavaScript name is examined as a
script when reached. A target read before its HTML file, never reached, or not
placeable on disk counts as a skipped map, so the check stays unknown.
* fix(audit): charge sibling map reads to the map count
Every generic .map beside a selected file got a full 1 MiB read and a
parse, with no cumulative bound, so the 4,096-entry listing allowed about
4 GiB of reading for one small selection.
Move the slot rule into chargeMapRead, which examineMap and sibling
classification now share. Past the count a sibling reads only the header:
a linker map is still identified, and one that may be a source map stays
a skipped companion.
* fix(audit): report cleanup failures raised by member reads
A close failure during an embedded-package probe, member read or inventory
reaches the shared read filter as an ordinary reader failure. The filter kept
only cancellation and integrity, so the member became a skipped read, inspect
succeeded, and the retained descriptor was reported nowhere. MCP does not close
the service after each request, so no caller saw it.
Propagate any failure that carries cleanup, whatever its reason, so it reaches
releaseExposureFailure and the audit reports its retained resources.
* fix(audit): charge unsuccessful text reads to the byte allowance
An oversized, undecodable, or failed text read left textBytes unchanged, so
every member got the full per-member budget and the cumulative allowance did
not bound what a package decompressed. Count the bytes member reads produce
and charge them whatever the read yields. An oversized file on disk still
costs only its probe.
* fix(audit): bound the paths kept for HTML script targets
A base href is copied into every script target it resolves, so a page under
the text member allowance could retain hundreds of megabytes of paths. Charge
each new target's resolved path to a byte allowance and leave targets past it
unassessed.
* fix(audit): keep examining a page loaded as a script as HTML
A script element naming an .html member promoted that member to a
script, and the text pass then chose one reading. The page's own script
elements were never read, so an inline map in it was lost and a shipped
sourcesContent went unreported.
Run the script reading and the HTML reading independently, and list the
script target allowance among the cases that leave source map coverage
undecided.
* fix(audit): detect credential markers that end in a hyphen
The Google, GitLab, and Slack rules allow a hyphen in the token but ended
on a word boundary, so a token ending in a hyphen at the rule's minimum
length was missed and the audit reported the check as not observed.
Open-ended rules now take the whole token, and the fixed-length Google
rule checks the full token alphabet after its last character.
* fix(audit): confirm FAT identity for probed package members
The package probe treated a decoded FAT table as a universal Mach-O. A
JVM class past the 4 KiB probe shares the FAT magic, so its truncated
prefix decoded as a table, the member counted as an unread native image,
and Electron could not be ruled out of a complete scan.
Pass the member's declared size to the sniff and take identity from
classifyFatContainerEvidence. A member with no usable size, or with
slices past the probe, stays counted as possibly native.
* fix(audit): admit binary plists in package member probes
A package member's probe judged text by its own NUL test, which accepted
byte order marks but not the binary plist signature decodeMemberText also
reads. A PyInstaller executable shipping a binary Localizable.strings was
rejected without a scan or a skipped count, so a UTF-16BE private key
marker inside it could report not_observed.
Name the encodings that explain their own NULs in one predicate, and move
the plist fixture builders to module scope.
* fix(audit): locate Go findings only in images that retain build paths
The exposed decision required non-empty paths, but the locations took
every image whose build info read. An image with no paths was named as a
source of the leak beside the one that had them.
One predicate now decides both.
* fix(audit): keep completed findings when member cleanup fails
A descriptor close failure aborts the member scan, and the scan's state
was dropped with it, so the caller got a typed cleanup failure and no
report. Source maps already counted and credential hits already collected
were lost.
The reader now hands the service what it assembled, with coverage unknown
and a limitation saying so, alongside the failure. The service runs that
scan through its existing stopped-audit path: no later owner runs, and the
report goes on the error as its partial observation.
* fix(audit): keep coverage unknown for nested ASARs in package hosts
A member of a PyInstaller or .NET host reaches the binary probe as a
plain file. An ASAR among them has a NUL-bearing header, so the probe
dropped it with no traversal and no omission, and source map checks could
read not_observed with complete coverage.
Ask the ASAR owner's signature probe, and record a match as a nested
package that was not expanded, as inventory-classified ones already are.
* fix(audit): let native format facts outrank member names
examineMember tried the name-based text and map routes first, so a PE an
owner identified inside an ASAR, named payload.json, was read as text.
Its CodeView path and the packaged-native omission were both lost, and
native.pdb_path read not_applicable.
Route an owner-established native format to the native probe first, and
count an encrypted one as a packaged native image for the same reason.
* fix(audit): report relative paths given to compiler path flags
go.build_paths only kept rooted paths, so retained build settings like
CGO_CFLAGS=-Iprivate-sdk read not_observed even though the setting names
a directory.
Take the relative argument of a C compiler path flag, attached or in the
next field. Scope this to compiler settings, because Go's own tools use
the same letters for non-paths such as -B gobuildid and -T 0x1000.
* test(javascript): expect retained text on SVG inline scripts
The SVG test from main predates this branch's inlineScripts text field,
so each side passed alone and only their merge failed.
* fix(audit): keep an unresolved Electron host's fuses unknown
fuseCheck decided from whatever outcomes the images reported, so a host
with no fuse answer read not_observed as soon as any other image in the
directory reported safe fuses.
Pass the images named as the host into the report and leave a check
unknown when one of them has no answer for it. Other images stay free to
have no fuses, because helpers in a real Electron install have none.
* fix(audit): bound the paths kept for local source map targets
noteMapTarget kept every declared target resolved against its member's
directory, so a short URL under a long directory could retain far more
than the script's own size, with no cumulative limit.
Add the allowance noteScriptTarget already has. A target past it stays
unassessed and counts as a skipped map, and is never read.
* fix(audit): borrow the audit's scope admission for inventory
inspect holds resources.run for the whole audit, and inventoryArtifact
entered the same scope again. With failed cleanup pending from another
audit, the inner run waits for a drain that waits for the outer run.
Add inventoryArtifactInScope, which takes the caller's admission.
* fix(audit): run the exposure verifier's commands under supervision
The verifier launched npm and the CLI with raw execFile and then removed
its temporary tree, so a timed-out child could outlive the tree it was
using.
Use the shared verifier commands and close them before removing the tree.
Their exit error carries stdout on its snapshot, so the audit's exit-code
branch reads it from there.
* fix(audit): read relative -L paths and split flag arguments
The relative-path reader handled C compiler settings only, so a directory
kept through Go's own linker flag, such as -ldflags='-L private-sdk -s -w',
reported go.build_paths as not_observed.
A quoted flag and argument also reached it as one field. The separating
space stayed on the argument, which reported rooted paths twice and kept a
leading space on relative ones.
Give -ldflags its own pattern that matches only -L, so -B gobuildid and
-T 0x1000 stay ignored, and consume the separator in the flag patterns.
* fix(audit): keep embedded leaves beside schema-invalid map fields
The reader recovers independent indexed leaves only after a
SourceMapFormatFailure, but inspectSourceMap let the schema's ZodError
through. A sibling with mappings "!", a section with a bad offset or a
root with a bad field skipped recovery, so a valid leaf with
sourcesContent reported javascript.source_map_sources as unknown.
Normalize schema rejection in SourceMapFormat, which owns these parses,
so every structural failure reaches consumers as a format failure. The
message is unchanged, and the codec and web fetcher already map both
cases to the same result.
* fix(audit): keep packaged executables' own packages unknown
examinePackagedBinary counted a native image inside a package and moved
on, while only on-disk images reached the embedded-package probes. An
ASAR carrying a PyInstaller, Bun, Node SEA or .NET executable with a
source map therefore scanned complete and reported
javascript.source_map_sources as not_observed.
Run the same classifier on packaged images. The probes read carrier
offsets from a file, so the member's bytes stay in a temporary file for
the probe alone. A detected package keeps member coverage unknown, as a
nested ASAR does, and an image past one file's total byte allowance
stays unprobed and unknown. A plain native member leaves coverage intact.
* fix(audit): read an app's sampled images for fuses
A .app target sent only the bundle candidate to the fuse reader, which
resolves to the outer Electron Framework. A sidecar with RunAsNode enabled
was never read, so the check reported not_observed in the app view while
the directory view reported exposed.
Send the bundle candidate first and then the sampled native images. An
image whose digest matches the bundle's read is skipped, so the framework
is not read twice. A sidecar's safe fuses cannot settle the outer framework,
so a bundle read with no answer for a fuse keeps that check unknown.
* fix(audit): read a Node shebang as script identity
The path classifier gives an extensionless executable a resource kind,
so a script such as bin/runner with a `#!/usr/bin/env node` header was
scanned as text without its inline maps being examined or counted. A
complete inventory then reported no shipped sources for it.
The probe holds the first bytes a member reader sees, so establish the
identity there. The script keeps the usual outcomes: a map that is read
counts, and one past an allowance or undecodable stays unknown.
* fix(audit): keep linker strings out of Go build paths
Reading `-X 'main.usage=-L level'` as a library directory made
go.build_paths report `level`, so a plain string could fail an audit
under --fail_on low.
Read the optional package selector once, before the setting's first
flag, and keep the argument of -X attached to it, so a string it sets
is never scanned as another option.
* fix(audit): keep a failed probe directory removal owned
A packaged image's probe copies the member's full bytes into a temporary
directory. When removing it failed, the error became an ordinary
unreadable probe: the audit could still succeed, nothing could retry the
removal, and the client heard nothing about the leftover bytes.
Hold the directory in the reader's resource scope, so close retries it,
and report the unfinished cleanup without replacing a primary failure.
* fix(audit): keep a declared .map from reading as a linker map
A script's sourceMappingURL=assets.map was dropped because the name
already looked like a map. A malformed assets.map then settled as a
linker map, so both source-map checks read not_observed and
native.debug_companions reported an exposure the release does not ship.
Keep a generic .map name as a declared target and read it as a
JavaScript map, in either scan order, so it stays unresolved.
* test(audit): rethrow typed errors from release exposure checks
A failed Result was rewrapped in a plain Error, which dropped its type,
cause and cleanup details from the test output.
* fix(audit): match companions by member identity, not spelling
An ASAR member's location is relative, and a declared map target is
absolute, so a malformed assets.map read before z.js stayed a linker
companion. The examined-map lookup, which is normalized, then returned
early, leaving both source-map checks not_observed.
Index companions by memberLocationKey and keep each reported spelling
under it. Spellings stay distinct because symlinks can make them differ.
* fix(audit): retract only guessed linker maps on a declaration
A script declaring sourceMappingURL=symbols.pdb after the scan reached
symbols.pdb removed the shipped PDB from the companion list, because a
late declaration retracted any companion. native.debug_companions could
then read not_observed.
Keep name-identified companions in their own set, and retract only maps
that were read as non-JSON and guessed to be linker maps.
* fix(audit): record an unfollowed map link as skipped
The directory and ASAR readers do not follow links, so a symlinked
app.js.map dropped out before any map counter moved. Its target was
scanned as plain text, and both source-map checks read not_observed.
Count a link that names a map as skipped wherever entries reach the
audit, and mark it examined so a declaration does not count it twice.
* fix(audit): read relative paths in Go compiler and assembler flags
A binary built with -gcflags=-I private-sdk keeps that setting, but
relative parsing covered only C compiler settings and -ldflags, so the
directory dropped out of go.build_paths.
Give -gcflags and -asmflags their own path options, taken from what
go tool compile and go tool asm document, and strip the package selector
as -ldflags does. The assembler's -D defines a symbol, so it is not one.
* docs(audit): state the packaged image read allowance
Packaged ELF, Mach-O and PE images are read in full and copied to a
temporary file, sharing one max_file_bytes allowance, but the docs and
coverage.allowances still said only a PE gets more than a header read.
Name the allowance and the copies in docs/release-exposure.md,
RELEASE_EXPOSURE_ALLOWANCES and the max_file_bytes description.
* docs(audit): say how go.build_paths treats rooted -X values
A rooted -X value cannot be told from a file path, so the audit reports
it. State that, and why, so the rule is a contract rather than a side
effect of how flags are scanned.
* fix(audit): keep a declared sibling map out of the linker guess
When the selected file declared a sibling map, the sibling classifier
never consulted the declaration. A malformed assets.map then read as a
linker map and raised native.debug_companions.
Leave a declared sibling to the unreached-target accounting, as members
already are, so it stays an unresolved source map.
* fix(audit): keep undecodable Go and Rust metadata unknown
Unsupported meant two things: a container this reader does not handle,
and metadata the image holds in a form the reader cannot decode, such as
a compressed .dep-v0 section. Both read as not_applicable, so an image
that carries metadata reported that the check did not apply.
Mark the second case on the format failure and carry it on
AnalysisUnsupportedTargetError, so the audit records a failed read and
stays unknown. The tools' own errors and messages do not change.
* fix(audit): keep coverage unknown for a PE header past the probe
A packaged member whose PE signature lies beyond the probe prefix stayed
unclassified, so the embedded-package probe never ran and coverage stayed
complete. A map inside such an image then read as not_observed.
Record the member as an unexamined package where it is already marked as
a possible native image.
* fix(audit): read the scripts of a standalone SVG
An SVG can carry a script with an inline source map, but only .html and
.htm members reached the script owner, so such a file left every map
counter at zero and javascript.source_map_sources read not_observed.
Treat .svg as a script-bearing page. htmlArtifactReferences already
reads SVG script elements, so this adds no parsing.
* fix(audit): keep every possible host when no name picks one out
With no named Electron host, every sibling ranks alike and
electron_host_paths stayed empty, so one image's safe fuses settled the
checks even when another possible host reported none.
Keep all sampled candidates as hosts in that case, so a candidate
without fuses leaves the checks unknown.
* fix(audit): read a text-named native member by its bytes
A PE member called payload.json took the text route, so it reached no
native probe, and its CodeView path and packaged-native uncertainty were
lost.
Decide from the bytes the text read already loaded, so real text costs
no extra read and the map and text allowances stay as they were.
* fix(audit): let a map or script read identify a native member
A package lists names without formats, so a PE called payload.js.map
took the map route and never reached a native probe. Its CodeView path
was lost, and with nothing counted as failed, native.pdb_path could read
not_applicable. A PE at a declared script or map target was lost the same
way, because declaration overrode the route.
Decide from the bytes the route already read, in the map route as in the
text route, and keep the listed kind for a promoted script target. Real
text and maps cost no extra read, so those allowances are unchanged.
* fix(audit): name a symlinked sibling debug companion
Selecting an executable skipped every symlink beside it before the name
check, so an app.pdb link left no companion path and no skip count, and
native.debug_companions read not_observed. Selecting the directory
reports the same link.
Check the name first. The map branch already needs a regular file, so no
link is followed.
* fix(audit): keep map coverage unknown for a linked script
A link named app.js to an extensionless payload was skipped as a link,
and its target was read as plain text with no map directives parsed. An
inline map there left no parsed or skipped count, so
javascript.source_map_sources read not_observed. Only links with a map
name were counted.
Count a link under any name that can declare a map, the set an unread
member already uses. It stays one omission when a page or a directive
also names it. The target is still not followed.
* fix(audit): keep a declared JavaScript map beside a selected file unknown
A local sourceMappingURL with a JavaScript map name such as app.js.map
was dropped on the assumption that it would be examined as a member.
When the selection is a file, sibling .js.map files are never read, so
the declared map left javascript.source_map_sources not_observed while
the same target spelled as a file: URL left it unknown. Record every
local target so an unreached one counts as skipped.
* fix(audit): read paths a C compiler forwards to the linker
CGO flags can hand linker options through -Wl,a,b or -Xlinker a. The
compiler flag parser saw -Wl,-L,private-sdk as one field and missed the
relative library directory, so go.build_paths reported not_observed.
Split forwarded options out before reading paths and read them with the
linker's own path options (-L, -T, --library-path, --script). This also
stops -Xlinker -Bsymbolic from being read as the compiler's -B prefix.
* fix(audit): read forwarded linker options by their linker meaning
The forwarded linker flag pattern read -Ttext=0x400000 as a script path
named text=0x400000, and it ignored -I, so -Xlinker -Ilib/ld-custom.so
lost the interpreter path the compiler flag pattern found before the
split.
Exclude the -T section and segment address options (bss, data, text,
text-segment, rodata-segment, ldata-segment) and read -I and
--dynamic-linker as paths.
* fix(audit): split Go flags as Go does and read external linker flags
Go splits a flag value with quoted.Split: a field that starts with a
quote runs to the matching quote, and any other quote is a literal
character. The reader stripped every quote, so CGO_CFLAGS
-I"/home/O'Neil/include" reported /home/ONeil/include, while cc was
given the directory "/home/O'Neil/include", quotes included.
-ldflags kept -extldflags values with the Go linker flags, where only
-L counts, so -extldflags=-Lprivate-sdk reported no path. The values
are now read as the C compiler driver reads them, including -Wl, and
-Xlinker forwarding.
A package member named payload.json or payload.js.map that holds an
ASAR took the text or map route, which checked only native signatures,
so the nested archive was scanned as text and coverage read complete.
The route's own read now recognizes the ASAR signature as well, and the
nested package leaves coverage unknown.
* fix(audit): keep a named script beside a selected HTML file unknown
A script element whose target had a JavaScript name such as app.js was
dropped on the assumption that it would be examined as a member. When
the selection is an HTML file, sibling scripts are never read, so a
sibling holding an inline map left javascript.source_map_sources
not_observed while the same target named app left it unknown. Record
every local script target so an unreached one counts as skipped.
* fix(audit): read a retained -pgo setting as one profile path
`-pgo file` names a single profile, and under -trimpath Go keeps only
its file name, so the flag readers dropped values such as
`private-training.pgo` and split paths with spaces or commas. Report
the value whole, skipping `auto` and `off`.
* fix(audit): read -extldflags paths as the external linker splits them
With -ldflags set to -extldflags '-L "/srv/private sdk"', the outer
flag reader could not match the path behind the inner quote, and the
external reader kept only relative paths, so go.build_paths read
not_observed. Read each -extldflags value once, as go tool link splits
it with quoted.Split, and collect rooted and relative paths from those
same arguments. The rest of the setting is read as before.
* fix(audit): read the Go linker's other path options
go.build_paths read only -L as a Go linker path option, so a retained
-ldflags='-tmpdir=private-obj -s -w' reported no path and --fail_on low
missed the directory.
Read every option `go tool link -h` documents as taking a directory,
path or file: -I, -L, -benchmarkprofile, -capturehostobjs, -cpuprofile,
-fipso, -importcfg, -memprofile, -o, -r and -tmpdir. -extld, -extar and
-libgcc name a program or `none`, so only their rooted values count.
* fix(audit): keep a quoted Go argument whole
goFields already reads -L "/tmp/private - sdk" as two arguments, but
each field was then split again before every whitespace-led dash, so
go.build_paths reported /tmp/private instead of the retained directory.
Split again only a field that begins with a flag, after any package
selector, as a quoted "-L dir -s -w" does. Any other field is one
argument and is kept whole, in -ldflags, -extldflags and CGO flags.
* fix(audit): keep coverage unknown for an unread packaged image
When a listed package member is a native image but its first read
fails, examinePackagedBinary returned before probePackagedImage could
mark its possible embedded package unexamined. Coverage stayed complete
and javascript.source_map_sources could report not_observed for a
payload nobody read.
Mark member coverage unknown wherever a native member goes unread,
including an encrypted one, as an image the probe could not read
already does, and say so in the limitations.
* fix(audit): read rooted Go tool path arguments whole
`-L "/tmp/private,sdk"` and `-L=/tmp/a=b` name one directory, so a `,` or
`=` in a documented path flag's argument is literal. `-trimpath` keeps its
prefix=replacement structure.
* fix(audit): count an unread packaged member once
* fix(audit): keep trimpath rewrites and directory fields apart from selectors
* fix(audit): keep a .map unsettled once a declaration was discarded
* fix(audit): skip env option arguments when reading a Node shebang
* fix(audit): keep companion coverage unresolved for a generic .map link
* fix(audit): count a dropped linker-map guess as a skipped companion
* fix(audit): read env settings after -- in a Node shebang
* fix(audit): route a selected native image named .map by its format
A directly selected or listed native image has no package format, so
examineMember skipped its native-first branch and sent a file such as
release.map through map routing. Its bytes then became a linker-map
guess and marked native.debug_companions exposed. Native format now
outranks the name for every member, packaged or not.
* fix(audit): read forwarded -rpath and -rpath-link as linker paths
A cgo build with CGO_LDFLAGS=-Wl,-rpath,private-runtime kept the
relative directory, but the forwarded linker parser only knew -L, -T,
-I, --library-path, --dynamic-linker and --script, so go.build_paths
reported not_observed. Add -rpath and -rpath-link to the same parser,
in the attached, = and next-field forms. Loader-expanded values such as
$ORIGIN/../lib and @loader_path/Frameworks name no build-host directory
and stay unreported.
* fix(audit): read each entry of a forwarded linker search list
-rpath and -rpath-link take a colon-separated search list. The loader
token filter tested the whole list, so -Wl,-rpath,$ORIGIN:/opt/private-runtime
dropped /opt/private-runtime along with $ORIGIN, and the rooted-path pass
cannot see an entry that follows a colon. Split each search list into its
entries and drop only the loader-expanded ones.
* fix(audit): expand loader tokens only in a linker search list
The runtime loader expands $ORIGIN and @loader_path only in a -rpath or
-rpath-link search list. The token filter also ran on the argument of -T,
-L and -I, so -Wl,-T,$ORIGIN.ld dropped a literal linker script path and
a complete read could report go.build_paths as not observed. Search list
entries keep the filter; every other linker path argument is kept as is.
* fix(audit): count a .map link beside a selected file as a skipped companion
The sibling scan skipped any .map entry that was not a regular file, so a
symlink such as symbols.map -> payload beside a selected executable left
debug companion coverage at not observed. The reader never follows links,
so the linker map such a link may name is unread, not absent. Count a
generic .map sibling link as a skipped companion without following it,
as noteUnfollowedMapLink already does inside a selected directory.
* fix(audit): split a linker search list only for an ELF image
Only the ELF linker reads a -rpath or -rpath-link argument as a
colon-separated list. The Mach-O linker stores each -rpath argument as one
directory, so -Wl,-rpath,@loader_path/lib:extra is a single loader-relative
entry, yet it was split and extra was reported as a build path. Carry the
native image format into the Go flag reader and split search lists only for
ELF; other formats read the whole argument as one entry.
* fix(audit): expand only the image loader's runtime tokens
A runtime search path token is expanded only by the loader of the image's
format. The ELF loader expands $ORIGIN, $LIB and $PLATFORM; dyld expands
@executable_path, @loader_path and @rpath. Each is literal text to the
other loader, so -Wl,-rpath,$ORIGIN:/opt/private-runtime on a Mach-O image
names a literal directory, yet it was dropped and go.build_paths reported
not_observed. Pick the token set from the native image format; the PE
loader reads no search list, so it expands none.
* fix(audit): keep a declared .map link beside a selected script a source map
The sibling scan counted any .map symlink as a skipped debug companion
before checking whether the selected script declared it as its source
map. classifySiblingMap already excludes a declared target that is a
regular file, so a declared symbols.map link added a false unknown to
debug_companions. Apply the declared-target check before either file-type
branch; noteUnreachedTargets still counts the unread map as skipped.
* fix(audit): byte-check members whose kind is only an inventory path role
Inventory gives a packaged member a role from its name, such as resource
for payload.json or source-map for payload.map. The packaged-format guard
treated any kind other than "file" as owner-stated, so a nested ASAR an
ASAR lists under one of those names skipped signature recognition and
cleared coverage. Track whether the package owner stated the kind (Bun
sourcemap region, SEA main script) and only skip the byte check then.
* fix(audit): read Go's linker -r setting as a runtime search list
go tool link -r sets the ELF dynamic linker search path, dir1:dir2:...,
which the linker writes to RUNPATH. It was read as one whole path, so a
release built with -ldflags='-r=$ORIGIN/../lib' reported the
loader-relative entry as a build path and go.build_paths as exposed.
Read -r entry by entry with the same image-loader-aware split and token
filter a forwarded -rpath uses: ELF splits at ':' and drops $ORIGIN
entries; Mach-O keeps the argument as one directory.
* fix(audit): keep a selected package executable's native carrier format
A directly selected PyInstaller executable named release.map recorded the
package format as its member format, even though its ELF or PE carrier was
already recognized. examineMember then missed the native-first branch and
treated the executable as a linker map, reporting a debug companion.
Forward the native carrier format for the selected file, as listed members
already do.
* fix(audit): keep coverage unknown for an unread unclassified member
A packaged member with no format of its own, such as a PyInstaller
payload.bin, recorded only a skipped text member when its read failed
before the header. Member coverage then stayed complete, though the
member could be a native image or carry a nested package. Leave
coverage unknown and say which member could not be read, as an unread
native member already does.
* fix(audit): keep ASAR header admission distinct from max_file_bytes
A directory holding an ASAR whose header exceeds the header admission
was reported as a file-size refusal with max_file_bytes advice, which
cannot fix it. Report the header admission as its own bound instead.
* fix(audit): thread the shared ASAR header admission into release exposure
createReleaseExposureService built ReleaseExposureReader with no admission,
so its inventory and every reopened ASAR reader stayed on the 16 MiB
default and ignored REA_ASAR_MAX_HEADER_BYTES and asar.max_header_bytes.
Resolve the admission in the composition with runtimeAsarHeaderAdmission,
as the Electron and V8 observation providers do, and pass it to both
inventoryArtifactInScope calls, the reopened container readers, and the
nested ASAR signature probe. The refusal now names the setting that can
raise it instead of claiming there is no runtime override.
* fix(audit): keep a nested ASAR probe stopped at the admission undecided
The release exposure reader treated an ASAR signature probe that stopped
at the header admission as "not an ASAR". A packaged member such as
payload.json holding an ASAR whose JSON starts past asar.max_header_bytes
was then read as plain text, its inner maps went unread, and
javascript.source_map_sources could stay not_observed.
A probe that reaches the admission limit, or runs out of a prefix read
after its extents parse, now marks package coverage unknown and records
a limitation, while the member is still examined as text.
* fix(cli): parse audit-release-exposure --max-file-bytes as a decimal token
Main now requires every numeric CLI option to admit decimal tokens and
reject 0x10 and 1e3 (tests/boundary/cli/cliDecimalArguments.test.ts).
Wrap the release exposure maxFileBytes option in cliDecimalField like the
other maxFileBytes options, so the domain field keeps its bounds and default.
* docs(specs): add the release exposure audit specification
Add spec.md, plan.md and tasks.md under docs/specs/artifacts/release-exposure/
as docs/development/documentation.md requires for a new feature, and list
them in docs/specs/README.md. The existing guide at docs/release-exposure.md
keeps describing current behavior and its route is unchanged.
* fix(audit): bind unpacked ASAR member reads to their inventoried digests
A selected ASAR is captured and checked against its inventoried digest, which
binds packed members. An unpacked member is read from its .unpacked companion
on disk instead, and readContainerMember and readEntryBytes never compared
those bytes with the member digest inventory recorded, so a companion
replaced between inventory and the scan was accepted.
readContainerMember now passes the member's inventoried digest for unpacked
entries, and readStreamBounded hashes the full stream, draining past the read
allowance when needed, and fails with "ASAR member changed before
interpretation", which surfaces as an artifact_changed error like the
container check. A regression test replaces a companion after inventory. The
guide, spec, plan and tasks describe the binding.
* fix(audit): let stated JavaScript roles outrank a .map name
isDeclaredMap() routed any member whose name ended in .map to map
handling before the owner's stated role or a page's script declaration
was considered. A Node SEA main script at boot.map, which the SEA reader
marks as javascript, and a .map script an HTML file loads both became
linker-map guesses, so their inline maps were never read and
native.debug_companions was reported exposed.
An owner-stated source-map role or a declared map target still makes a
member a map. Otherwise a stated javascript kind or a script target now
wins, and only then does the .map suffix, or the inventory path role that
repeats it, decide. Regression tests cover both cases, and the guide
notes that a SEA main script is read as a script whatever it is called.
* fix(audit): keep coverage unknown when a packaged text member fails to read
A compressed member named as text, such as payload.json in an unexpanded
PyInstaller host, can fail inflation before any header is returned. The
.bin case already leaves member coverage unknown because an unread header
may hide a nested image or package, but examineTextMember() only counted
the member as skipped text, so otherwise complete coverage stayed
complete and package-dependent checks could settle.
An unread packaged member whose kind its package did not state now goes
through noteUnreadBinary(), which counts it as skipped text, marks
coverage unknown, and records the limitation. A regression test corrupts
the zlib stream of payload.json in a CArchive host and checks the intact
member keeps coverage complete. The guide describes the rule.
* fix(audit): keep the unpacked ASAR digest check inside the read allowance
ea87cf8 made readStreamBounded hash an unpacked ASAR companion to its end
so it could compare against the inventoried digest, which drained the
stream past the read allowance. A 1 GiB unpacked module was read in full
for a 4 KiB header probe and again for a package probe, and a companion
that grew after inventory had no byte limit on that path.
readStreamBounded now stops at the allowance as every other member read
does, and compares the digest only when the member ended within the
allowance. A companion past the allowance stays over_allowance without a
digest verification. A test counts the bytes each scan read pulls from an
8 MiB companion. The guide and spec state the bound.
* fix(audit): read a script target whose bytes are a source map as a map
1d3f89f let a stated javascript kind or an HTML script target outrank a
.map name, so an HTML `<script src="main.map">` that points at a valid
source map with sourcesContent was read only as a script. Its sources
were never counted and javascript.source_map_sources could miss an
exposed finding.
A script role says how a member is loaded, not what its bytes hold. A
.map member or loaded script whose text parses as a source map, a JSON
object with version and mappings, sources, or sections, is now read as
a source map. Anything else is still read as a script, so a SEA main
script named boot.map and a .map script carrying an inline map keep
reaching examineInlineMaps. A regression test covers the map case, and
the guide states the rule.
* fix(audit): retract a linker-map guess when a page loads the map as a script
noteScriptTarget() returned early for a target already read, counting its
source-map coverage as skipped but leaving any linker-map guess the first
read recorded. When a directory or ASAR listed a non-JSON app.map before
the index.html that loads it with <script src="app.map">, app.map stayed in
native.debug_companions and the result depended on enumeration order.
noteMapTarget() already retracted such a guess for a late map declaration.
A late script declaration now retracts the guess too, and still counts the
script as skipped because it was never examined as JavaScript. A test
covers the directory and ASAR order, and the guide states the outcome.
* fix(audit): respect script and native identity for sibling linker maps
classifySiblingMap() guessed a linker map for any sibling .map whose bytes
could not begin a JSON object. A sibling boot.map that a selected index.html
loads with <script src="boot.map">, or an ELF image named release.map beside
a selected executable, was therefore reported in native.debug_companions.
scanSiblings() now skips a sibling that is a script target as well as a map
target, leaving noteUnreachedTargets() to count it, and classifySiblingMap()
records no guess when sniffNativeFormat() recognizes the header. Tests cover
both cases and the guide states the outcome.
* fix(audit): read a script-loaded map from the text that recognized it
scriptHoldsMap() recognizes a .map or script target as a source map from
decodeMemberText(), which honors a UTF-16 byte order mark, but
readScriptAsMap() then passed the raw bytes to readSourceMap(), which reads
UTF-8. A BOM-marked UTF-16 main.map loaded by <script src="main.map"> failed
the second parse and its prefix became a linker-map guess.
readScriptAsMap() now hands the decoded text to readSourceMapText(), which
readSourceMap() also uses for byte input, so both stages read the same text.
A test covers a UTF-16 map with embedded sources.
* fix(release-exposure): honor byte order marks in binary plist data
A binary plist data object holding BOM-marked UTF-16 text was decoded as
UTF-8, so credential markers in it went unmatched while coverage still
read complete. Decode such payloads with the encoding their mark declares,
and leave coverage incomplete for UTF-32, which no reader scans.
* fix(audit): keep an unprobed package member past the text allowance unknown
Once the cumulative text allowance was spent, examineTextMember() counted a
packaged member named like text, such as payload.json in a .NET or
PyInstaller bundle, as skipped text without reading its header. Coverage
stayed complete even though the member could be a native image or a nested
ASAR, while the same bytes named payload.bin kept coverage unknown.
An unexpanded package member whose kind the owner did not state now leaves
coverage unknown with a limitation when the allowance skips it, the same
uncertainty an unread .bin member carries. No extra read is made, so the
allowance still bounds what is opened. A test covers an ASAR with an
embedded source map named payload.json after sixteen full text members.
* fix(release-exposure): require a read host for every Electron layout
A directory with several resources/app.asar layouts counted one named
host as fuse coverage for all of them. Track host coverage per layout so
safe fuses stay unknown when a whole layout's possible hosts were
dropped from the native image sample.
* fix(release-exposure): keep a .map script past the target allowance unassessed
When HTML script references spent the resolved-path allowance, a later
.map script target was dropped without its script role, so its JavaScript
bytes settled as a linker-map guess and debug_companions reported an
exposure. A refused script target now sets mapTargetsDiscarded, the same
flag a refused map target sets, so a later .map stays unassessed instead
of becoming companion evidence.
* fix(release-exposure): read a Node shebang as script in any text member
A selected or listed text file such as runner.txt that starts with a Node
shebang went straight to the text reader, which never gave it a JavaScript
role, so its inline map went unread and source_map_sources read
not_observed. The text reader now takes the role from the bytes it read,
so every route into it sees the shebang, and the probed-text path no longer
sets the role itself.
* fix(release-exposure): exclude Go search lists by position, not by value
compilerPaths dropped every literal path whose text equalled a forwarded
-rpath or -rpath-link list, so CGO_LDFLAGS=-Wl,-L,$ORIGIN -Wl,-rpath,$ORIGIN
lost the literal -L directory and go.build_paths read not_observed. The
literal flags are now read with each search list option and its argument
removed by position, and the search lists are still read entry by entry.
* fix(release-exposure): keep a probed Node launcher's role past the allowance
The probe recognized a Node shebang but handed the text reader the member
without its JavaScript role. Once earlier text spent the shared allowance,
the text reader returned before reading the header again, so an
extensionless launcher added no skipped map and both source-map checks read
not_observed instead of unknown. The probe now passes the role it found.
* fix(release-exposure): decode a map in the encoding its byte order mark names
The map reader, its prefix check and the sibling classifier decoded map
bytes as UTF-8, so a generic symbols.map holding BOM-marked UTF-16 source
map JSON failed to parse and its prefix read as a linker map: a spurious
debug companion and source_map_sources not_observed. Map bytes are now
decoded by their byte order mark at that boundary, and a UTF-32 map, which
this reader does not decode, stays unresolved.
* fix(release-exposure): keep a lone dash inside a quoted Go path argument
splitGoField split a quoted field before every whitespace-led dash, so the
retained -ldflags '-s "-L=/tmp/private - sdk" -w' read the dash inside the
directory as a new flag and goPaths reported /tmp/private. A quoted field
now splits only before a dash that begins a flag, so a dash followed by a
space stays part of the path.
* fix(release-exposure): choose possible Electron hosts within each layout
The named-or-possible host choice ran across every kept image, so a named
a/a.exe removed the unnamed b/launcher.exe of a second ASAR layout from
electron_host_paths, and its unresolved fuses no longer held
electron.run_as_node at unknown. Each layout now chooses its own named or
possible hosts, and the sets are combined.
* fix(release-exposure): read a relative compiler --sysroot as a build path
The compiler path flag list had no --sysroot, and the rooted-path fallback
cannot see a relative operand, so CGO_LDFLAGS=--sysroot=private-sdk left
go.build_paths at not_observed. --sysroot is now a compiler path flag,
attached with = or given as the next field.
* fix(release-exposure): recognize Windows Electron layouts by the owner's path
The desktop owner gives each file a native local_path, such as
C:\ship\App\resources\app.asar on Windows, and desktopPathConventions only
splits on /, so every ASAR basis was skipped: host ranking and
electron_host_paths stayed empty and a named host with no sentinel let a
safe helper read electron.run_as_node as not_observed. Each basis now
carries whether the owner's slash-separated path is an ASAR layout, and the
layout folders read a Windows absolute path with Windows separators on any
host.
* fix(release-exposure): read a Bun module by its JavaScript loader
An extensionless Bun contents member such as $bunfs/root/release carries
loader_name js, but ownerStatedKind kept only the sourcemap region, so the
member was never read as a script and its inline map went unseen. Give a
Bun contents region the javascript role for the js, jsx, ts and tsx
loaders, as SEA main scripts get it.
* fix(release-exposure): read a Node shebang .map as a script
An undeclared `.map` was routed as a source or linker map before its
first bytes were checked, so a Node script named `runner.map` recorded a
linker-map companion and never had its inline map read. When the map
read of an undeclared `.map` starts with a Node shebang, its map slot is
returned and the text reader examines it as JavaScript, so its inline
map is parsed and no companion is reported.
* Detect full-length GitHub refresh tokens
Narrow github.token to gh[opus]_ with 36 characters and give ghr_ its
own 76-character github.refresh_token rule, with a test.
* fix(release-exposure): bind a negative package probe to the inventoried image
An on-disk native image whose embedded-package probe finds nothing clears member coverage, so it now hashes the probed descriptor and compares it with the inventoried digest before accepting absent. A replacement after inventory fails as a changed member instead of reporting complete coverage.
* fix(release-exposure): let an app bundle read settle only its own layout
An app target, an app-bundle fuse read or a sampled selected executable used to mark the Electron executable as included for every ASAR layout, so safe outer fuses reported not_observed even when a nested Electron app's host was never read. Those reads now settle only their own layout; every other discovered layout still needs its own host read, so an omitted nested host keeps fuse checks unknown.
* fix(release-exposure): honor a Node shebang before guessing a sibling linker map
A sibling .map beside the selected file that starts with a Node shebang is a script, as it already is for a member .map, so classifySiblingMap no longer records it as a linker-map debug companion.
* fix(release-exposure): name a bundle's own layout by the owner's path
The settled layout set joined the caller's bundle spelling with Contents, so a selected /tmp/App.app missed the owner's resolved /private/tmp/App.app/Contents layout and safe fuse checks fell to unknown. Each archive basis now carries the owner's relative layout folder, and the bundle's own layout is the one whose relative folder is Contents, looked up by the owner's resolved folder.
* fix(release-exposure): read file-valued linker options as build paths
LINKER_PATH_FLAG listed --script but not the other linker options that read a file, so -extldflags=-Wl,--version-script=private-versions.map left go.build_paths not_observed. --version-script, --dynamic-list and --retain-symbols-file now read their operand as a path, attached or in the next field.
* fix(release-exposure): count a macOS bundle's framework and MacOS hosts in its layout
A .app layout resolves to its Contents folder, but its hosts are Contents/MacOS/* and the Electron framework binary under Contents/Frameworks, so per-layout host ranking and fuse coverage dropped them and one bundle's safe fuses could settle another bundle. Those images now belong to the bundle's layout, with the framework binary ranked as its host.
* fix(release-exposure): keep a bundle's launcher out of its fuse hosts
A macOS bundle's Electron Framework binary carries its fuses. When it
is among a layout's named hosts, it is now the layout's only fuse host;
the MacOS launcher beside it is still sampled but no longer leaves safe
fuse checks undecided when it has no sentinel.
* fix(release-exposure): keep an undecided PE package member out of linker maps
An unexpanded package member named .map whose PE signature lies past the
retained prefix fell through to noteUnreadMap, which read its MZ prefix as
a linker map and reported native.debug_companions exposed with complete
coverage. The map route now records the same undecided-PE uncertainty the
binary route does, so the member stays unexamined and coverage unknown.
* fix(release-exposure): scan the text of an undecided PE map member
A packaged .map member whose MZ header is undecided returned before its
fully read bytes reached the credential scanner, so a PEM key in it read
as unknown instead of exposed. Scan bytes read in full before returning,
while still keeping PE coverage undecided.
* fix(release-exposure): keep an oversized text member's undecided PE header
A packaged text member past the per-member allowance keeps only its prefix,
so an MZ header whose PE signature lies further in returned null and the
text route dropped the native and package uncertainty. Note the undecided
header as the map and binary routes do.
* fix(release-exposure): join Electron layouts by caller path and keep partial discovery unknown
The desktop owner resolves its root, so a target selected through a
symlinked parent got canonical local_paths that never matched the
audit's caller-spelled images, and the named host dropped out of
electron_host_paths. Rebase owner paths under the canonical root onto
the selected path.
When discovery stopped after it found one Electron layout, the basis
list was treated as exhaustive. Report the layouts as incomplete so safe
fuses on the listed host stay unknown.
* fix(release-exposure): keep Electron hosts under a trailing-slash selection
Join the rebased archive path with appendCallerPath so a selected
`/work/app/` yields `/work/app/resources/app.asar`, whose layout folder
matches the inventoried executables' parent.
* fix(release-exposure): keep maps unassessed for unread text past the allowance
A text-named member such as `runner.txt` can start with a Node shebang.
Once the text allowance is spent its header is not read either, so count
every unread member that could hold a script as a skipped map instead of
trusting the filename-derived role.
* fix(release-exposure): keep an unread packaged map member undecided
When a packaged map member yields no header (a failed read or no map
slot), its name is no evidence of its format. Route it through
noteUnreadBinary, as the text route does, so a member that may be a
native image or package keeps coverage unknown.
* fix(release-exposure): keep an undecided sibling PE header out of linker-map guesses
A sibling .map past the map allowance keeps only its probe, so an MZ
header whose PE signature lies further in is undecided. Share the
undecided-PE test with packaged members and count such a sibling as a
skipped companion instead of guessing a linker map.
* fix(release-exposure): keep possible fuse hosts when no ASAR layout names one
Without an ASAR basis no image is ranked, so no image counted as a fuse
host and one image's safe fuses could settle a sentinel-less sibling.
While the desktop family is undecided and no layout names a host, treat
every sampled image as a possible host.
* fix(release-exposure): keep images outside a found ASAR layout as possible fuse hosts
An asar: false build beside a discovered ASAR layout gets no basis, so
its executables were never fuse hosts and the layout's safe fuses could
settle a sentinel-less image in the other build. When no name picks out
a host outside the found layouts, treat every sampled image there as a
possible host, as the no-layout case already does while the family is
undecided.
* fix(release-exposure): keep a dropped host outside a found ASAR layout unread
An asar: false build beside a found ASAR layout names no host, so an
executable outside every layout that the native-image sample dropped may
be that build's unread fuse host. Coverage no longer counts as proven
when such an image was inventoried but not sampled.
* fix(release-exposure): read Go build-path response files named in compiler and linker flags
A CGO setting such as `-Wl,@/tmp/private-link.rsp` or
`-Xlinker @/tmp/private-link.rsp` names a response file the linker reads
options from, and a bare `@FILE` is read by the compiler driver. Report
the file's path, relative or rooted like other path arguments, without
opening it. An option's own value, such as `-install_name
@rpath/libx.dylib`, and dyld runtime tokens are not response files.
* fix(release-exposure): keep a dropped host outside an ASAR layout unread whatever its name
The guard for a sampled-out host beside a found ASAR layout ruled images
out by filename, so an asar: false host named like `b/My.App` was missed
and safe fuses from the sampled layout settled the installation. Every
inventoried native image outside the found layouts that the sample
dropped now keeps the fuse checks unknown.
* fix(release-exposure): keep maps unassessed for an unreadable extensionless launcher
An unclassified file whose initial header probe fails, such as an
extensionless `runner` that became unreadable after inventory, may carry
a Node shebang and a map directive. The probe failure now leaves its
maps unassessed, as an unread `runner.js` or packaged member does,
instead of counting the file as holding no script.
* fix(release-exposure): read a response file named like a dyld token
`-Wl,@rpath/private-link.rsp` names a linker response file, not a dyld
runtime path, so dropping every `@rpath` or `@loader_path` argument hid
it. A dyld token is now skipped only as the value of an option that takes
one, such as `-install_name` or `-dylib_file`.
* fix(release-exposure): keep the partial member scan on ordinary cancellation
A scan cancelled after some members were examined reported its partial
scan only when cleanup also failed, so a clean cancellation dropped the
recovered source-map and credential observations. A cancelled scan now
hands its assembled facts to the interrupted-scan visitor with unknown
coverage and a cancellation limitation, and the caller still receives
the cancellation error carrying that partial audit.
* docs(release-exposure): describe the partial report kept when the member scan is cancelled
Since the member scan keeps what it examined on ordinary cancellation,
the status guide no longer says that cancelling the scan returns no
partial report. It now describes the partial observation and limits the
no-report case to cancellation before the scan can be assembled.
* fix(release-exposure): keep maps unassessed when a text header is cut short
A text-named member read with one or two bytes of the text allowance
left returns only `#` or `#!`, which cannot establish a Node shebang, so
`runner.txt` kept its non-script role and its unexamined inline map was
treated as absent. A partial or missing header that could still start a
shebang line now leaves the member's maps unassessed, as an unread
member's do once the allowance is spent.
* fix(release-exposure): keep dotted executables as possible hosts in ASAR layouts
Inside an ASAR layout, a sibling whose name had a dot and no `.exe`
suffix, such as `my.app`, was neither ranked for the native sample nor
counted as a possible Electron host. With shared libraries filling the
sample, that image could be dropped while the layout still counted its
host as read, so safe fuses on a helper reported `run_as_node` as not
observed. A dotted sibling that is not named like a shared library is
now sampled after plain siblings and counted as a possible host.
* fix(release-exposure): split env -S shebangs on unquoted blanks only
The env shebang reader split the whole line on spaces before handling -S,
so a launcher such as `#!/usr/bin/env -S NODE_OPTIONS='--conditions
development' node` named `development'` as its program and its maps were
never assessed. An -S operand now takes the rest of the line and is split
the way GNU env -S splits it: single and double quotes, backslash escapes,
\_ and \c, and a leading # comment. A string env itself rejects, such as an
unterminated quote, runs no program.
* fix(release-exposure): keep owner facts completed before cancellation
When a Go, Rust, fuse or desktop owner finished its Evidence and then saw
cancellation, completeProviderEvidence returned that Evidence as the
error's partial observation, but the audit only kept the error and
substituted a not-run fact, so a recovered Go build path or Rust package
list reported unknown. Each owner's Evidence-to-fact reading now lives in
one function used for both a successful read and a completed partial, so
the partial passes the same schema and inventory digest checks before its
fact is kept. Later owners still do not run.
* fix(release-exposure): keep env -S programs named by a variable unassessed
`env -S` expands `${NAME}` from the environment, so a launcher such as
`#!/usr/bin/env -S ${NODE_BIN}` may run Node, but the shebang reader kept
the placeholder as the program name and treated the file as plain text,
reporting its inline map as absent. The env splitter now marks fields that
expand a variable, and a program whose name holds one leaves the shebang
undecided: the member's maps count as unassessed, and a `.map` companion
with such a header is not taken as a linker map. Quoted or escaped `${`
stays literal, and a resolved basename such as `${DIR}/bin/deno` still
decides.
* fix(release-exposure): keep the ASAR header admission for selected files
A directly selected app.asar whose header exceeds asar.max_header_bytes
now returns the typed header admission refusal and its recovery, as the
directory scan already does, instead of a generic partial report.
* fix(release-exposure): keep a map member with a variable env -S program unassessed
An undeclared `runner.map` whose `env -S` program is `${NODE_BIN}` may
run Node, so `examineMapMember` now treats an undecided `nodeShebang`
result as an unassessed map and companion, as `classifySiblingMap`
already does, rather than falling through to a linker-map guess.
* fix(release-exposure): read a forwarded -Map linker map path
splitForwardedLinkerFlags hands -Wl,-Map=private-link.map to the linker
side, but LINKER_PATH_FLAG had no Map entry, so the relative map path was
dropped and go.build_paths read not_observed. Add Map to the file-valued
linker options so -Map=FILE and -Map FILE are both reported.
* fix(release-exposure): count unread text in undecided map branches
An oversized .map whose shebang names its program with an unresolved
variable, or a package member with an undecided PE header, returned
before counting its unscanned text, so credential markers could read
not_observed. Count any non-bytes read as skipped text in both branches.
* fix(audit): bound map reads and preserve linker paths
* fix(audit): retain unread package uncertainty
* fix(audit): retain ambiguous linker operands
* fix(audit): preserve compiler path separators
* fix(audit): retain distinct linker map occurrences
* fix(audit): apply map declarations to their exact source path
A declaration recorded only its NFC lookup key, so every member or
sibling sharing that key took the declared JavaScript-map route. On a
normalization-sensitive filesystem an unrelated linker map spelled in
another form lost its companion evidence when the declaring script was
scanned first.
Record the resolved spellings each declaration writes and treat only an
exact match as declared. Another spelling in the bucket is read as an
undeclared map; when no declared spelling is reached, its linker-map
guess stays unresolved coverage rather than companion evidence.
* fix(audit): keep partial inventory from settling fuse-host coverage
When a directory read found ASAR layouts but the inventory stopped
early, a separate asar: false build outside those layouts may never
have been listed. Only sampled-then-dropped images counted as a
possible missed host, so reading every found layout's host reported
safe fuses as not_observed for the whole selection.
In limitNativeSample, treat an incomplete inventory as a possible
missed host outside the found layouts unless the read root is itself
a found layout, where host identity already settles coverage.
* fix(audit): apply script targets to their exact source path
A page's script element was recorded only by its NFC lookup key, so on
a normalization-sensitive filesystem a distinct file spelled in another
normalization form inherited the JavaScript role. An unrelated linker
map such as café.map beside a loaded café.map was never classified
and debug companion coverage could settle as not observed.
Keep the resolved spellings each page wrote under the lookup key, as map
declarations already do, and give the script role only to a member or
sibling whose exact path a page loaded.
* fix(audit): keep partial inventory uncertain beside a root ASAR layout
When the read root was itself a found ASAR layout, an incomplete
inventory was trusted to have listed every possible fuse host. A second
asar: false build under a subdirectory names no layout and sits outside
the root layout, so an inventory that stopped after the root host could
miss its launcher and still report the root's safe fuses as
not_observed. The same launcher, once listed, already kept the result
unknown.
Drop the root-layout exemption in limitNativeSample so any incomplete
inventory beside a found layout keeps fuse-host coverage unknown. A
complete inventory that lists only the named host still settles.
* fix(audit): read the linker's --just-symbols file operand
A Go build that forwarded -Wl,--just-symbols=private.syms through
-extldflags or CGO_LDFLAGS kept the relative symbol file in its build
settings, but the forwarded linker matcher knew only the short -R
spelling. Complete metadata then reported go.build_paths as
not_observed while the equivalent -R build was exposed.
Add --just-symbols to the linker options that read a file. Only -R
turns into -rpath when given a directory, so the $ORIGIN ambiguity
check stays on the short spelling.
* fix(audit): anchor expanded package members to the selected path
An inventory-expanded package member reports its location relative to
the selected path, such as app.asar/boot.map, but its lookup identity
resolved that location against the process working directory. An
on-disk page loading app.asar/boot.map, or an absolute file: map
declaration into the archive, resolved under the selected root, so the
declaration never matched the member it named. A script target with an
inline map then settled as a linker map, and a declared map stayed
skipped.
Resolve every member lookup and declaration base against the selected
path, and keep the reported location as written. Linker-map retraction
compares the same anchored spelling.
* fix(audit): read GCC's -I- include barrier as an option
CGO_CFLAGS=-I- is GCC's include barrier, the same as --include-barrier,
not -I with the directory "-". The compiler path matcher read it as a
build path, so a Go binary retaining it reported go.build_paths exposed.
Skip that exact field before path matching.
* fix(audit): keep packaged-image uncertainty after reading the host
A native image stored inside a package is never opened by a fuse reader,
so reading the Electron host on disk does not settle its fuses. Count the
packaged omission toward a partial fuse sample on its own, so safe host
implications stay unknown instead of not_observed while a packaged image
is unread.
* fix(audit): require every named host in a layout to be sampled
An ASAR layout can name more than one host, such as `a/a.exe` beside
`a/electron.exe`. The sample counted the layout as read when any one
named host was kept, so dropping the other still reported the Electron
executable as included and let safe fuses from the sampled images settle
run_as_node as not_observed. Count a layout as read only when every host
it could have was sampled, reusing the named-host selection the fuse
reducer already applies.
* fix(audit): keep a layout-less bundle read from settling partial inventory
A bundle with no ASAR layout, such as an asar: false build, counted its
framework read as covering the Electron host even when inventory stopped
early or the sample dropped an image. A nested build's host could sit in
the part never listed, so the root framework settled RunAsNode as
not_observed. Apply the same incomplete-inventory guard the layout branch
uses to the bundle read.
* fix(audit): read the linker's --sysroot directory operand
A Go ELF build that forwarded -Wl,--sysroot=private-sdk through
-extldflags or CGO_LDFLAGS kept the relative sysroot in its build
settings, but the forwarded linker matcher did not know the option.
Complete metadata then reported go.build_paths as not_observed, while
the compiler-owned --sysroot was already recognized.
Add --sysroot to the linker options that take a path, so the -Wl comma
form, the -Wl = form and both -Xlinker forms are read.
* test(audit): group Deno boundary regressions
* fix(audit): preserve Go linker file operands
* fix(audit): track script completion per source occurrence
* refactor(audit): give Go path parsing one semantic owner
* refactor(audit): preserve map roles at the parsing boundary
* refactor(audit): consume toolchain operands once
---------
Co-authored-by: Matt Van Horn <[email protected]>
Co-authored-by: morluto <[email protected]>
* fix(desktop): preserve cancellation after output publication
* fix(desktop): retain cancellation in combined output failures
* fix(desktop)!: preserve cancellation origins in state workflows
Keep physical failures primary when cancellation arrives during cleanup. Migrate application checkpoints and composed readers to existing typed cancellation owners while retaining exact reasons and cleanup leases.
BREAKING CHANGE: Internal desktop readers must report cancellation with AnalysisCancelledError or ArtifactReaderFailure instead of throwing an arbitrary caller cancellation reason.
* fix(desktop): preserve cancellation origins in file reads
fix(desktop): validate ELF dependency string mappings
Recover the preserved desktop-domain audit repair omitted from the library-identity publication. Reuse the canonical virtual span mapper for complete, unambiguous file backing and shared PT_LOAD admission; retain the original inert malformed/alias controls with an adjacent-mapping control.
* fix(website): keep the HTML validator when the Markdown route is absent
A caller that prefers Markdown gets the HTML probe with its conditional headers
deleted, so the Markdown route can be evaluated on its own. When the Markdown
sidecar is missing the worker answers with that probe, which never saw the
caller's `If-None-Match`, so a matching HTML ETag returned 200 instead of 304.
Ask the asset service again with the caller's own request before answering with
HTML. The decision moves into `htmlFallback` so the request handler keeps its
complexity below the repository bound.
Verified by executing `node --test website/worker.test.mjs`: without the change
the new case fails with 200 instead of 304, and all four cases pass with it.
`oxlint` reports no findings and `oxfmt --check` passes. The website worker test
is not part of a CI workflow, so this suite runs locally only.
* fix(scripts): resolve package-map imports in the module-boundary guard
The guard resolved only relative, absolute and `file:` specifiers, so every
`#`-prefixed package-map import returned no target and was skipped. A pure-layer
file importing `#javascript-analysis-worker` passed, while the same import
written relatively failed with a `pure-layer` diagnostic: the gate failed open
on the spelling the package map exists to encourage.
Resolve a package-map specifier to the source file that produces its built
output, then apply the same ownership rules. The package map is read once per
repository root.
Verified with the new `scripts/lib/module-boundaries.test.mjs` (2 pass; the
package-map case failed before the change) and by running the complete guard on
this checkout, which still reports 4138 files with no findings, so the new rule
adds no false positive. `oxfmt --check` passes; `oxlint` reports the same single
pre-existing `failedBoundary` complexity warning it reported before this change.
The script tests are not part of a CI workflow, so this suite runs locally only.
* fix(scripts): normalize package targets as file URLs
---------
Co-authored-by: 吴佳浩 <[email protected]>
Both function inventory and project queries now retain runtime editability and name the recoverable setting when reset exposes an invalid launch seed. Four provider-boundary regressions fail before the fix and pass afterward.
Co-authored-by: gensokyo-dev <[email protected]>
Retain the original stack merge resolutions for runtime update modes, migrated guard exceptions, deadline ownership and boundary expectations on the merged settings base.
Co-authored-by: gensokyo-dev <[email protected]>
An invalid REA_KUNA_TIMEOUT_MS throws once its runtime override is reset. That check now happens before either shared-slot acquisition, so a failed independent decompile cannot leave the provider-wide pool blocked.
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.
Startup no longer rejects a runtime-owned REA_ seed, so update_analysis_settings can replace it. Debugger construction keeps only the restart-only caps, Kuna stores the launch baseline rather than a cached override, and the advertised operations include GDB close, Rizin close, Kuna list_procedures, and the stable-file ChangeTime callers.
The bridge ceiling lists every Hopper and Ghidra bridge operation plus the Cutter calls, and a test fails when a registry entry is missing. ChangeTime observation and the other launch-seed parsers read an accepted override before rejecting an invalid value.
JEB, Kuna, Windows, debugger, and Cutter consult an accepted override before rejecting an invalid launch value. JEB and Hopper startup deadlines keep default, setting, or runtime as the limit source, and a Kuna timeout without its own limit uses the selected project deadline source.
Startup timeout wrapping keeps the debugger setting limit. Pwntools and Kuna failures report the selected source. Cutter session and command operations can inspect the bridge ceiling.
GDB reads the accepted command-output cap when it admits a command. Cutter uses that same accepted bridge ceiling for mismatch and request checks. Kuna project-timeout metadata includes the operations that wait on the shared export.
Part of #9881. Move the limits checked on each call into update_analysis_settings, keep their REA_ seeds and bounds, and reject updates outside the allowed range.
Retain existing merge resolutions for update modes, source-scan failures and external-reader fixtures while replaying the settings layer on the merged inventory. Preserve parent provenance, candidate counts and presentation-test resource bounds.
Co-authored-by: gensokyo-dev <[email protected]>
A one-shot CLI failure tells the caller to set the launch environment for the next REA process. update_analysis_settings stays on the continuing session. Reused JavaScript workers also keep the limit source that selected them.
analysis_timeout_ms, max_heap_mb, and command_timeout_ms keep the source chosen by the caller, the runtime override, or the built-in default. A request value reports update_mode parameter because update_analysis_settings cannot change that call.
artifact_limits and web-network budgets report update_mode parameter because update_analysis_settings cannot change them. The legacy-name scan no longer reads docs, and the 2026-11-09 Ghidra receipts are restored to the fields they recorded.
Invalid ChangeTime settings now carry update_mode and pending_restart through the configuration error and its projection, including startup parsing. Runtime recoveries name update_analysis_settings instead of telling the caller to restart REA.
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.
The prepared full/full fixture takes 31.73 seconds on two CPU cores; restoring a 20 second child deadline causes a real bounded-run failure. Keep the 60 second child budget and 70 second outer deadline.
Co-authored-by: gensokyo-dev <[email protected]>
A numeric parser now reads property and element accesses in its argument, so Number(process.env.REA_ARCHIVE_ENTRY_LIMIT) is a producer without a schema key. Template discovery keeps generated assignments and no longer treats a diagnostic sentence as a read.
New per-call registry entries are rejected. Inventory checks require a table row, and the Electron argv handoff is process protocol rather than a caller setting.
A constructor copy is not a per-call read. Numeric limits also come from a number schema or a numeric parse, and the inventory no longer lists path and provider constants.
Object bindings, in-checks, and object keys that a spread can copy are producers. The architecture guard skips the TypeScript parse until typescript6 is installed, and the inventory links stay inside the published site.
Property access, element access, and helper-call arguments are producers. Comments and array literals are excluded by the tree, so a later argument such as read(environment, "REA_NEW_MAX_BYTES") stays classified.
Discovery no longer treats comments or registration-list entries as producers. The three limits moving on their own PRs must join ANALYSIS_SETTINGS; the exception registry cannot classify them.
Part of #9881. Record every numeric REA_ limit and fail static checks when one is neither a runtime analysis setting nor a documented exception with a reason.
Validate the Evidence owner on every successful dossier before canonical deduplication. Preserve the unowned alias observation in the typed refusal.
Co-authored-by: gensokyo-dev <[email protected]>
Preserve resolution rows while coalescing identical binding tasks before input-index expansion. A 16-target fixture retains one gap group and one argument binding.
Co-authored-by: gensokyo-dev <[email protected]>
Group exact input indices with caller Evidence after complete parameter examination; preserve budget uncertainty and advance the trace workflow profile.
Co-authored-by: gensokyo-dev <[email protected]>
Move binding finalization into one owner; preserve exact callee selectors while indexing retained parameter and return nodes by the dossier address.
Co-authored-by: gensokyo-dev <[email protected]>
* 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(reference): retain proven loader calls and unresolved targets
* refactor(reference): remove unused ancestor lookup
* fix(reference): drop loader calls that return a reassigned member
An immediate call could still use a member's original require initializer
because mutation facts were not built for a call-valued callee. A definite
assignment that precedes the call now replaces that initializer.
* fix(reference): select returned loaders from indexed read facts
* fix(reference): refresh loader facts for wrapped returned members
A sequence such as (0, box.load) still used the original require
initializer. Follow sequence, conditional, and logical returns to the
member before deciding whether a prior assignment replaced it.
* test(reference): record unresolved CommonJS targets in source evidence
unknown.cjs calls require with a non-literal target. The inventory now
keeps that coverage limitation on the file entry, and the boundary
fixture asserts it beside the resolved member relationships.
* fix(reference): cover opaque apply lists and assigned returned members
require.apply(null, args) has no normalized argument list, so the
inventory now records an unresolved target instead of complete coverage.
An assignment return such as f = box.load is followed to the member
before a prior replacement is trusted.
* fix(reference): keep self-defaulted module delegates loadable
Timing a member read at a later call made `box.load = box.load || null` observe its own write and drop the native delegate. Reads that already fed an earlier assignment stay at the member.
---------
Co-authored-by: Cursor Agent <[email protected]>
* fix(elf): keep compressed storage out of decoded metadata facts
* fix(elf): preserve virtual relocation identity
Keep linked initializer backing and relocation values in their owning virtual
or ET_REL section coordinates. Replace the IL2CPP physical-copy materializer
with compact virtual value facts while preserving file-backed capacity.
Reuse initializer repairs and original regressions from ShuaixinHuang PRs
repair retains independent alias values instead of blanket alias refusal.
Co-authored-by: 帅鑫 Huang <[email protected]>
* fix(elf): admit compressed string tables the profile does not read
SHF_COMPRESSED string tables are rejected only when they name sections
or symbols. An unreferenced string table is not decoded, so it no longer
makes an otherwise analyzable ELF unsupported.
* fix(il2cpp): admit virtual pointer workspace before retention
* fix(elf): reject null relocation target sections
* fix(il2cpp): avoid decoding unrelated Mach-O words
* fix(il2cpp): preserve uncertainty in candidate scans
* fix(il2cpp): bound uncertain array candidates
* fix(il2cpp): preserve unknown registration candidates
* fix(il2cpp): classify type kinds before bound data
* fix(il2cpp): bind candidate counts to matching names
---------
Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: 帅鑫 Huang <[email protected]>
* fix(artifacts): keep an all-zero MD5 note as metadata
A Godot member with an all-zero MD5 records a limitation instead of a
digest. That note is the metadata difference from a member that claims
an MD5. A caveat without a one-sided integrity claim stays unchanged.
* fix(artifacts): compare declared digests without limitation text
A one-sided declared_integrity claim is already a metadata difference.
The limitation-array helper could not change that result and treated
unrelated observation caveats as source metadata. Remove it.
* test(artifacts): describe the one-sided MD5 claim as the metadata fact
The Godot acceptance case still expects metadata when one member has a
declared MD5 and the other has an all-zero MD5. That difference is the
missing claim, not the reader's limitation note.
---------
Co-authored-by: Cursor Agent <[email protected]>
* fix(pe): 保留证书名称的 RDN 分组
* fix(pe): distinguish multi-valued RDNs in certificate name text
Join attributes that share an RDN with "+" and keep ", " between RDNs,
in encoded order. RFC 4514's RDN reversal is not applied, so names made
only of single-valued RDNs keep their existing spelling.
OID 2.5.4.10 is organizationName, so one RDN renders as
commonName=A+organizationName=B and two RDNs as
commonName=A, organizationName=B.
---------
Co-authored-by: AixSxT <[email protected]>
Co-authored-by: Cursor Agent <[email protected]>
* fix(verifiers): retain native cleanup ownership on failure
* fix(verification): load cleanup fixtures through the module resolver
CodeQL's local threat model treats file contents passed to
vm.SourceTextModule as user-controlled code execution. Import the real
verifier modules and redirect their dependencies to static mocks instead
of compiling script text read from disk.
* fix(verifiers): own CLI and MCP process families
* fix(verifiers): retire workspaces after cleanup retry
---------
Co-authored-by: Cursor Agent <[email protected]>
* fix(desktop): retain capture identity and cleanup ownership
* fix(desktop): validate destinations and finish inputs before export
* fix(desktop): preserve package ownership and apt trust boundaries
* fix(sqlite): preserve observed facts and distinguish special real values
* fix(desktop): bind computer files to data-object metadata
* test(desktop): count the tagged negative-zero cell spelling
SQLite view cells encode negative zero as a real object so it stays
distinct from the text "-0". The byte budget follows that spelling.
* fix(desktop): keep unresolved discovered state directories
Canonicalizing an unreadable path or dangling symlink aborted capture
before the inventory could record that the path was unknown. Those
discovered directories now keep their absolute spelling in the scope.
* test(desktop): keep the capture scope suite within its line limit
The unresolved state-directory case lives beside the suite so the
existing callback stays within the lint limit.
* fix(desktop): retain cancellation through capture input cleanup
* fix(desktop): avoid reserved Pydantic settings properties
---------
Co-authored-by: Cursor Agent <[email protected]>
* fix(javascript): preserve semantic facts across writes and implicit effects
* fix(javascript): preserve bundler identities and helper bindings
* fix(javascript): retain semantic graph evidence and coverage
* test(javascript): include the request link in endpoint traces
Response consumers now also link the originating request to the response, so an endpoint trace reports both that association and the response's json consumer.
* fix(javascript): keep source accessors known after a copied value escapes
An unknown computed getter makes accessor lookup consider every property.
A spread copy already records a value escape, and that escape was treated
as an unknown getter on the source, so the next read escaped the whole
object. Accessor lookup still fails closed for unknown writes and calls.
* fix(javascript): keep Object.defineProperty identified at its own call
The call that escapes Object was treated as a prior replacement of
defineProperty, so a descriptor value never replaced the exported sender
and bundled IPC paths were dropped.
* fix(javascript): keep Vite preload calls that precede reassignment
Helper identity used every write to the binding, so a later assignment
erased a call that had already read the canonical helper. Use the binding
value at the call when that order is known.
* fix(javascript): apply Vite cache mutations before a later reassignment
A later helper write made the whole-binding initializer check ignore an earlier cache mutation. Identity at that mutation uses the same ordered read as the call.
---------
Co-authored-by: Cursor Agent <[email protected]>
* fix(setup): retain Hopper operation failure attribution
* fix(setup): retain falsy Hopper abort reasons from body reads
After response headers, Node's fetch body reader rejects a null, false,
0, or empty-string abort with a fresh AbortError instead of that reason.
Replace that substituted error with the caller's reason. A body
AbortError while the caller has not aborted still propagates.
* fix(setup): preserve independent response body aborts
* fix(setup): identify aborts through owned transport
---------
Co-authored-by: Cursor Agent <[email protected]>
* fix(ci)!: verify exact conformance fixture records
BREAKING CHANGE: Regenerate unversioned conformance manifests with npm run build:fixtures. Schema version 2 separates exact string markers from intentional fragments and rejects the ambiguous strings field.
* test(conformance): wait out a cold verifier instead of treating SIGTERM as rejection
The compiler-produced complete/REA_C_ENTRY run was killed by its 10s
execFile timeout on the loaded source-test runner, with empty stderr and
SIGTERM. The stricter record checks already passed locally and for the
other compiler-produced cases. Assert the process status and give that
cold run room inside the test budget.
* fix(ci): preserve carriage returns in nm symbol records
nm -j terminates each name with LF. A carriage return before that LF is
part of the symbol. Splitting on CR LF dropped it, so a renamed symbol
ending in CR matched the name without it.
* test(ci): supervise conformance verifier subprocesses
---------
Co-authored-by: Cursor Agent <[email protected]>
* fix(api-contract): retain partial OpenAPI evidence
* docs(api-contract): describe partial OpenAPI Evidence on interruption
Early failures still keep the file snapshot hash. Cancellation or the
retained-facts limit after a row now returns authenticated Evidence, and
the guide explains how to reuse that partial inventory.
* fix(api-contract): count finished paths and keep earlier diagnostics
A path counts only after its item is read, so a path refused before
traversal does not increase path_entries_examined. A paths diagnostic
that exhausts the retained-facts allowance still keeps diagnostics
already stored, including invalid info.
* test(api-contract): keep invalid info when paths exhaust the allowance
The paths diagnostic can fail after invalid info is already stored. The
partial report must still carry that diagnostic and count no path entries.
* docs(api-contract): name every retained row and the view source
The hash remains only when no operation, reference, or diagnostic row was
kept. Partial Evidence is reread through inspect_analysis_view's source
and view, not as separate Evidence fields.
* test(api-contract): verify interrupted inventory evidence
---------
Co-authored-by: Cursor Agent <[email protected]>
* fix(adb): reject unrepresentable activity arguments
* test(adb): lock complete activity component boundaries
The CLI and advertised schema still admitted a slash-free 512-character
component after admission required a nonempty package and class. Keep
that length sample rejected, admit a pattern-valid 512-character
component, and assert the typed field error.
* style(docs): format NTFS verification tables
oxfmt rejects the tables merged from main. The alignment is formatting
only, so static checks can run on this branch.
* test(adb): use a well-formed component for the length constraint
An over-long run of letters also fails the package/activity pattern, so the selector check reported both issues. The length fixture now has a valid component shape.
---------
Co-authored-by: Cursor Agent <[email protected]>
Validate the final 64-bit payload before interpreting unwind LEB operands,
and retain valid ten-byte signed padding. Keep invalid records incomplete
without discarding independent valid FDEs.
Adapted from ShuaixinHuang’s closed PR #9872, original head
021ab3ad62. Original fixture, regression
and documentation changes are retained; decoder comments are shortened.
Co-authored-by: 帅鑫 Huang <[email protected]>
* fix(elf): keep compressed storage out of decoded metadata facts
* fix(elf): admit compressed string tables the profile does not read
SHF_COMPRESSED string tables are rejected only when they name sections
or symbols. An unreferenced string table is not decoded, so it no longer
makes an otherwise analyzable ELF unsupported.
* fix(elf): keep requirement names when verdef declares no entries
A compressed GNU version-definition section with sh_info 0 has no
definitions to decode. Skipping it leaves requirement names in place
instead of clearing them as an unread compressed table.
* fix(elf): skip empty version requirements before storage reads
* fix(elf): skip empty compressed version-requirement tables
A GNU verneed section with sh_info 0 has no requirement records. Reading
its compressed bytes was a version failure and partial coverage; the
declared count now skips that read.
* fix(mips): distinguish compressed metadata storage (#9959)
* fix(elf): note a missing symbol table only when a relocation needs it
A sectionless image with direct DT_INIT and DT_FINI no longer becomes incomplete solely because DT_SYMTAB does not match a decoded table. The same note remains when a dynamic relocation asks for a symbol.
---------
Co-authored-by: Cursor Agent <[email protected]>
* fix(process): revalidate ownership before signaling
* fix(process): retain cleanup ownership until completion
* fix(process): keep omitted live descendants unresolved
A non-empty process table that omits a retained live PID cannot authorize a signal or certify cleanup. An empty table can still describe a token-verified owner.
* fix(process): read omitted Darwin run tokens by PID
A live identity that ps omitted was still validated through Darwin
environment(), which rejects any PID absent from a second process table.
Ask the native reader for that PID directly so the launch token can bind.
---------
Co-authored-by: Cursor Agent <[email protected]>
* 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 <[email protected]>
* fix(native): bind initializer evidence to selected Mach-O slices
* fix(native): reject initializer slices outside the artifact
* test(native): retain completed initializer evidence on late cancellation
completeProviderEvidence keeps the finished report on
AnalysisCancelledError.partialObservation and the caller's abort reason.
The regression only checked the error tag, so dropping that evidence
would pass. Restore both structured assertions.
* fix(native): bind initializer facts to the selected slice label and range
A universal report could keep an arm64 cpu_type beside an x86_64 slice
label, and entry file offsets only had to fall inside the container.
Require the slice architecture to match the request, and require each
slot and value file offset to lie in that slice.
---------
Co-authored-by: Cursor Agent <[email protected]>
* fix(cli): retain cancellation observed during final cleanup
* fix(cli): cancel reference source imports through cleanup
* fix(cli): preserve signal status through command logging
* fix(views)!: distinguish unavailable artifact collections
Keep missing BEAM tables and unavailable Go and Rust build metadata
separate from decoded empty collections. Retain producer limitations
on model metadata, tensor, and type pages.
BREAKING CHANGE: BEAM, Go, and Rust analysis view pages for unavailable
collections now report coverage.status "unavailable", coverage.total
null, and coverage.exhausted false. Refresh saved result schemas and
handle unknown totals before using coverage to infer absence.
* test(views): expect unavailable Go dependency pages
Missing Go build metadata is unavailable coverage, so the MCP page reports a null total instead of an empty collection.
* test(fixtures): ignore a torn build-computer note line
The cancellation test reads the helper's note file while the append is
still in progress. An empty file or an unfinished last line is not a
record yet. A completed line still has to parse.
* fix(fixtures): reject terminated malformed build-computer notes
Trim removed the final newline, so a completed corrupt record looked like
a torn append and the preceding notes were returned. Keep the terminator
and parse every finished line.
---------
Co-authored-by: Cursor Agent <[email protected]>
* fix(firmware): distinguish empty ESP flash partitions from missing bytes
* fix(firmware): keep empty ESP partitions that start before the dump
A zero-byte partition below the selected flash origin was reported as
unread and omitted from the inventory. It declares no bytes to read, so
the plan keeps the empty region and the zero-byte diagnostic.
* fix(firmware): clamp an empty partition before the flash origin
A zero-byte partition below the selected origin has no file bytes. Its
inventory offset is the start of the carrier, which stays nonnegative.
* fix(firmware): clamp empty ESP entries that start before the dump
A zero-byte partition below the caller origin kept a negative file
offset, which the inventory published as a negative source location.
The empty entry now sits at the start of the carrier.
---------
Co-authored-by: Cursor Agent <[email protected]>
* fix(agentenv): bind solve results to the named verifier
* fix(agentenv): retain a planned solve pass through selected-verifier binding
Cleanup and cancellation tests plan a completed pass with no criterion
rows. An empty selected-verifier result list is unscored, so those
reports lost outcome passed even though sandbox retention and the
cancellation cause were unchanged. Record one selected result for a
planned pass or miss so the bound outcome stays the planned one.
---------
Co-authored-by: Cursor Agent <[email protected]>
* fix(desktop)!: preserve build evidence and validate task inputs
BREAKING CHANGE: Witnessing requires the current generated verifier. Re-export bundles with older evaluators using the original capture Evidence and task inputs.
* test(desktop): match reader source inspection diagnostics
Unreadable reader sources now report the source inspection failure, including the operating-system reason, instead of the old file wording.
* fix(environment): advertise canonical bundle names (#9939)
fix(environment): advertise portable bundle name constraints
---------
Co-authored-by: Cursor Agent <[email protected]>
* fix(config): preserve client settings during launcher refresh
* fix(setup): retain parsed client launcher ownership
* fix(uninstall): retain resource cleanup diagnostics
* test(setup): keep temporary paths inside the workspace root
Resolve setup fixtures through the test workspace path helper so joined paths stay under the temporary root.
* test(setup): validate temporary paths before filesystem calls
Contain workspace children and the symlink target before creating the launcher alias so a temporary path cannot leave its root.
* test(setup): expect owned command identity in BOM registration status
Aligned client registration status includes command_identity. The BOM
read-back compared the status without that field, so an owned launcher
failed the equality check.
---------
Co-authored-by: Cursor Agent <[email protected]>
* fix(targets): select a preferred Mach-O host subtype
Host admission refused every extra slice of the host CPU type, so arm64
plus arm64e and arm64e ABI variants never became universal targets. Rank
known subtypes and keep the earliest capability variant. Identical
subtypes and ties between unlisted bases still fail closed.
* fix(artifacts): treat a one-sided declared digest as metadata
An all-zero Godot MD5 was visible only because limitation text differed.
After comparison stopped using that prose, a claim on one side and no
claim on the other lost its metadata dimension. Compare the structured
integrity claim instead.
* fix(browser): keep unavailable resource inventories unknown
Directional coverage treated a resource retained from an invalid protocol
tree as an addition against a complete peer. An unavailable collection is
not a truncated sample, so it cannot prove membership.
* fix(native): read the admitted Mach-O slice for metadata
Capability-variant admission chose the first slice, then dispatch
metadata and slice tools ranked the FAT file again and rejected that
pair. Pass the admitted offset, and refuse a winner set that repeats
a subtype.
---------
Co-authored-by: Cursor Agent <[email protected]>
Run oxfmt on the NTFS verification notes and the MFT plan so format:check
matches the repository formatter.
Co-authored-by: Cursor Agent <[email protected]>
* fix(cli): validate named inputs before target admission
* fix(mcp): preserve valid inputs when advertised schema references widen
* test(inputs): reach session behavior with admitted inputs
One-shot admission now rejects incomplete requests before a session exists. Lifecycle tests supply the required fields, and unknown arguments are rejected before provider issues are retained.
* fix(cli): preserve Electron fuse path spelling
Anchor relative fuse paths without stripping dot segments, and let an empty path reach the operation so it still reports invalid_request.
* fix(cli): reject outline-depth unless detail is summary
The advertised schema already required detail summary whenever outline-depth
was present. The parser still applied the full default and accepted that
combination, so its output failed the same schema. Parsing now rejects it
before the command runs.
---------
Co-authored-by: Cursor Agent <[email protected]>
* fix(ci): retry interrupted native profile downloads
The immutable mitmproxy GET can reset after a partial body with curl 56,
which the existing default retry policy does not retry. Retry transfer
errors within the existing attempt count and keep the checksum gate.
Co-authored-by: gensokyo-dev <[email protected]>
* test(ci): verify native profile download recovery
Execute the named workflow step with real curl, a local HTTP server and
a tiny archive. Cover TCP reset recovery, retry exhaustion, HTTP failure
and digest rejection before profile extraction or environment export.
Co-authored-by: gensokyo-dev <[email protected]>
---------
Co-authored-by: gensokyo-dev <[email protected]>
Cursor Marketplace installs are Git checkouts, so the plugin manifest
points at a synced copy of the authored skill and starts
npx -y rea-agents@<release> mcp. Release Please and the release
checkpoint keep that pin aligned with the package version.
The manifest, skill, and icon checks follow the local plugin bundle
contract from #7619, adapted for a checkout that has no dist/.
Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: GamerDubz <[email protected]>
Keep inventory counts inline and preserve explicit 1–32 record detail requests. The real 26-record Windows sample serialized 57,946 metadata bytes before detail selection.
Co-authored-by: gensokyo-dev <[email protected]>
Keep attribute parsing at the verified record boundary and retain only the identity index for journal joins.
Co-authored-by: gensokyo-dev <[email protected]>
Retain one completed pair per resource scope, revalidate selected file state on every page, invalidate failures, and clear retained bytes at shutdown. Keep cold CLI process costs explicit.
Co-authored-by: gensokyo-dev <[email protected]>
* fix(browser-scenario): page captured text on Unicode boundaries
* fix(browser-scenario): retain popup events when open metadata exceeds its budget
* fix(browser-scenario): redact saved secrets across wire encodings
* fix(browser-scenario)!: record parsed request actions in evidence
BREAKING CHANGE: New captures with request actions that save nothing include save: [] in the recorded parameters. Their scenario and Evidence digests can differ from older captures of the same scenario. Read retained captures with their original recorded digests and use the newly recorded digest for new captures.
---------
Co-authored-by: Cursor Agent <[email protected]>
* 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 <[email protected]>
---------
Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: N0zoM1z0 <[email protected]>
Co-authored-by: gensokyo-dev <[email protected]>
The new NTFS $MFT files were committed unformatted, so the Static checks lane
failed on format:check. Formatting only; no behaviour change.
(cherry picked from commit dc6a6d40ff)
An acquired $MFT stream had no reader, so an analyst holding one could only
treat it as an opaque file. Recognize the FILE signature of the first record
and inventory every record's header: sequence number, flags, used and total
bytes, base record reference and update-sequence array.
Each record's update-sequence fixups are verified against its sector
trailers. A mismatch keeps the header facts, is reported on the record and is
counted in the metadata, so a record that was not cleanly written is visible
rather than silently accepted. A missing or short array is reported as
unverified, a wrong signature stops the scan at its offset, and a trailing
partial record is reported as a byte count. Attributes, filenames and the
$UsnJrnl:$J journal are not decoded, so no path or change history is claimed.
Part of #9867
(cherry picked from commit ccb45f5712)
Attempt independent resource cleanup without losing primary failures, retain unresolved owners, and publish completion after release. Bound existing producer commands, preserve helper return values, and settle startup and fixture children on failure.
Validation: 29 focused boundary tests passed; actual helper-value and import-bound filesystem cleanup controls passed. The real inert-child termination control and targeted JavaScript binding checks passed, alongside typecheck, lint and formatting. Live Ghidra, Cutter, Electron GUI and Windows addon qualification was not run.
Since Linux 6.1, kbuild compiles init/version.o with a temporary
UTS_VERSION. Its build number and timestamp stay empty unless
KBUILD_BUILD_VERSION or KBUILD_BUILD_TIMESTAMP is set. That weak
linux_banner stays in the image ahead of the banner from
init/version-timestamp.o, and the first-match scan reported it. The WSL
6.18 kernel showed '# SMP PREEMPT_DYNAMIC ' instead of
'#1 SMP PREEMPT_DYNAMIC Tue Oct 6 19:13:07 UTC 2026'.
Treat a banner with a space right after '#' or at its end as the
placeholder. Keep scanning and report a later banner that repeats its
text up to '#' and is not a placeholder. Report the placeholder when no
such banner follows.
* fix(windows): leave a shortcut icon comparison unknown across code pages
Windows stores the LinkInfo LocalBasePath only as ANSI when the system
code page can represent it, while StringData stays UTF-16. The parser
projects the ANSI bytes as Latin1, so on a code page 936 host a path such
as F:\百度网盘\BaiduNetdisk\BaiduNetdisk.exe never matched the same
Unicode IconLocation, and icon_location_differs_from_target reported true
for a shortcut whose icon is its own target.
Record whether each target path is an ANSI projection. Compare it with
the icon only when both use the same encoding or the ANSI text is ASCII.
A full target that cannot be compared makes the result null, like a
%VAR% target.
* fix(windows): keep each LinkInfo part's encoding when joining paths
A joined LinkInfo target was marked ANSI when any part was ANSI. An ASCII
ANSI share with a UTF-16 suffix such as \\server\share + 工具\app.exe
then could not be compared with a Unicode icon, although no byte needs a
code page. Record which encoding each part's non-ASCII text came from,
and compare two paths when their non-ASCII text shares one encoding.
* fix(process): recognize permission-denied user namespace peers
* fix(process): fall back to UID maps when namespace links are denied
---------
Co-authored-by: dhruvac29 <[email protected]>
Co-authored-by: morluto <[email protected]>
* fix(cli): publish partial Evidence to --evidence_output on failure
publishCliAnalysisEvidence returned a failed result unchanged before any
--evidence_output handling. When a source reached its deadline, or another
failure carried partial Evidence, the requested file was not written and
--detail summary still printed the complete partial Evidence inline, which
reached more than 64 MB on a large build.
A failure whose details.partial_observation is authenticated Evidence now
publishes it through the same projection and new-file publication as a
successful result. The failure keeps its code, message and remediation.
With --detail summary, details.partial_observation holds the summary
view, and details.diagnostics.evidence_bundle reports the path and bytes.
A refused publication keeps the complete partial Evidence inline and adds
details.diagnostics.evidence_bundle_error with the refusal and a
limitation. Without --evidence_output, for cancellation, and for raw
provider observations the failure output is unchanged.
Fixes#7787
* fix(cli): keep committed partial Evidence receipts on refused publication
When publishing the partial Evidence of a failed analysis, a refusal can
follow a commit. Staging cleanup that stays incomplete after its retry
returns cleanup_incomplete with published_path and bytes, and a
cancellation after commit returns cancelled with
diagnostics.evidence_bundle. The failure path read only the second form,
so a committed file with incomplete cleanup was reported as not
published, without its receipt or the cleanup resources, and the full
partial Evidence went back inline.
Keep the original analysis failure and record the receipt from either
form in details.diagnostics.evidence_bundle. Incomplete publication
cleanup adds published, published_path, bytes, cleanup and resources to
details, the shape a successful run's cleanup_incomplete reports;
resources join any the analysis failure already reports, and its own
cleanup reason is kept. Once the file is committed, the delivered view
(the summary with --detail summary) replaces the inline partial Evidence.
Receipts omit a path that holds an excluded sensitive value, as on
success.
Tests drive the real staged publication through its commit point: cleanup
that stays incomplete, cleanup confirmed by retry, and SIGINT after
commit. A composition test checks receipt redaction on the failure path.
* fix(cli): report incomplete partial publication cleanup
---------
Co-authored-by: morluto <[email protected]>
* fix(cli): refuse an unsupported Node.js release before linking
The dispatcher imported the compiled runtime before checking the runtime
range, so a release outside `engines.node` failed while linking a module
that imports `createZstdDecompress`. Callers saw a module-linking stack
trace from every command, including `--version` and `doctor`, instead of
the refusal the installer and `engines` already promise.
Check `engines.node` from the package read the dispatcher already performs,
before the first compiled import. The helper has no imports of its own: the
dispatcher must admit the runtime before any dependency can be resolved.
Fixes#9296
* fix(cli): ship and align runtime admission
* fix(ci): cover packaged runtime admission helper
* test(cli): assert Node admission with structured range results
Export satisfiesNodeRange and cover caret, minimum, union, and
non-release versions. The dispatcher proves the compiled runtime never
loads by the absence of its marker file, and compares stderr to the
helper's own message.
---------
Co-authored-by: 2SinceJuly <[email protected]>
Co-authored-by: morluto <[email protected]>
Co-authored-by: Cursor Agent <[email protected]>
Validate analyze-javascript-application paths with the shared localPathStringSchema before anchoring or provider execution. Empty paths now return CLI field errors; NUL bytes are rejected at the same boundary. Preserve selected-path Evidence for valid inputs and update the CLI regressions. Closes#9747.
* 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 <[email protected]>
* style(desktop): format the noisy-record draft files with the pinned oxfmt
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* 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 <[email protected]>
* 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 <[email protected]>
* 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 <[email protected]>
* 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 <[email protected]>
* 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 <[email protected]>
* 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 <[email protected]>
* 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 <[email protected]>
* 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 <[email protected]>
* 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 <[email protected]>
* 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 <[email protected]>
* 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 <[email protected]>
* 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 <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
* 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 <[email protected]>
Co-authored-by: gensokyo-dev <[email protected]>
Project analyze_function limitations from the provider instead of a sentence #9246 removed, so the boundary test follows the producer and main CI can go green.
* fix(desktop): preserve local file URL authority
* fix(desktop): preserve local file URL authority
* fix(desktop): preserve local file URL authority
* fix(desktop): render captured file URLs at launch
* fix(desktop): classify numeric timestamps using field context
* fix(desktop): classify numeric timestamps using field context
* fix(desktop): classify numeric timestamps using field context
* fix(desktop): retain context for generated draft values
* fix(desktop): retain context for generated draft values
* fix(desktop): retain context for generated draft values
* test(desktop): cover generated IDs in text guards
* test(desktop): cover generated IDs in text guards
* test(desktop): cover generated IDs in text guards
* fix(desktop): isolate launcher URL encoding
* fix(desktop): isolate launcher URL encoding
* fix(desktop): isolate launcher URL encoding
* fix(desktop): isolate launcher URL encoding
* fix(desktop): bound private key redaction spans
* fix(desktop): retain published bundle on copy failure
* fix(desktop): retain secret file cleanup owners
* fix(desktop): retain both published output cleanup owners
* test(desktop): verify UTF-16 credential refusal
* test(desktop): verify truncated secret redaction over MCP
* fix(desktop): retain clean text in noisy record fields
* fix(desktop): classify added line values in one pass
* fix(desktop): scan private key runs in one pass
* fix(desktop): apply XML quote rules to text drafts
* fix(desktop): distinguish XML attributes from quoted text
* fix(desktop): preserve parsed XML line context
* Format appTaskChecks.ts with the pinned oxfmt
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): retain partial XML syntax facts
* fix(desktop): recognize markdown autolinks in drafts
* fix(desktop): reuse export exclusions in task drafts
* fix(desktop): report unchecked client build paths
* docs(desktop): clarify gzip scrub limits
* test(desktop): build the fixture JWT from its parts and reformat
The literal token made every edit of this file trip secret scanners; the test
checks the same string. Also applies the pinned oxfmt to the key-run test.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): reuse export exclusions in task drafts
* docs(desktop): clarify gzip scrub limits
* fix(setup): own desktop build process cleanup
* fix(desktop): find no private key in NSS's key4.db password check
An Electron app's NSS key database holds a password-check value shaped like
an encrypted private key, so every such export was refused. An encrypted key
now counts only under a password-based encryption scheme, never PBKDF2 or
PBMAC1, and with at least the 48 bytes of encrypted data the smallest key
needs.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(doctor): compare desktop settings with resolved defaults
* style(desktop): format the noisy-record draft files with the pinned oxfmt
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* 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 <[email protected]>
* fix(doctor): inspect client-recorded desktop runners
* fix(doctor): reuse matching runner probes
* fix(setup): propagate cancellation to desktop actions
* 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 <[email protected]>
* fix(desktop): close five withheld-state gaps a review found
- A sign-in window whose walk hit its entry limit, could not read a path or
left a change unconfirmed is refused, rather than trusted as complete.
- The export refuses app dconf keys the sign-in window changed and the seed
would ship with a different value, naming the keys, not their values.
- A bundle holding a gzip file that expands past what REA scans is refused.
- Withheld bytes are kept under a handle of their own, created exclusively and
never replaced; a later build's different bytes take the next free handle,
and a handle the computer's answer names is checked before any write.
- Setting names a Python keyword or a leading digit would break get file_,
and names that collide get _2, _3 in a stable order.
- Adds the feature's tasks.md.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): redact a cut secret's start only at REA's own cuts
The boundary match for a secret cut short hid the start of any ellipsis-ended
text, so "Please…" became a redaction. It now applies only where text ends
one of REA's value cuts, whose lengths the producers and the redactor share,
and the host redacts the app's stderr and progress before it quotes their
end. Redacting before anything cuts stays a follow-up.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* 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 <[email protected]>
* fix(desktop): publish secret files atomically, decompress a gzip seed once, and scan JSON strings linearly
- A secret file is written under a private temporary name and linked to its
handle, so a concurrent export never reads a half-written file and reuses
its handle for other bytes.
- The export decompresses each gzip seed file once and shares the result
with the secret scan, the credential scan and the size refusal.
- The cut-secret stopgap finds JSON string literals in one forward pass
instead of restarting at every quote.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* 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
* fix(desktop): retain temporary secret file cleanup
* fix(desktop): preserve unmarked token-shaped values
* fix(desktop): withhold drafts after secret use
* fix(desktop): guard drafts against shared secret changes
* test(desktop): compare preserved seed bytes without enumeration
* 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(doctor): query agentenv for model endpoint
* fix(doctor): preserve uncertainty for empty endpoint override
* fix(desktop): protect host controls and quote withheld paths
* fix: redact declared form-encoded secrets
* 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): retain record identity in draft checks
* fix(desktop): refuse unchecked declared secrets
* fix(desktop): retain partial withheld export receipts
* fix(desktop): detect escaped secrets in JSON state
* fix(desktop): scrub XML independently of reader compatibility
* fix(desktop): preserve file aliases and reader diagnostics
* fix(desktop): scan escaped secrets in UTF-16 JSON
* style(desktop): format secret scan changes
* style(desktop): format review fixes
* style(desktop): format capture review fixes
* style(desktop): format review changes
---------
Co-authored-by: morluto <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix(desktop): preserve document URLs and value formats
* fix(desktop): bound added checks and omit noisy text guards
* fix(desktop): exclude unseeded locks from task checks
* fix(desktop): preserve draft bounds and launch URLs
* fix(desktop): render draft values using their source format
* fix(desktop): preserve launch URLs and JSON draft values
* fix(desktop): recognize captured file URL documents
* fix(desktop): render captured file URLs for runtime home
* fix(desktop): distinguish phone numbers from save metadata
* fix(desktop): preserve launch URLs and user text
* fix(desktop): preserve captured URLs and user text
* fix(desktop): preserve local file URL authority
* fix(desktop): preserve local file URL authority
* fix(desktop): preserve local file URL authority
* fix(desktop): render captured file URLs at launch
* fix(desktop): classify numeric timestamps using field context
* fix(desktop): classify numeric timestamps using field context
* fix(desktop): classify numeric timestamps using field context
* fix(desktop): retain context for generated draft values
* fix(desktop): retain context for generated draft values
* fix(desktop): retain context for generated draft values
* test(desktop): cover generated IDs in text guards
* test(desktop): cover generated IDs in text guards
* test(desktop): cover generated IDs in text guards
* fix(desktop): isolate launcher URL encoding
* fix(desktop): isolate launcher URL encoding
* fix(desktop): isolate launcher URL encoding
* fix(desktop): isolate launcher URL encoding
* fix(desktop): retain clean text in noisy record fields
* fix(desktop): classify added line values in one pass
* fix(desktop): apply XML quote rules to text drafts
* fix(desktop): distinguish XML attributes from quoted text
* fix(desktop): preserve parsed XML line context
* Format appTaskChecks.ts with the pinned oxfmt
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): retain partial XML syntax facts
* 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 <[email protected]>
* fix(desktop): recognize markdown autolinks in drafts
* fix(desktop): reuse export exclusions in task drafts
* fix(desktop): reuse export exclusions in task drafts
* fix(desktop): report unchecked client build paths
* docs(desktop): clarify gzip seed scrubbing
* fix(desktop): report unchecked client build paths
* docs(desktop): clarify gzip scrub limits
* fix(desktop): reuse export exclusions in task drafts
* docs(desktop): clarify gzip scrub limits
* fix(setup): own desktop build process cleanup
* fix(setup): own desktop build process cleanup
* fix(doctor): compare desktop settings with resolved defaults
* fix(doctor): compare desktop settings with resolved defaults
* style(desktop): format the noisy-record draft files with the pinned oxfmt
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* 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 <[email protected]>
* fix(doctor): inspect client-recorded desktop runners
* fix(doctor): inspect client-recorded desktop runners
* fix(doctor): reuse matching runner probes
* fix(doctor): reuse matching runner probes
* fix(setup): propagate cancellation to desktop actions
* fix(setup): propagate cancellation to desktop actions
* 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 <[email protected]>
* 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 <[email protected]>
* 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
* 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
* test(desktop): use the framework pin in doctor fixtures
* 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(doctor): query agentenv for model endpoint
* fix(doctor): preserve uncertainty for empty endpoint override
* style(desktop): format framework pin fixture
* 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): retain record identity in draft checks
* fix(desktop): inspect supported runtime archives
* fix(setup): retain completed actions on cancellation
* docs(desktop): clarify document lock exclusion
* fix(desktop): scrub XML independently of reader compatibility
* fix(desktop): preserve file aliases and reader diagnostics
* style(desktop): format review fixes
* style(desktop): format capture review fixes
* fix(desktop): retain merged installer behavior
---------
Co-authored-by: morluto <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix(desktop): preserve document URLs and value formats
* fix(desktop): bound added checks and omit noisy text guards
* fix(desktop): exclude unseeded locks from task checks
* fix(desktop): preserve draft bounds and launch URLs
* fix(desktop): render draft values using their source format
* fix(desktop): preserve launch URLs and JSON draft values
* fix(desktop): recognize captured file URL documents
* fix(desktop): render captured file URLs for runtime home
* fix(desktop): distinguish phone numbers from save metadata
* fix(desktop): preserve launch URLs and user text
* fix(desktop): preserve captured URLs and user text
* fix(desktop): preserve local file URL authority
* fix(desktop): preserve local file URL authority
* fix(desktop): preserve local file URL authority
* fix(desktop): render captured file URLs at launch
* fix(desktop): classify numeric timestamps using field context
* fix(desktop): classify numeric timestamps using field context
* fix(desktop): classify numeric timestamps using field context
* fix(desktop): retain context for generated draft values
* fix(desktop): retain context for generated draft values
* fix(desktop): retain context for generated draft values
* test(desktop): cover generated IDs in text guards
* test(desktop): cover generated IDs in text guards
* test(desktop): cover generated IDs in text guards
* fix(desktop): isolate launcher URL encoding
* fix(desktop): isolate launcher URL encoding
* fix(desktop): isolate launcher URL encoding
* fix(desktop): isolate launcher URL encoding
* fix(desktop): retain clean text in noisy record fields
* fix(desktop): classify added line values in one pass
* fix(desktop): apply XML quote rules to text drafts
* fix(desktop): distinguish XML attributes from quoted text
* fix(desktop): preserve parsed XML line context
* Format appTaskChecks.ts with the pinned oxfmt
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): retain partial XML syntax facts
* fix(desktop): recognize markdown autolinks in drafts
* fix(desktop): reuse export exclusions in task drafts
* fix(desktop): report unchecked client build paths
* docs(desktop): clarify gzip scrub limits
* fix(desktop): reuse export exclusions in task drafts
* docs(desktop): keep the judge command on one line
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(desktop): clarify gzip scrub limits
* fix(setup): own desktop build process cleanup
* fix(doctor): compare desktop settings with resolved defaults
* style(desktop): format the noisy-record draft files with the pinned oxfmt
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* 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 <[email protected]>
* fix(doctor): inspect client-recorded desktop runners
* fix(doctor): reuse matching runner probes
* fix(setup): propagate cancellation to desktop actions
* 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 <[email protected]>
* 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 <[email protected]>
* 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): pin reader-free verifier to selector parent
* 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
* test(desktop): check retained log seed admission
* 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(desktop): compare screen task views by value
* fix(desktop): reject inexact reader numbers
* fix(desktop): budget export for both reader passes
* fix(desktop): bound reader runner argument size
* test(desktop): verify chunked reader payloads and parent output
* fix(doctor): query agentenv for model endpoint
* style(desktop): format reader payload assertion
* 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): retain record identity in draft checks
* fix(desktop): align timestamp selector digits
* fix(desktop): scrub XML independently of reader compatibility
* fix(desktop): preserve file aliases and reader diagnostics
* fix(desktop): retain remote reader cleanup ownership
* style(desktop): format capture review fixes
* fix(desktop): retain reader leases across verifier reloads
* test(desktop): overlap reader cleanup retries
* fix(desktop): run long exports within exec limits
---------
Co-authored-by: morluto <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix(desktop): recognize captured file URL documents
* fix(desktop): render captured file URLs for runtime home
* fix(desktop): distinguish phone numbers from save metadata
* fix(desktop): preserve launch URLs and user text
* fix(desktop): preserve captured URLs and user text
* fix(desktop): preserve local file URL authority
* fix(desktop): preserve local file URL authority
* fix(desktop): preserve local file URL authority
* fix(desktop): render captured file URLs at launch
* fix(desktop): classify numeric timestamps using field context
* fix(desktop): classify numeric timestamps using field context
* fix(desktop): classify numeric timestamps using field context
* fix(desktop): retain context for generated draft values
* fix(desktop): retain context for generated draft values
* fix(desktop): retain context for generated draft values
* test(desktop): cover generated IDs in text guards
* test(desktop): cover generated IDs in text guards
* test(desktop): cover generated IDs in text guards
* fix(desktop): isolate launcher URL encoding
* fix(desktop): isolate launcher URL encoding
* fix(desktop): isolate launcher URL encoding
* fix(desktop): isolate launcher URL encoding
* fix(desktop): retain clean text in noisy record fields
* fix(desktop): classify added line values in one pass
* fix(desktop): apply XML quote rules to text drafts
* fix(desktop): distinguish XML attributes from quoted text
* fix(desktop): preserve parsed XML line context
* Format appTaskChecks.ts with the pinned oxfmt
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): retain partial XML syntax facts
* fix(desktop): recognize markdown autolinks in drafts
* fix(desktop): reuse export exclusions in task drafts
* fix(desktop): report unchecked client build paths
* docs(desktop): clarify gzip scrub limits
* fix(desktop): reuse export exclusions in task drafts
* docs(desktop): clarify gzip scrub limits
* fix(setup): own desktop build process cleanup
* fix(doctor): compare desktop settings with resolved defaults
* style(desktop): format the noisy-record draft files with the pinned oxfmt
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* 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 <[email protected]>
* 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 <[email protected]>
* 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 <[email protected]>
* 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 <[email protected]>
* 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(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
* 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
---------
Co-authored-by: morluto <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix(desktop): align nested state selections with aliases
* fix(desktop): check build runtime export capability
* fix(desktop): provide complete runtime repack command
* fix(desktop): retain runtime probe failures
* test(desktop): identify capture independently of schema preflight
* fix(desktop): preserve document URLs and value formats
* fix(desktop): bound added checks and omit noisy text guards
* fix(desktop): exclude unseeded locks from task checks
* fix(desktop): preserve draft bounds and launch URLs
* fix(desktop): render draft values using their source format
* fix(desktop): preserve launch URLs and JSON draft values
* fix(desktop): recognize captured file URL documents
* fix(desktop): render captured file URLs for runtime home
* fix(desktop): distinguish phone numbers from save metadata
* fix(desktop): preserve launch URLs and user text
* fix(desktop): preserve captured URLs and user text
* fix(desktop): preserve local file URL authority
* fix(desktop): preserve local file URL authority
* fix(desktop): preserve local file URL authority
* fix(desktop): render captured file URLs at launch
* fix(desktop): classify numeric timestamps using field context
* fix(desktop): classify numeric timestamps using field context
* fix(desktop): classify numeric timestamps using field context
* fix(desktop): retain context for generated draft values
* fix(desktop): retain context for generated draft values
* fix(desktop): retain context for generated draft values
* test(desktop): cover generated IDs in text guards
* test(desktop): cover generated IDs in text guards
* test(desktop): cover generated IDs in text guards
* fix(desktop): isolate launcher URL encoding
* fix(desktop): isolate launcher URL encoding
* fix(desktop): isolate launcher URL encoding
* fix(desktop): isolate launcher URL encoding
* fix(desktop): retain clean text in noisy record fields
* fix(desktop): classify added line values in one pass
* fix(desktop): apply XML quote rules to text drafts
* fix(desktop): distinguish XML attributes from quoted text
* fix(desktop): preserve parsed XML line context
* Format appTaskChecks.ts with the pinned oxfmt
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): retain partial XML syntax facts
* fix(desktop): recognize markdown autolinks in drafts
* fix(desktop): reuse export exclusions in task drafts
* fix(desktop): report unchecked client build paths
* docs(desktop): clarify gzip scrub limits
* fix(desktop): reuse export exclusions in task drafts
* docs(desktop): clarify gzip scrub limits
* fix(setup): own desktop build process cleanup
* fix(doctor): compare desktop settings with resolved defaults
* style(desktop): format the noisy-record draft files with the pinned oxfmt
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* 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 <[email protected]>
* fix(doctor): inspect client-recorded desktop runners
* fix(doctor): reuse matching runner probes
* fix(setup): propagate cancellation to desktop actions
* 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 <[email protected]>
* 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 <[email protected]>
* 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
* 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(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
* 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
---------
Co-authored-by: morluto <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix(desktop): keep an empty env value empty
An empty assignment is not a path. Resolving it named the working directory
and rewrote the value when that directory's spelling moved.
* fix(desktop): keep a run's font on the run, not the frame's text
A formatting attribute such as Scribus FONT is not the document's words.
Joining it across runs made a frame-wide text check of ItalicBold that
failed when a run was split. The font stays a value check on its own run.
* fix(desktop): do not pair JSON records by a selector-shaped field
A field named @record_id matched the id suffix and became [@record_id=one],
which reads an XML attribute. Records paired while their pointers matched
nothing. Names that start with @ or > are not id fields.
* fix(desktop): count a save-numbered id once when pairing members
xml:id and draw:id values such as id123456 were both generated and
numbered, so the same selector was counted twice and refused. A text
edit of two elements keyed only by those ids now keeps its check.
* fix(desktop): count members of root JSON arrays
* fix(desktop): preserve selected state directory aliases
* docs(desktop): explain tool and skill refresh prerequisites
* fix(desktop): align nested state selections with aliases
* fix(desktop): check build runtime export capability
* fix(desktop): provide complete runtime repack command
* fix(desktop): retain runtime probe failures
* test(desktop): identify capture independently of schema preflight
* fix(desktop): preserve document URLs and value formats
* fix(desktop): bound added checks and omit noisy text guards
* fix(desktop): exclude unseeded locks from task checks
* fix(desktop): preserve draft bounds and launch URLs
* fix(desktop): render draft values using their source format
* fix(desktop): preserve launch URLs and JSON draft values
* fix(desktop): recognize captured file URL documents
* fix(desktop): render captured file URLs for runtime home
* fix(desktop): distinguish phone numbers from save metadata
* fix(desktop): preserve launch URLs and user text
* fix(desktop): preserve captured URLs and user text
* fix(desktop): preserve local file URL authority
* fix(desktop): preserve local file URL authority
* fix(desktop): preserve local file URL authority
* fix(desktop): render captured file URLs at launch
* fix(desktop): classify numeric timestamps using field context
* fix(desktop): classify numeric timestamps using field context
* fix(desktop): classify numeric timestamps using field context
* fix(desktop): retain context for generated draft values
* fix(desktop): retain context for generated draft values
* fix(desktop): retain context for generated draft values
* test(desktop): cover generated IDs in text guards
* test(desktop): cover generated IDs in text guards
* test(desktop): cover generated IDs in text guards
* fix(desktop): isolate launcher URL encoding
* fix(desktop): isolate launcher URL encoding
* fix(desktop): isolate launcher URL encoding
* fix(desktop): isolate launcher URL encoding
* fix(desktop): retain clean text in noisy record fields
* fix(desktop): classify added line values in one pass
* fix(desktop): apply XML quote rules to text drafts
* fix(desktop): distinguish XML attributes from quoted text
* fix(desktop): preserve parsed XML line context
* Format appTaskChecks.ts with the pinned oxfmt
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): retain partial XML syntax facts
* fix(desktop): recognize markdown autolinks in drafts
* fix(desktop): reuse export exclusions in task drafts
* fix(desktop): report unchecked client build paths
* docs(desktop): clarify gzip scrub limits
* fix(desktop): reuse export exclusions in task drafts
* docs(desktop): clarify gzip scrub limits
* fix(setup): own desktop build process cleanup
* fix(doctor): compare desktop settings with resolved defaults
* style(desktop): format the noisy-record draft files with the pinned oxfmt
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* 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 <[email protected]>
* fix(doctor): inspect client-recorded desktop runners
* fix(doctor): reuse matching runner probes
* fix(setup): propagate cancellation to desktop actions
* 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 <[email protected]>
* 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 <[email protected]>
* 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
* 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(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): retain record identity in draft checks
* fix(desktop): scrub XML independently of reader compatibility
* fix(desktop): preserve file aliases and reader diagnostics
* style(desktop): format review fixes
* style(desktop): format capture review fixes
---------
Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix(desktop): keep a selected folder that is named like a database log
A folder the caller selected stays a selected root when its name ends in
-journal or -wal. A generated log beside a database file still does not.
* fix(desktop): refuse a build command that exceeds the argument vector
Splitting one argument still leaves the whole command under Linux's E2BIG
limit. A request past that budget is refused before the process starts.
* fix(desktop): remap a captured path in an env assignment
The program word stays the program. A NAME=value assignment keeps its name
and follows a captured path value when that path's spelling moves.
* fix(desktop): keep oversized text out of draft guards
A value or line longer than the draft's text bound is not copied into a
guard, so one large frame cannot fill the task by itself.
* docs(desktop): correct dconf and check-preview guidance
A dconf version kept without its bytes is unreadable, not an empty key
list. A values preview is copied into a check only when it is an uncut scalar.
* fix(desktop): keep an empty env value empty
An empty assignment is not a path. Resolving it named the working directory
and rewrote the value when that directory's spelling moved.
* fix(desktop): keep a run's font on the run, not the frame's text
A formatting attribute such as Scribus FONT is not the document's words.
Joining it across runs made a frame-wide text check of ItalicBold that
failed when a run was split. The font stays a value check on its own run.
* fix(desktop): do not pair JSON records by a selector-shaped field
A field named @record_id matched the id suffix and became [@record_id=one],
which reads an XML attribute. Records paired while their pointers matched
nothing. Names that start with @ or > are not id fields.
* fix(desktop): count a save-numbered id once when pairing members
xml:id and draw:id values such as id123456 were both generated and
numbered, so the same selector was counted twice and refused. A text
edit of two elements keyed only by those ids now keeps its check.
* fix(desktop): count members of root JSON arrays
* fix(desktop): preserve selected state directory aliases
* docs(desktop): explain tool and skill refresh prerequisites
* fix(desktop): align nested state selections with aliases
* fix(desktop): check build runtime export capability
* fix(desktop): provide complete runtime repack command
* fix(desktop): retain runtime probe failures
* test(desktop): identify capture independently of schema preflight
* fix(desktop): preserve document URLs and value formats
* fix(desktop): bound added checks and omit noisy text guards
* fix(desktop): exclude unseeded locks from task checks
* fix(desktop): preserve draft bounds and launch URLs
* fix(desktop): render draft values using their source format
* fix(desktop): preserve launch URLs and JSON draft values
* fix(desktop): recognize captured file URL documents
* fix(desktop): render captured file URLs for runtime home
* fix(desktop): distinguish phone numbers from save metadata
* fix(desktop): preserve launch URLs and user text
* fix(desktop): preserve captured URLs and user text
* fix(desktop): preserve local file URL authority
* fix(desktop): preserve local file URL authority
* fix(desktop): preserve local file URL authority
* fix(desktop): render captured file URLs at launch
* fix(desktop): classify numeric timestamps using field context
* fix(desktop): classify numeric timestamps using field context
* fix(desktop): classify numeric timestamps using field context
* fix(desktop): retain context for generated draft values
* fix(desktop): retain context for generated draft values
* fix(desktop): retain context for generated draft values
* test(desktop): cover generated IDs in text guards
* test(desktop): cover generated IDs in text guards
* test(desktop): cover generated IDs in text guards
* fix(desktop): isolate launcher URL encoding
* fix(desktop): isolate launcher URL encoding
* fix(desktop): isolate launcher URL encoding
* fix(desktop): isolate launcher URL encoding
* fix(desktop): retain clean text in noisy record fields
* fix(desktop): classify added line values in one pass
* fix(desktop): apply XML quote rules to text drafts
* fix(desktop): distinguish XML attributes from quoted text
* fix(desktop): preserve parsed XML line context
* Format appTaskChecks.ts with the pinned oxfmt
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): retain partial XML syntax facts
* fix(desktop): recognize markdown autolinks in drafts
* fix(desktop): reuse export exclusions in task drafts
* docs(desktop): clarify gzip scrub limits
* style(desktop): format the noisy-record draft files with the pinned oxfmt
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* 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 <[email protected]>
* 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
* 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
* 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
* 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
* test(desktop): specify XML format in heap pairing fixture
* 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): retain record identity in draft checks
* fix(desktop): scrub XML independently of reader compatibility
* style(desktop): format review fixes
* style(desktop): format capture review fixes
---------
Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix(desktop): keep a program word and a directory's neighbor
The program after env stays the program even when a captured file sits in a
folder of that name. A selected directory is not a database, so a neighboring
file keeps the parent folder's spelling.
* fix(desktop): record whether a selected path is a directory
Capture stores the walked path's kind. A neighboring file of a selected
directory keeps the parent folder's spelling even when that directory never
changes and its children are not content roots.
* fix(desktop): mark containing paths without a pairwise scan
A recorded path marks its known ancestors as directories in one walk up
the path, instead of comparing every recorded path with every other.
* fix(desktop): keep a selected folder that is named like a database log
A folder the caller selected stays a selected root when its name ends in
-journal or -wal. A generated log beside a database file still does not.
* fix(desktop): refuse a build command that exceeds the argument vector
Splitting one argument still leaves the whole command under Linux's E2BIG
limit. A request past that budget is refused before the process starts.
* fix(desktop): remap a captured path in an env assignment
The program word stays the program. A NAME=value assignment keeps its name
and follows a captured path value when that path's spelling moves.
* fix(desktop): keep oversized text out of draft guards
A value or line longer than the draft's text bound is not copied into a
guard, so one large frame cannot fill the task by itself.
* docs(desktop): correct dconf and check-preview guidance
A dconf version kept without its bytes is unreadable, not an empty key
list. A values preview is copied into a check only when it is an uncut scalar.
* fix(desktop): keep an empty env value empty
An empty assignment is not a path. Resolving it named the working directory
and rewrote the value when that directory's spelling moved.
* fix(desktop): keep a run's font on the run, not the frame's text
A formatting attribute such as Scribus FONT is not the document's words.
Joining it across runs made a frame-wide text check of ItalicBold that
failed when a run was split. The font stays a value check on its own run.
* fix(desktop): do not pair JSON records by a selector-shaped field
A field named @record_id matched the id suffix and became [@record_id=one],
which reads an XML attribute. Records paired while their pointers matched
nothing. Names that start with @ or > are not id fields.
* fix(desktop): count a save-numbered id once when pairing members
xml:id and draw:id values such as id123456 were both generated and
numbered, so the same selector was counted twice and refused. A text
edit of two elements keyed only by those ids now keeps its check.
* fix(desktop): preserve selected state directory aliases
* docs(desktop): explain tool and skill refresh prerequisites
* fix(desktop): align nested state selections with aliases
* fix(desktop): check build runtime export capability
* fix(desktop): provide complete runtime repack command
* fix(desktop): retain runtime probe failures
* test(desktop): identify capture independently of schema preflight
* fix(desktop): preserve document URLs and value formats
* fix(desktop): bound added checks and omit noisy text guards
* fix(desktop): exclude unseeded locks from task checks
* fix(desktop): preserve draft bounds and launch URLs
* fix(desktop): render draft values using their source format
* fix(desktop): preserve launch URLs and JSON draft values
* fix(desktop): recognize captured file URL documents
* fix(desktop): render captured file URLs for runtime home
* fix(desktop): distinguish phone numbers from save metadata
* fix(desktop): preserve launch URLs and user text
* fix(desktop): preserve captured URLs and user text
* fix(desktop): preserve local file URL authority
* fix(desktop): preserve local file URL authority
* fix(desktop): preserve local file URL authority
* fix(desktop): render captured file URLs at launch
* fix(desktop): classify numeric timestamps using field context
* fix(desktop): classify numeric timestamps using field context
* fix(desktop): classify numeric timestamps using field context
* fix(desktop): retain context for generated draft values
* fix(desktop): retain context for generated draft values
* fix(desktop): retain context for generated draft values
* test(desktop): cover generated IDs in text guards
* test(desktop): cover generated IDs in text guards
* test(desktop): cover generated IDs in text guards
* fix(desktop): isolate launcher URL encoding
* fix(desktop): isolate launcher URL encoding
* fix(desktop): isolate launcher URL encoding
* fix(desktop): isolate launcher URL encoding
* fix(desktop): retain clean text in noisy record fields
* fix(desktop): classify added line values in one pass
* fix(desktop): apply XML quote rules to text drafts
* fix(desktop): distinguish XML attributes from quoted text
* fix(desktop): preserve parsed XML line context
* fix(desktop): retain partial XML syntax facts
* fix(desktop): recognize markdown autolinks in drafts
* fix(desktop): reuse export exclusions in task drafts
* docs(desktop): clarify gzip scrub limits
* style(desktop): format the noisy-record draft files with the pinned oxfmt
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* 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
* test(desktop): separate markup boundary regression group
* test(desktop): remove duplicate seed filename case
* docs(desktop): clarify task checks before scratch export
* fix(desktop): respell selected paths in file URLs
* test(desktop): group captured document spelling cases
* 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
* 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): 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): scrub XML independently of reader compatibility
* style(desktop): format review fixes
* style(desktop): format capture review fixes
---------
Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix(desktop): follow a database mapping and own limitation text
A selected database keeps its log when a parent folder moves or the file
sits outside the home. Trash and seed-lock tests compare the owning notice
with the paths, and launch options that are not captured paths stay as written.
* test(desktop): pass the selected log into the draft
The export's selected home paths decide which log stays in the draft.
The line-guard test now names that log instead of relying on its reason.
* fix(desktop): retarget a settings folder that holds captured files
A nested profile directory is a captured path when a seeded file is inside
it. An option value that only resolves under the home stays as written.
* fix(desktop): keep a program word and a directory's neighbor
The program after env stays the program even when a captured file sits in a
folder of that name. A selected directory is not a database, so a neighboring
file keeps the parent folder's spelling.
* fix(desktop): record whether a selected path is a directory
Capture stores the walked path's kind. A neighboring file of a selected
directory keeps the parent folder's spelling even when that directory never
changes and its children are not content roots.
* fix(desktop): mark containing paths without a pairwise scan
A recorded path marks its known ancestors as directories in one walk up
the path, instead of comparing every recorded path with every other.
* fix(desktop): keep a selected folder that is named like a database log
A folder the caller selected stays a selected root when its name ends in
-journal or -wal. A generated log beside a database file still does not.
* fix(desktop): refuse a build command that exceeds the argument vector
Splitting one argument still leaves the whole command under Linux's E2BIG
limit. A request past that budget is refused before the process starts.
* fix(desktop): remap a captured path in an env assignment
The program word stays the program. A NAME=value assignment keeps its name
and follows a captured path value when that path's spelling moves.
* fix(desktop): keep oversized text out of draft guards
A value or line longer than the draft's text bound is not copied into a
guard, so one large frame cannot fill the task by itself.
* docs(desktop): correct dconf and check-preview guidance
A dconf version kept without its bytes is unreadable, not an empty key
list. A values preview is copied into a check only when it is an uncut scalar.
* fix(desktop): keep an empty env value empty
An empty assignment is not a path. Resolving it named the working directory
and rewrote the value when that directory's spelling moved.
* fix(desktop): keep a run's font on the run, not the frame's text
A formatting attribute such as Scribus FONT is not the document's words.
Joining it across runs made a frame-wide text check of ItalicBold that
failed when a run was split. The font stays a value check on its own run.
* fix(desktop): do not pair JSON records by a selector-shaped field
A field named @record_id matched the id suffix and became [@record_id=one],
which reads an XML attribute. Records paired while their pointers matched
nothing. Names that start with @ or > are not id fields.
* fix(desktop): count a save-numbered id once when pairing members
xml:id and draw:id values such as id123456 were both generated and
numbered, so the same selector was counted twice and refused. A text
edit of two elements keyed only by those ids now keeps its check.
* fix(desktop): preserve selected state directory aliases
* fix(desktop): align nested state selections with aliases
* fix(desktop): check build runtime export capability
* fix(desktop): provide complete runtime repack command
* fix(desktop): retain runtime probe failures
* test(desktop): identify capture independently of schema preflight
* fix(desktop): preserve document URLs and value formats
* fix(desktop): bound added checks and omit noisy text guards
* fix(desktop): exclude unseeded locks from task checks
* fix(desktop): preserve draft bounds and launch URLs
* fix(desktop): render draft values using their source format
* fix(desktop): preserve launch URLs and JSON draft values
* fix(desktop): recognize captured file URL documents
* fix(desktop): render captured file URLs for runtime home
* fix(desktop): distinguish phone numbers from save metadata
* fix(desktop): preserve launch URLs and user text
* fix(desktop): preserve captured URLs and user text
* fix(desktop): preserve local file URL authority
* fix(desktop): preserve local file URL authority
* fix(desktop): preserve local file URL authority
* fix(desktop): render captured file URLs at launch
* fix(desktop): classify numeric timestamps using field context
* fix(desktop): classify numeric timestamps using field context
* fix(desktop): classify numeric timestamps using field context
* fix(desktop): retain context for generated draft values
* fix(desktop): retain context for generated draft values
* fix(desktop): retain context for generated draft values
* test(desktop): cover generated IDs in text guards
* test(desktop): cover generated IDs in text guards
* test(desktop): cover generated IDs in text guards
* fix(desktop): isolate launcher URL encoding
* fix(desktop): isolate launcher URL encoding
* fix(desktop): isolate launcher URL encoding
* fix(desktop): isolate launcher URL encoding
* fix(desktop): retain clean text in noisy record fields
* fix(desktop): classify added line values in one pass
* fix(desktop): apply XML quote rules to text drafts
* fix(desktop): distinguish XML attributes from quoted text
* fix(desktop): preserve parsed XML line context
* fix(desktop): retain partial XML syntax facts
* fix(desktop): recognize markdown autolinks in drafts
* fix(desktop): reuse export exclusions in task drafts
* docs(desktop): clarify gzip scrub limits
* style(desktop): format the noisy-record draft files with the pinned oxfmt
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* 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
* test(desktop): separate markup boundary regression group
* docs(desktop): clarify task checks before scratch export
* fix(desktop): respell selected paths in file URLs
* test(desktop): group captured document spelling cases
* 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
* 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): 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
* style(desktop): format capture review fixes
---------
Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix(desktop): keep a lone lock a document and drop one beside its file
A selected name such as notes.lck stays a document, so launch and the draft
still treat it as the caller's file. A lock beside a recorded sibling is left
out of the seed unless that lock path itself was selected.
* fix(desktop): require the added record and leave save times out of list checks
A record a selector names is checked itself, so its words have to be in that
record. When an unnamed record only repeats words the seed already has, a
list that holds a save time is checked by the text around that time. A later
save of the same records still passes, and a missing or rewritten record fails.
* fix(desktop): judge the older index helper by pointer segments only
A leading zero in a file name or a URL does not change how the older index
helper reads a pointer. The witness still rejects that helper when a pointer
segment is a number such as 01.
* fix(desktop): keep a document whose name ends in .lock
A lock beside a recorded file still stays out of the seed unless that lock
was selected. A document such as Cargo.lock in the home is the user's file,
so the seed keeps it. An application lock in the home, such as poedit.lock,
stays out.
* fix(desktop): do not suggest list text the seed already passes
A repeated record is shown by text the seed does not hold. The seed's own
text is not a check. A pointer inside a check value is not a pointer segment
for the older index helper.
* fix(desktop): do not let a left-out lock hide settings checks
A sibling lock the seed omits is not the document the draft follows. Settings
changes stay suggested. The unused sibling-lock name helper is removed.
* fix(desktop): keep a linked settings file in the folder the link points at
A selected ~/.config alias no longer moves MuseScore settings to that alias.
The seed keeps settings/MuseScore or Documents, which is the path the export
opens the note from.
* fix(desktop): split home fetches by answer size and keep a size refusal
A batch of tiny files stops before each path's envelope exceeds the host
answer budget. A capture output past that ceiling stays a resource error
instead of becoming a generic exit.
* fix(desktop): seed a selected link under the path the app uses
The selected spelling wins, so ~/.config/MuseScore is not rewritten to the
folder it points at and the note stays the one launched document. A database
wal and journal follow that database. A launch argument is respelled the same
way. Teardown signals a detached PID only when its start identity still matches.
* fix(desktop): keep a witness's actions and an incomplete checkpoint
A failed witness save retries the action log it first recorded. A deletion
left unconfirmed between the seed and the end is not reported as no change.
Windows packs npm through a shell, which can start npm.cmd.
* fix(desktop): read verifier pointers from the parsed checks
A brace inside a check value is not an object boundary. A leading-zero
pointer after that value still rejects the older index helper.
* fix(desktop): leave out a sibling lock under a selected folder
A file under a selected folder inherits selected_path. That does not make
the lock beside its map a chosen document, so a settings change stays drafted.
* fix(desktop): prefer the most specific home spelling and retarget launch files
A selected parent directory no longer hides a settings folder walked through
a link. A launch filename under that folder is replaced with the seeded
file's absolute path.
* fix(desktop): pack the build runtime through npm's JavaScript entry
npm pack and npm ci no longer spawn npm.cmd through a shell, so a temporary
directory whose path contains spaces stays one argument.
* fix(desktop): spell launch settings and keep a database log exact
A database log matches only that file, so a neighboring folder is not moved.
A launch option and the working directory use the same home spelling as the seed.
* fix(desktop): draft a lock the export selected exactly
The draft uses the same selected paths as the export. A lock chosen by
itself stays in the draft. A lock under a selected folder stays out.
* fix(desktop): move a state database's logs with the database
The write-ahead log and journal follow the spelling the database received,
including a walked state link. A neighboring folder stays put. A separate
--config argument uses that spelling too.
* fix(desktop): keep a selected file ahead of a database-log guess
A file selected as itself keeps that spelling even when its name ends in
-journal. Relative launch arguments are resolved in the captured working
directory before that directory moves.
* fix(desktop): keep outside-home database logs and option values
A selected database outside the home keeps its write-ahead log. Launch
options are rewritten only when they name a captured file or settings path.
* fix(desktop): follow a database mapping and own limitation text
A selected database keeps its log when a parent folder moves or the file
sits outside the home. Trash and seed-lock tests compare the owning notice
with the paths, and launch options that are not captured paths stay as written.
* fix(desktop): retarget a settings folder that holds captured files
A nested profile directory is a captured path when a seeded file is inside
it. An option value that only resolves under the home stays as written.
* fix(desktop): keep a program word and a directory's neighbor
The program after env stays the program even when a captured file sits in a
folder of that name. A selected directory is not a database, so a neighboring
file keeps the parent folder's spelling.
* fix(desktop): record whether a selected path is a directory
Capture stores the walked path's kind. A neighboring file of a selected
directory keeps the parent folder's spelling even when that directory never
changes and its children are not content roots.
* fix(desktop): mark containing paths without a pairwise scan
A recorded path marks its known ancestors as directories in one walk up
the path, instead of comparing every recorded path with every other.
* fix(desktop): keep a selected folder that is named like a database log
A folder the caller selected stays a selected root when its name ends in
-journal or -wal. A generated log beside a database file still does not.
* fix(desktop): refuse a build command that exceeds the argument vector
Splitting one argument still leaves the whole command under Linux's E2BIG
limit. A request past that budget is refused before the process starts.
* fix(desktop): remap a captured path in an env assignment
The program word stays the program. A NAME=value assignment keeps its name
and follows a captured path value when that path's spelling moves.
* fix(desktop): keep oversized text out of draft guards
A value or line longer than the draft's text bound is not copied into a
guard, so one large frame cannot fill the task by itself.
* docs(desktop): correct dconf and check-preview guidance
A dconf version kept without its bytes is unreadable, not an empty key
list. A values preview is copied into a check only when it is an uncut scalar.
* fix(desktop): keep an empty env value empty
An empty assignment is not a path. Resolving it named the working directory
and rewrote the value when that directory's spelling moved.
* fix(desktop): check build runtime export capability
* fix(desktop): provide complete runtime repack command
* fix(desktop): retain runtime probe failures
* test(desktop): identify capture independently of schema preflight
* fix(desktop): preserve document URLs and value formats
* fix(desktop): bound added checks and omit noisy text guards
* fix(desktop): preserve launch URLs and JSON draft values
* fix(desktop): recognize captured file URL documents
* fix(desktop): render captured file URLs for runtime home
* fix(desktop): distinguish phone numbers from save metadata
* fix(desktop): preserve launch URLs and user text
* fix(desktop): preserve local file URL authority
* fix(desktop): preserve local file URL authority
* fix(desktop): render captured file URLs at launch
* fix(desktop): classify numeric timestamps using field context
* fix(desktop): classify numeric timestamps using field context
* fix(desktop): retain context for generated draft values
* fix(desktop): retain context for generated draft values
* test(desktop): cover generated IDs in text guards
* test(desktop): cover generated IDs in text guards
* fix(desktop): isolate launcher URL encoding
* fix(desktop): isolate launcher URL encoding
* fix(desktop): isolate launcher URL encoding
* fix(desktop): retain clean text in noisy record fields
* style(desktop): format the noisy-record draft files with the pinned oxfmt
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* 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
* docs(desktop): clarify task checks before scratch export
* fix(desktop): respell selected paths in file URLs
* test(desktop): group captured document spelling cases
* 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
* 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(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): 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
* style(desktop): format capture review fixes
---------
Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
* fix(desktop): keep added-record checks off ids, dates, and joined timestamps
A position-named record's fields keep their paths, so an identifier, a stamped date, and text past the limit are not suggested checks. A whole-text check does not climb into an ancestor whose text joins a timestamp.
* fix(desktop): bound home fetches and keep unsettled edits readable
Split home-folder reads by the guest answer budget, keep a capacity error
from the capture output, and drop the superseded task-argument refusal.
Grade kept text per line, leave unretained dconf unread, and reserve
by-position notes for XML children. Map a linked database's sidecars
through the selected spelling and stop a detached capture only when its
start identity still owns the pid.
* fix(desktop): describe unretained dconf as unread
The key comparison treats a missing copy as unknown. The comment still
said that copy counted as an empty key list.
* test(desktop): keep a capture capacity error through endedCapture
An output past the guest answer ceiling stays the resource constraint,
with its heap recovery and non-retryable status, instead of a generic
capture-exit message.
* fix(desktop): keep nested save times and formatting runs out of checks
A field check drops save-time leaves before it joins the field, so a nested
timestamp is not part of the suggested text. A text check beside a save time
uses the paragraph when the diff names a formatting run.
* test(desktop): read paragraph pointers from checks that have them
* test(desktop): expect a large task to export intact
The 96 KiB refusal is gone. A prompt past that size reaches the computer
and comes back unchanged, instead of failing before export.
* fix(desktop): keep real edits that save-side heuristics were dropping
Repeated unnamed fields stay content checks. A formula that still has
text is not renumbered with its ExprID. One gained style attribute is
the edit; a repeated fill-in stays upkeep. Phone numbers and plain
clock times stay text, and a decimal is the element's whole text. An
older verifier matches on the task it states, not its evaluator source.
* fix(desktop): cover repeated records and keep timestamps out of checks
An added record whose words are already in the seed still gets the list's
text when that text fits. Readiness is a start reason, so a final check
names seed locks without matching the message. Field text split by a
timestamp stays separate, and a timestamp line is not offered as a guard.
* fix(desktop): compare a cell's joined decimal within tolerance
The whole-element check keeps numeric tolerance, so 437.80 and a long
float still match 437.8, including when a run splits the digits. A
different number or other text does not. Both evaluators read an
element's text for that comparison.
* fix(desktop): keep selected logs and stop treating unknown walks as complete
Match an app name as a whole token, treat an omitted unchecked_paths count as incomplete, seed Chromium user_prefs.json, and keep a caller-selected log even when that path is also a state directory. A log inside the app's own folder still marks that folder so settings beside it stay seeded, while a lock in the home does not. Sibling locks under a selected folder stay out unless that lock was the selected path.
* fix(desktop): grade a number from a complete XML element only
A tolerance check joins an element's text only in an XML file or an XML
archive entry, and leaves that check undecided when the view is cut, so a
dropped descendant cannot turn the kept runs into a passing number. A JSON
object with the same shape stays the value itself. Equivalent decimals and a
split run on a whole cell still match.
* fix(desktop): accept an older grader only when its index helper agrees
The witness still matches a bundle whose evaluator is this REA's, or the older
index expression alone when no path segment is a leading-zero number. A
truncated verifier, a changed grader, or that older helper beside a segment
such as 01 no longer counts as the same task.
* fix(desktop): keep a lone lock a document and drop one beside its file
A selected name such as notes.lck stays a document, so launch and the draft
still treat it as the caller's file. A lock beside a recorded sibling is left
out of the seed unless that lock path itself was selected.
* fix(desktop): require the added record and leave save times out of list checks
A record a selector names is checked itself, so its words have to be in that
record. When an unnamed record only repeats words the seed already has, a
list that holds a save time is checked by the text around that time. A later
save of the same records still passes, and a missing or rewritten record fails.
* fix(desktop): judge the older index helper by pointer segments only
A leading zero in a file name or a URL does not change how the older index
helper reads a pointer. The witness still rejects that helper when a pointer
segment is a number such as 01.
* fix(desktop): keep a document whose name ends in .lock
A lock beside a recorded file still stays out of the seed unless that lock
was selected. A document such as Cargo.lock in the home is the user's file,
so the seed keeps it. An application lock in the home, such as poedit.lock,
stays out.
* fix(desktop): do not suggest list text the seed already passes
A repeated record is shown by text the seed does not hold. The seed's own
text is not a check. A pointer inside a check value is not a pointer segment
for the older index helper.
* fix(desktop): do not let a left-out lock hide settings checks
A sibling lock the seed omits is not the document the draft follows. Settings
changes stay suggested. The unused sibling-lock name helper is removed.
* fix(desktop): keep a linked settings file in the folder the link points at
A selected ~/.config alias no longer moves MuseScore settings to that alias.
The seed keeps settings/MuseScore or Documents, which is the path the export
opens the note from.
* fix(desktop): split home fetches by answer size and keep a size refusal
A batch of tiny files stops before each path's envelope exceeds the host
answer budget. A capture output past that ceiling stays a resource error
instead of becoming a generic exit.
* fix(desktop): seed a selected link under the path the app uses
The selected spelling wins, so ~/.config/MuseScore is not rewritten to the
folder it points at and the note stays the one launched document. A database
wal and journal follow that database. A launch argument is respelled the same
way. Teardown signals a detached PID only when its start identity still matches.
* fix(desktop): keep a witness's actions and an incomplete checkpoint
A failed witness save retries the action log it first recorded. A deletion
left unconfirmed between the seed and the end is not reported as no change.
Windows packs npm through a shell, which can start npm.cmd.
* fix(desktop): read verifier pointers from the parsed checks
A brace inside a check value is not an object boundary. A leading-zero
pointer after that value still rejects the older index helper.
* fix(desktop): leave out a sibling lock under a selected folder
A file under a selected folder inherits selected_path. That does not make
the lock beside its map a chosen document, so a settings change stays drafted.
* fix(desktop): prefer the most specific home spelling and retarget launch files
A selected parent directory no longer hides a settings folder walked through
a link. A launch filename under that folder is replaced with the seeded
file's absolute path.
* fix(desktop): pack the build runtime through npm's JavaScript entry
npm pack and npm ci no longer spawn npm.cmd through a shell, so a temporary
directory whose path contains spaces stays one argument.
* fix(desktop): spell launch settings and keep a database log exact
A database log matches only that file, so a neighboring folder is not moved.
A launch option and the working directory use the same home spelling as the seed.
* fix(desktop): draft a lock the export selected exactly
The draft uses the same selected paths as the export. A lock chosen by
itself stays in the draft. A lock under a selected folder stays out.
* fix(desktop): move a state database's logs with the database
The write-ahead log and journal follow the spelling the database received,
including a walked state link. A neighboring folder stays put. A separate
--config argument uses that spelling too.
* fix(desktop): keep a selected file ahead of a database-log guess
A file selected as itself keeps that spelling even when its name ends in
-journal. Relative launch arguments are resolved in the captured working
directory before that directory moves.
* fix(desktop): keep outside-home database logs and option values
A selected database outside the home keeps its write-ahead log. Launch
options are rewritten only when they name a captured file or settings path.
* fix(desktop): follow a database mapping and own limitation text
A selected database keeps its log when a parent folder moves or the file
sits outside the home. Trash and seed-lock tests compare the owning notice
with the paths, and launch options that are not captured paths stay as written.
* fix(desktop): retarget a settings folder that holds captured files
A nested profile directory is a captured path when a seeded file is inside
it. An option value that only resolves under the home stays as written.
* fix(desktop): keep a program word and a directory's neighbor
The program after env stays the program even when a captured file sits in a
folder of that name. A selected directory is not a database, so a neighboring
file keeps the parent folder's spelling.
* fix(desktop): record whether a selected path is a directory
Capture stores the walked path's kind. A neighboring file of a selected
directory keeps the parent folder's spelling even when that directory never
changes and its children are not content roots.
* fix(desktop): mark containing paths without a pairwise scan
A recorded path marks its known ancestors as directories in one walk up
the path, instead of comparing every recorded path with every other.
* fix(desktop): keep a selected folder that is named like a database log
A folder the caller selected stays a selected root when its name ends in
-journal or -wal. A generated log beside a database file still does not.
* fix(desktop): refuse a build command that exceeds the argument vector
Splitting one argument still leaves the whole command under Linux's E2BIG
limit. A request past that budget is refused before the process starts.
* fix(desktop): remap a captured path in an env assignment
The program word stays the program. A NAME=value assignment keeps its name
and follows a captured path value when that path's spelling moves.
* fix(desktop): keep oversized text out of draft guards
A value or line longer than the draft's text bound is not copied into a
guard, so one large frame cannot fill the task by itself.
* fix(desktop): keep an empty env value empty
An empty assignment is not a path. Resolving it named the working directory
and rewrote the value when that directory's spelling moved.
* fix(desktop): check build runtime export capability
* fix(desktop): provide complete runtime repack command
* fix(desktop): retain runtime probe failures
* test(desktop): identify capture independently of schema preflight
* fix(desktop): preserve document URLs and value formats
* fix(desktop): bound added checks and omit noisy text guards
* fix(desktop): preserve launch URLs and JSON draft values
* fix(desktop): recognize captured file URL documents
* fix(desktop): render captured file URLs for runtime home
* fix(desktop): distinguish phone numbers from save metadata
* fix(desktop): preserve launch URLs and user text
* fix(desktop): preserve local file URL authority
* fix(desktop): preserve local file URL authority
* fix(desktop): render captured file URLs at launch
* fix(desktop): classify numeric timestamps using field context
* fix(desktop): classify numeric timestamps using field context
* fix(desktop): retain context for generated draft values
* fix(desktop): retain context for generated draft values
* test(desktop): cover generated IDs in text guards
* test(desktop): cover generated IDs in text guards
* fix(desktop): isolate launcher URL encoding
* fix(desktop): isolate launcher URL encoding
* fix(desktop): isolate launcher URL encoding
* fix(desktop): retain clean text in noisy record fields
* style(desktop): format the noisy-record draft files with the pinned oxfmt
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* 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
* docs(desktop): clarify task checks before scratch export
* fix(desktop): respell selected paths in file URLs
* test(desktop): group captured document spelling cases
* 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
* 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(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): 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
* style(desktop): format capture review fixes
---------
Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
* docs(i18n): add Bulgarian README
Add a complete Bulgarian (bg) translation of the README and list it in
every README language bar and the website language inventory.
Register bg with a pending website catalog that uses the labeled,
nonindexable English fallback, with the fallback notice translated.
Regenerate the shared language picker and hreflang links and refresh
messages.pot.
* docs(i18n): clarify Bulgarian project code license scope
---------
Co-authored-by: morluto <[email protected]>
* fix(javascript): reject a tls protocol version Node refuses at the call site
`tls.connect` resolves `minVersion`/`maxVersion` before it opens a socket, so a bad literal
throws from the call itself and no socket exists. The acquisition filter decided the port
only, so it reported a complete built-in resource for a call that throws.
Measured on Node 24.22.1:
| `minVersion` / `maxVersion` | outcome |
| --- | --- |
| `"TLSv1"`, `"TLSv1.1"`, `"TLSv1.2"`, `"TLSv1.3"` | socket created |
| `null`, `undefined`, absent | socket created |
| `"TLSv1.0"`, `"tlsv1"`, `"tlsv1.2"`, `"TLSv1.4"`, `"nope"`, `""` | THREW `ERR_TLS_INVALID_PROTOCOL_VERSION` |
| `5`, `true`, `{}`, `[]` | THREW `ERR_TLS_INVALID_PROTOCOL_VERSION` |
| `1n` | THREW `TypeError` |
The accepted set is exact and case-sensitive, so it is a closed list rather than a pattern.
`net` ignores these members, so only `tls` is consulted. The member is read in write order,
which is why `{minVersion: "nope", minVersion: "TLSv1.2"}` keeps its acquisition; a spread,
a computed key or an accessor leaves the answer undecided rather than decided against.
22 cases, 11 failing before this commit. `src/domain/javascript`: 5040 to 5062 passing.
`tsc` 0; `build` 0; `npm run format:check` 0; `npm run lint` 0 errors; `knip` 0.
* fix(javascript): read every tls options slot and check both version members
Four review findings, each measured on Node 24.22.1. All four were real gaps in the first
version of this check.
**An accepted `minVersion` returned before `maxVersion` was read.** `tls.connect({port: 80,
minVersion: "TLSv1.2", maxVersion: "nope"})` throws, because both members are resolved
before the socket opens. The loop now continues past an accepted version and returns only
for a rejected one.
**Only argument zero was read.** `tls` also accepts `(port, options)`, `(port, host,
options)` and `(path, options)`, and all three throw on a bad version:
`tls.connect(443, {minVersion: "nope"})`, `tls.connect(443, "127.0.0.1", {minVersion:
"nope"})` and `tls.connect("/tmp/x.sock", {minVersion: "nope"})`. Every object literal in
the argument list is now read; a spread argument shifts those positions, so it leaves the
answer undecided instead of picking a wrong slot.
**A signed number was left unknown.** `minVersion: -1`, `+1` and `-0` all throw, but they
arrive as a `UnaryExpression` and matched none of the literal arms. `readExactJavaScriptLiteral`
already normalises `-1` and `+1`; `-0` is the one case it excludes, so a sign over a numeric
literal is decided explicitly here rather than treated as unknown.
13 cases added; reverting the slot and loop changes fails 4 of them.
`src/domain/javascript`: 5062 to 5075 passing. `tsc` 0; `build` 0;
`npm run format:check` 0; `npm run lint` 0 errors; `knip` 0.
* fix(javascript): merge the tls arguments and honour a supplied secure context
Four review findings, each measured on Node 24.22.1. All four were real: the previous
reader checked each object argument independently and ignored the context.
**Several object arguments merge in order, so the later one wins.** The reader rejected the
first object and stopped:
| call | Node 24.22.1 |
| --- | --- |
| `tls.connect({port: 443, minVersion: "nope"}, {minVersion: "TLSv1.2"})` | SOCKET |
| `tls.connect({port: 443, minVersion: "TLSv1.2"}, {minVersion: "nope"})` | THREW |
| `tls.connect({port: 443}, {minVersion: "nope"})` | THREW |
The members are now resolved across the whole argument list in write order, so a later
argument replaces an earlier one the way Node's merge does.
**A supplied `secureContext` bypasses version parsing entirely.** Node uses
`options.secureContext || tls.createSecureContext(options)`, so
`tls.connect({port: 80, secureContext: ctx, minVersion: "nope"})` connects while a `null`
context throws. An unreadable context leaves the answer undecided.
**An ordinary method member cannot install a version.** The reader skipped any object
containing a method, so `{checkServerIdentity() {}, minVersion: "nope"}` kept a socket
Node never creates. Only an accessor runs a body and leaves the member open; a plain method
is a value Node never reads as a version name. A `minVersion() {}` member is itself such a
value, so it rejects.
**Signed BigInt and function-shaped versions were left unknown.** `-1n`, `() => {}`,
`function () {}` and `class {}` are all truthy non-strings that Node refuses; none matched
an existing arm. They are now decided.
12 cases added; reverting the merge and context changes fails 2 of them and reverting the
method arm fails 6. `src/domain/javascript`: 5075 to 5087 passing. `tsc` 0; `build` 0;
`npm run format:check` 0; `npm run lint` 0 errors; `knip` 0.
* fix(javascript): read only the options slot tls.connect copies, and its accessors
Six review findings, each measured on Node 24.22.1. All six were real.
**A falsy `secureContext` does not bypass version parsing.** Node tests
`options.secureContext` for truth, so `{secureContext: false, minVersion: "nope"}`, and `0`
and `""` with it, all throw. The guard only recognised nullish syntax, so it dropped real
throws. `staticTruthiness` now decides the context: a known-falsy value keeps checking, a
known-truthy one ends the check, and an unreadable one stays undecided.
**Node copies at most one options object.** Measured:
| call | result |
| --- | --- |
| `(443, {minVersion: "TLSv1.2"}, {minVersion: "nope"})` | SOCKET — the third argument is ignored |
| `(443, {minVersion: "nope"}, {minVersion: "TLSv1.2"})` | THREW |
| `(443, "127.0.0.1", {minVersion: "nope"})` | THREW — the host fills slot 1 |
| `({port: 443, minVersion: "nope"}, {minVersion: "TLSv1.2"})` | SOCKET — the second object wins |
The reader treated every object argument as merged in order, which let an ignored object
decide. `tlsOptionsObjects` now models the two overloads: an object in slot 0 is merged with
slot 1, and a port or path in slot 0 makes the first object among slots 1 and 2 the only one
read.
**An exact computed key names the member.** `{["minVersion"]: "nope"}` throws, but the
reader marked every computed key unknown before the shared name resolver saw it. Only an
unresolvable key is unknown now, and the case that asserted the old behaviour uses a
binding key so it still covers the unknown path.
**An accessor can write the member first.** `{get side() { this.minVersion = "TLSv1.2"; },
minVersion: "nope"}` connects, because Node's own copy runs the getter before it reads
`minVersion`; and a `set minVersion` in an earlier argument absorbs the later write. Both
are bodies this reader does not run, so an accessor anywhere in the merged objects now
leaves every version member undecided. A plain method still rejects, since Node never reads
it as a version name.
**An unshadowed `NaN` or `Infinity` is a non-string version.** Both throw, and neither
matched an existing arm. They are decided the same way the adjacent port filter decides
them, through `isUnmodifiedGlobalReference`, so a shadowed binding stays unknown.
16 cases added; reverting the truthiness, accessor and computed-key changes fails 2 of them.
`src/domain/javascript`: 5075 to 5188 passing. `tsc` 0; `build` 0;
`npm run format:check` 0; `npm run lint` 0 errors.
* fix(javascript): use the nullable key owner and settle unary versions
Six review findings, each measured on Node 24.22.1. All six were real, and three of them
were removing real sockets.
**`semanticStaticPropertyName` encodes an unresolved key as `""`, never `undefined`.** The
guard tested `undefined`, so it never fired and an unresolved computed key was read as a
member literally named empty. `const k = "minVersion";
tls.connect({port: 443, minVersion: "nope", [k]: "TLSv1.2"})` connects on Node 24.22.1
because the later computed member wins, while the analysis filtered on the stale `"nope"`
and dropped the socket. `semanticStaticPropertyKey` is the nullable owner and is now used,
with `null` treated as unknown.
**A getter can replace a plain version method before Node copies it.** The method branch
ignored `accessorWritten`, so `{get side() { this.minVersion = "TLSv1.2"; }, minVersion()
{}}` still returned `"rejects"` although it connects. The same unknown result now applies to
methods when an accessor is present.
**A non-literal second argument is a merged options object, not an absent one.**
`const o = {minVersion: "TLSv1.2"}; tls.connect({port: 443, minVersion: "nope"}, o)`
connects. The slot reader mapped every non-object to `undefined`, so only the stale first
object was examined. A non-object in a copied slot now stays unknown.
**`-0` is falsy, so a signed-zero context does not bypass parsing.** The local truthiness
helper did not fold a unary `-`, so `{secureContext: -0, minVersion: "nope"}` was treated as
a possible bypass although it throws. The shared `staticJavaScriptTruthiness` owner already
handles signed zero and replaces the local helper, which is deleted.
**A unary operator over a settled operand is a settled non-string version.** `!0`, `!1`,
`~~0`, `!!0` and `typeof 1` all throw `ERR_TLS_INVALID_PROTOCOL_VERSION`; the previous arm
recognised only `+` and `-`, and the exact-literal and shape arms did not reach them.
`void <expr>` stays accepted, because it is `undefined`.
The slot reader also had to distinguish a host string in slot 1 from an options object, since
`tls.connect(443, "127.0.0.1", {minVersion: "nope"})` throws while
`tls.connect(443, {minVersion: "nope"})` is the two-slot form.
`src/domain/javascript`: 5188 to 5197 passing. Reverting the key owner fails 1 case and
reverting the unary arms fails 2. `tsc` 0; `build` 0; `npm run format:check` 0;
`npm run lint` 0 errors.
* test(javascript): distinguish package scope in route fixtures
* test(apple): separate identity and refusal fixture groups
* fix(javascript): share TLS option facts and preserve merge uncertainty
* fix(javascript): separate TLS assignment authority from copy facts
---------
Co-authored-by: morluto <[email protected]>
* feat(native): list ELF and Mach-O initializer and finalizer functions
inspect_native_initializers and inspect-native-initializers list the
functions an ELF or Mach-O image runs before and after main: ELF
DT_INIT, DT_FINI, preinit, init and fini arrays and .ctors/.dtors, and
Mach-O __mod_init_func, __mod_term_func and __init_offsets, with slot
addresses, resolved targets, symbols where available and relocation
facts for relocatable objects. Nothing is executed. Slot, symbol,
relocation and note budgets are shared across tables, sentinels are
matched at the image width, and target-derived names are bounded and
marked untrusted.
* fix(native): keep ELF initializer slots unresolved when relocation coverage is partial
A skipped, unreadable or over-budget section relocation table now keeps
every slot it could reach unresolved instead of reading the stored word as
raw or dropping a .ctors slot as a sentinel. Dynamic writes that only
partly overlap a slot, or write it twice, leave it unresolved. Linked
images no longer reapply retained non-SHF_ALLOC link-time records.
The dynamic relocation walk now pauses at the shared reader's step
boundaries and observes cancellation there, resolved targets need only
their first byte mapped, and unreadable symbol names make the report
incomplete.
* fix(native): stop reporting unchecked Mach-O initializer words as resolved
Object files now apply their own section relocations: an UNSIGNED entry
against a defined symbol gives symbol_addend, one against an undefined
symbol gives unresolved_import, and any other entry leaves the slot
unresolved. Linked images without a fixup decoder, such as 32-bit ones,
keep pointer slots unresolved when an LC_DYLD_INFO bind stream or
LC_DYSYMTAB external relocations could replace the stored word.
LC_DYLD_INFO rebases now report dyld_info_rebase instead of
chained_fixup, and unreadable symbol names make the report incomplete.
Section headers keep their declared reloff and nreloc for this.
* docs(native): move initializer guide under docs/guides/native
The documentation guide assigns analyst workflows to docs/guides/<domain>/.
Move the guide there, place its navigation and directory entries in the
Native binaries group, and update links from the docs index, CLI guide,
spec and authored skill. The public /native-initializers route is
unchanged.
The guide and spec also describe per-slot relocation coverage, retained
link-time records, Mach-O object relocations, undecoded bind streams,
the dyld_info_rebase resolution and unreadable names.
* fix(native): keep IFUNC and partly relocated ELF initializer slots unresolved
An absolute relocation against an STT_GNU_IFUNC symbol no longer reports
the resolver address as the target. Section relocation records now use
their real write width, so a record that writes only part of a slot,
starts inside it or repeats it leaves the slot unresolved instead of
reading a raw zero or dropping a .ctors slot as a sentinel.
On 32-bit ARM the Thumb bit is cleared when locating a target's section
and file offset, while value_va keeps the stored callable value.
* fix(native): locate Thumb Mach-O initializer targets and reject arm64_32 with a 64-bit header
On CPU_TYPE_ARM the Thumb bit is cleared when locating and naming a
target, since nlist values do not carry it. An arm64_32 image that
declares a 64-bit header is rejected as malformed rather than read with
8-byte slots. The guide and spec describe IFUNC relocations, partly
relocated section slots, Thumb targets and this rejection.
* fix(native): index initializer address lookups and keep ambiguous mappings unknown
ELF and Mach-O initializer lookups now build the shared file mapping
index once per image, so each slot and target lookup is logarithmic in
the number of load segments rather than a scan of every mapping.
An address that load segments map ambiguously no longer falls back to a
section header offset. Its file offset is reported as unknown and the
report is incomplete; only genuinely unmapped spans use the section
fallback.
* fix(native): report Mach-O bind names over the name budget
A bound import name longer than the 1024-byte name budget was dropped
without a trace, although it is the only identity of an unresolved
initializer target. It now counts toward the over-long name limitation
and makes the report incomplete.
* test(native): count mapping lookups instead of timing the load index regression
The load-mapping regression asserted a wall-clock bound, which is
sensitive to machine load. It now spies on the shared mapping module and
requires the same number of mapping scans and index builds for 8 and 64
slots, so a per-slot scan of every load segment fails deterministically
on a small fixture.
* fix(native): yield while decoding legacy rebase runs
* fix(native): preserve initializer mapping and coverage
* fix(native): index legacy fixup spans
* fix(native): preserve relocation uncertainty and bound bind lookup work
* fix(native): validate ELF slot mappings and yield section relocation scans
---------
Co-authored-by: Matt Van Horn <[email protected]>
Co-authored-by: morluto <[email protected]>
* fix(desktop): resolve desktop entry ids that name a nested entry
The desktop file id of an entry below an applications folder replaces
the directory separators with dashes, so applications/vendor/foo.desktop
advertises vendor-foo.desktop. locateDesktopEntry only probed the flat
path, so such an entry was either not found or, when a lower-priority
XDG root installed a flat file with the same id, a different
application was launched under the requested id.
List each applications directory instead and form every entry's id from
the path that holds it, so a dash inside a directory name does not
change the answer. The walk is bounded and stops reporting absence once
it can no longer decide, so a truncated search refuses instead of
falling through to a lower-priority root.
* fix(desktop): search nested entry directories without skipping answers
Three defects in findNestedDesktopEntry, all of which could select a lower-priority application under the same desktop file id:
`readdir` failure was treated as an empty directory, so an applications root or nested directory that could not be listed silently yielded to a lower-priority root. Only ENOENT and ENOTDIR now mean the root is not installed; any other failure reports desktop_entry_search_incomplete.
A directory whose own name ends in `.desktop` was skipped before it was searched, so `vendor.desktop/app.desktop` (id `vendor.desktop-app.desktop`) was unreachable. The suffix now selects a candidate only for files; directories still descend.
The entry budget was applied after `readdir` had materialized every name, so a large directory allocated far more than the 4096 entries it may examine. The walk now enumerates with `opendir` and charges the budget per entry.
* fix(desktop): finish the reachable nested search before reporting a gap
An unreadable or over-budget subtree is not proof the entry is absent, but
returning truncated as soon as one was met stopped the walk before the
siblings that could still hold it. The entry then looked incomplete even
though this root could answer, so a lower-priority root was never consulted
and the search reported a gap instead of the entry.
* test(javascript): distinguish package scope in route fixtures
* test(apple): separate identity and refusal fixture groups
* fix(desktop): resolve only applicable desktop ID paths
* test(desktop): cover applicable probe failure before a matching spelling
---------
Co-authored-by: 2SinceJuly <[email protected]>
Co-authored-by: morluto <[email protected]>
* feat(cli): expose provider selection diagnostics in session status
The registry now records how each deep provider became selected and why a
candidate was rejected, but the session status projection dropped both
fields. CLI providers/capabilities and MCP binary_session therefore showed
a candidate list that could not explain an automatic or explicit choice,
so users had to re-derive the reason from prose in selection errors.
Project selection_source and rejections for every candidate. Complete detail
keeps the rejection remediation and diagnostics; summary keeps the stable
origin, code and reason and lists the dropped fields in omitted_fields.
Existing candidate fields are unchanged, so both projections stay additive.
* test(javascript): distinguish package scope in route fixtures
* test(apple): separate identity and refusal fixture groups
* fix(session): preserve binding source in refreshed candidates
---------
Co-authored-by: 2SinceJuly <[email protected]>
Co-authored-by: morluto <[email protected]>
* fix(mcp): narrow union branch diagnostics to matching discriminators
* fix(mcp): merge union expected options at identical diagnostic paths
* fix(mcp): guide cross-kind sub-selector mistakes on inspect_analysis_view
* fix(mcp): support heterogeneous strict object variants in union discriminator narrowing
* fix(mcp): resolve union discriminators before narrowing and preserve nested alternatives
* fix(mcp): resolve setting discriminator before narrowing and preserve invalid_type failures
* fix(mcp): narrow branches by matched discriminators before synthesizing diagnostics
* test(javascript): distinguish package scope in route fixtures
* test(apple): separate identity and refusal fixture groups
* fix(input): preserve selector failures within projection bounds
---------
Co-authored-by: morluto <[email protected]>
* test(mcp): pin Kimi/Moonshot-accepted input schemas for #9581Closes#9581#9581 reports Kimi/Moonshot rejecting 9 tools whose input schemas
carried a $ref to a oneOf/anyOf with a nested $ref. The inlined
advertisement that fixes this is already on main (reused: "inline"
in toolInputSchemaWithMetadata, 554816be, unreleased): all 246
advertised input schemas are reference-free in both profiles. This
adds the regression coverage that pins it: the issue's minimal
trigger through the compact presenter, and the rejected reference
pattern checked across every served input schema in both profiles,
naming the nine tools from the issue.
Not verified against live Kimi/Moonshot (no access); verified locally
that no advertised input schema in either profile contains the
rejected pattern.
* test(javascript): distinguish package scope in route fixtures
* test(apple): separate identity and refusal fixture groups
* test(mcp): qualify reference detector and semantic inlining
---------
Co-authored-by: morluto <[email protected]>
* fix(javascript): reject a spawn stdio string Node refuses at the call site
`node:child_process` validates a literal `stdio` string before it creates anything, so a
bad value throws from the call itself and no child process exists. The analysis reported the
raw string as `stdioMode` and kept the spawn.
Measured on Node 24.22.1:
| `stdio` | outcome |
| --- | --- |
| `"pipe"`, `"overlapped"`, `"inherit"`, `"ignore"` | child starts |
| `"ipc"`, `"PIPE"`, `"pipe "`, `"pipeline"`, `"stream"`, `"stdio"` | THREW `ERR_INVALID_ARG_VALUE` |
The accepted set is exact and case-sensitive, so it is a closed list rather than a pattern.
`"ipc"` is the interesting one: it is valid as an array element but refused as the scalar
shorthand, which is the shape a caller reaches for when they mean the array form.
`fork` differs on one value because it appends `"ipc"` to a string and splits it, so `""`
becomes the lone element `"ipc"` and is refused:
| call | outcome |
| --- | --- |
| `spawn(..., {stdio: ""})` | child starts |
| `fork(..., {stdio: ""})` | THREW `ERR_INVALID_ARG_VALUE` |
`exec` is left alone, because it spreads its options and validates none of them. A number,
boolean or object `stdio` is also refused by Node, but `stdioMode` already reports those as
`"dynamic"`; deciding them here would claim more than the reported mode explains, so only
the string case is decided.
18 cases, 9 failing before this commit. `src/domain/javascript`: 5106 to 5124 passing.
`tsc` 0; `build` 0; `npm run format:check` 0; `npm run lint` 0 errors.
* fix(javascript): reject a non-string cwd or argv0 Node refuses at the call site
The same call-site validation the `stdio` string gets applies to `cwd` and `argv0`:
Node checks them before the child exists, so a non-string literal throws
`ERR_INVALID_ARG_TYPE` and the analysis still reported a spawn.
Measured on Node 24.22.1, identical on `spawn`, `fork`, `execFile` and `exec`:
| `cwd` / `argv0` | outcome |
| --- | --- |
| `"/tmp"` / `"x"` | child starts |
| `null` | child starts |
| `5`, `0`, `true`, `{}`, `[]` | THREW `ERR_INVALID_ARG_TYPE` |
`env` is deliberately not in the set: Node accepts any `env` value, including `5` and
`"x"`, so a non-object `env` is not a call-site failure.
Only an exactly readable literal is decided; a binding, a call or a member read stays
undecided, because its value is not settled by this expression.
The test file is renamed from `spawn_stdio_literal` to `spawn_option_literals` because it
now covers all three members. 16 cases added; reverting this check fails 9 of them.
`src/domain/javascript`: 5124 to 5140 passing. `tsc` 0; `build` 0;
`npm run format:check` 0; `npm run lint` 0 errors.
* fix(javascript): read the stdio and path options by value, not by AST kind
Seven review findings, each measured on Node 24.22.1. All seven were real, and two of them
were removing real children.
**`execFile` does not forward `stdio`.** Async `execFile` never passes `options.stdio` to its
underlying `spawn`, so `execFile(process.execPath, ["-e", ""], {stdio: "bad"})` returns a
real child — `stdioMode` already forces it to `"default"`. The new filter rejected it anyway
and dropped the spawn plus its linked interactions. `execFile` is now exempt, like `exec`.
**A no-substitution template literal is the same string to Node.** `readExactJavaScriptLiteral`
already resolves it, but the filter tested `t.isStringLiteral`, so ``{stdio: `ipc`}`` kept a
spawn Node refuses while ``{cwd: `/tmp`}`` — a valid path — was rejected because the same
branch called `readExactJavaScriptLiteral` and treated any hit as a non-string. Both sides
now read the value and test its type:
| call | Node 24.22.1 |
| --- | --- |
| ``spawn(..., {stdio: `ipc`})`` | THREW `ERR_INVALID_ARG_VALUE` |
| ``spawn(..., {stdio: `pipe`})`` | child starts |
| ``spawn(..., {cwd: `/tmp`})`` | child starts |
| ``spawn(..., {argv0: `x`})`` | child starts |
**A shape the literal owner does not price still has a settled type.** `{cwd: 1n}`,
`{cwd: /x/}`, `{cwd: () => {}}`, `{cwd: class {}}` and `{cwd: new String("x")}` all throw
`ERR_INVALID_ARG_TYPE`, and none matched the previous object/array/boolean arms.
**The `fork` explanation was wrong.** Node's `stdioStringToArray` rejects `stdio: ""` with an
explicit check before anything is appended; it does not split the string and it never builds
an IPC-only array. The rejection was right, the reason was not, so the comment now states the
real rule.
44 cases; reverting the `execFile` exemption fails 1, reverting the value-based read fails 1,
and reverting the extended non-string arms fails 6.
`src/domain/javascript`: 5158 to 5168 passing. `tsc` 0; `build` 0;
`npm run format:check` 0; `npm run lint` 0 errors.
* fix(javascript): keep a path-shaped cwd that Node accepts
`cwd` is a path, so Node takes a byte array or a file `URL` there, not only a string. The
blanket `NewExpression` arm rejected every constructor for both members, which dropped real
children.
Measured on Node 24.22.1, identical on `spawn`, `fork`, `execFile` and `exec`:
| `cwd` | outcome |
| --- | --- |
| `new Uint8Array([47, 116, 109, 112])` | child starts |
| `new Buffer("/tmp")` | child starts |
| `new URL("file:///C:/tmp/")` | child starts |
| `new String("/tmp")`, `new Number(1)`, `new Foo("/tmp")` | THREW `ERR_INVALID_ARG_TYPE` |
`argv0` is different: it is an argument string only, so `new Uint8Array([120])` and
`new URL(...)` both throw there. `isPathConstructor` names the constructors that can produce
a path, and only `cwd` consults it.
`URL` stays undecided rather than accepted, because whether a file URL resolves is a
platform property: `new URL("file:///tmp/")` throws `ERR_INVALID_FILE_URL_PATH` on Windows
while a drive-qualified path does not. Syntax alone cannot settle that, so the answer is
left open instead of guessed.
8 cases added; reverting the arm fails 3 of them.
`src/domain/javascript`: 5168 to 5176 passing. `tsc` 0; `build` 0;
`npm run format:check` 0; `npm run lint` 0 errors.
* test(javascript): distinguish package scope in route fixtures
* test(apple): separate identity and refusal fixture groups
* fix(javascript): keep unresolved cwd constructors unknown
* refactor(javascript): name process option admission by its contract
---------
Co-authored-by: morluto <[email protected]>
* fix(managed): report InternalCall methods as runtime-provided boundaries
inspect_managed_native_boundaries listed P/Invoke, native, runtime and
unmanaged methods, but not methods with MethodImplOptions.InternalCall
(0x1000). The runtime implements those bodies; mscorlib and
System.Private.CoreLib declare hundreds of them as FCalls. They now appear
in native_implementations with boundary_kind runtime-provided.
* fix(managed): keep InternalCall bodies out of CIL decoding
The native-boundary view reports InternalCall methods as runtime-provided.
Body admission still decoded CIL from a nonzero RVA when the row kept IL
and managed flags. CoreCLR rejects such a row (BFA_INTERNAL_METHOD_WITH_RVA).
Body admission now treats InternalCall like the Runtime code type. The
method gets a partial body with no instructions. The InternalCall test
covers both a zero and a nonzero RVA.
* test(javascript): distinguish package scope in route fixtures
* test(apple): separate identity and refusal fixture groups
* fix(managed): honor InternalCall during body admission
---------
Co-authored-by: morluto <[email protected]>
The IDA doctor test selects workspaceRoot `<root>/missing/..` and expects
the workspace to be unavailable. Windows folds `..` lexically before the
filesystem sees the path, as workspace allocation does, so the parent is
usable there and the test failed on every Windows run. Keep the POSIX
expectation and accept availability on Windows.
IDA checks a regex search pattern before it starts the provider. That early
check returned a generic "Supply a valid regular expression." issue, so IDA
callers never saw the parser explanation or the literal-mode recovery that
the shared regex worker gives Kuna and Hopper callers.
Build both refusals with one helper in RegexMatcher.
* feat(artifacts): inventory and extract Deno compile executables
Adds Deno standalone artifact support for Mach-O, ELF, and PE carriers,
exposing virtual files, entry points, recorded options, and file digests
without running the executable. Extraction uses the existing safe output
tree, preserves symlink targets as metadata, and assigns indexed paths
to unsafe or oversized names. Environment-file values and certificate
bytes remain in local Evidence; npm snapshots are reported by length
without decoding. The documentation describes supported layouts, parsing
limits, and refusal behavior.
* fix(artifacts): resolve nested Intel Deno carriers to the enclosing app
The backward trailer search stopped at the first confirmed sentinel, so an
Intel app storing another Intel Deno executable as a virtual file reported
the inner app. Search to the slice start, drop sentinels inside a confirmed
payload, and fail closed on side-by-side trailers that differ.
Correct the docs on retained environment values and generated regions.
* fix(artifacts): keep Deno coverage, remote paths and PE resources truthful
Report unknown coverage when the virtual filesystem cannot be listed, keep
resolved specifiers when another remote ID is missing, and percent-encode
characters in remote paths that a Windows destination would refuse. Reject a
directory at the PE resource language level, and correct the guide's
description of colliding generated regions.
* fix(artifacts): normalize Deno remote escapes so hex case cannot collide
Uppercase every percent escape in a generated remote path, so specifiers that
differ only in escape case reach the path registry as one name and the second
gets an indexed path instead of colliding on a case-insensitive disk.
* fix(artifacts): fail closed on truncated enclosing Deno trailers and encode reserved remote path characters
The Intel trailer scan rejected an overlong sentinel before reading its
marker, so a truncated enclosing app vanished and the nested app was
reported. Read the marker first and fail closed when a confirmed overlong
trailer encloses another trailer.
encodeURIComponent leaves `*` alone and the sanitizer covered only part of
the Windows reserved set. remoteDestination now encodes the whole set once
as uppercase percent hex; source_path keeps the original.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01KuWHGvzJkrhCqjtEHUwqyH
* fix(artifacts): defer truncated Intel Deno trailer failure until enclosing trailers resolve
A damaged Intel asset stored before a valid one inside a valid app made the
backward trailer search fail before it reached the outer trailer. Hold that
failure until the search ends and drop it when a confirmed enclosing trailer
holds the sentinel as data.
* fix(pe): fail closed when a resource type branch is unreadable beside a found name
namedResourceTargets skipped a type branch whose names directory, or a
name entry whose string, could not be read. A duplicate RT_RCDATA entry
pointing out of bounds therefore vanished once another branch matched,
and locatePeResourcePayloads returned only the readable ranges while
parsePeResources rejected the same tree.
The walk now records unresolved branches. hasPeResourceName still
reports false for a tree that never reaches the name, but once
locatePeResourcePayloads finds the name it raises a format failure if
any branch was left unresolved.
* fix(artifacts): skip Intel Deno sentinels that a native segment maps
A non-Deno x86_64 Mach-O that stores an Intel Deno executable in __DATA
had the asset's trailer promoted to the root application, because the
backward sentinel search never checked who owned the bytes it scanned.
locateMachOSlice now records the file ranges of every segment other than
__SUI and __LINKEDIT (libsui extends __LINKEDIT over the trailer), and
the Intel search skips any sentinel inside one of them. A slice whose
load commands do not frame stays with native inventory, since ownership
cannot be shown there.
* fix(artifacts): reject a universal Deno image with an unreadable sibling slice
The universal Mach-O locator skipped any slice whose load commands could
not be read or framed, and any slice with a FAT range issue. When another
slice confirmed Deno, that slice was reported as the whole image, so a
sibling declaring a different Deno application went unexamined.
locateMachOSlice now reports why part of a slice was not examined
(unreadable or unframed load commands, a truncated slice, or the Intel
sentinel allowance). locateMachO collects those reasons for every
declared and hidden slice and fails closed once any slice confirms a
Deno section. With none confirmed, the image stays with native inventory
as before.
* fix(scripts): supervise the Deno verifier version check
Run `deno --version` through the verifier command owner with a deadline
instead of raw execFile, and close the owner when the version is refused.
* fix(artifacts): keep unconfirmed Deno carriers native and fail truncated Intel trailers
An ELF SUI note whose descriptor is too short for the full d3n0l4nd name
now stays unconfirmed instead of failing closed; failures after the name
matches still fail closed.
Every marker-confirmed Intel trailer whose payload runs past the slice is
now kept until the backward search ends and fails unless an enclosing
payload contains it, so an Intel Deno app missing its last byte reports a
Deno failure instead of native inventory.
The Deno MCP boundary test now calls open_binary and inspect_artifact
through artifactMcpCall, as docs/testing.md requires.
* test(deno): drop source-text checks from verifier pin test
Keep the wrong-version subprocess check, which covers the pinned
release through behavior, and stop asserting on the verifier script's
source spelling.
* test(deno): close readers in finally in symlink and cancellation tests
A failed assertion or read error in either test skipped reader.close(),
leaving the file open while temp-directory cleanup ran. Both tests now
close the reader in finally, as CONTRIBUTING.md requires. The
closed-reader assertion in the symlink test stays after the finally.
* docs(specs): add deno-standalone spec, plan and tasks
Add docs/specs/artifacts/deno-standalone/ with spec.md, plan.md and
tasks.md for the Deno compile executable reader, as AGENTS.md requires
for a feature, and list them in the specifications index.
* docs: list the Deno compile guide in the documentation sidebar
* fix(deno): confirm the d3n0l4nd section before claiming Mach-O damage
A __SUI segment alone no longer makes later load-command damage Deno's.
A Mach-O whose __SUI segment holds only a differently named section now
stays absent and keeps native inventory, while damage seen before or
beside a confirmed d3n0l4nd section still fails closed.
* fix(deno): keep a slice with an unparsed segment command unread
A Mach-O segment command that frames but cannot be parsed may be the
__SUI segment, so the slice is no longer treated as fully examined. A
universal image whose other slice confirms Deno now fails closed instead
of letting that sibling supply the whole inventory, and with no confirmed
sibling the image still keeps native inventory.
* fix(deno): confirm an ELF SUI note before rejecting its length
A SUI note whose descsz runs past its note table was recorded as plain
damage before its descriptor name was read, so a malformed Deno carrier
fell back to native inventory. The owner and the d3n0l4nd name prefix are
now read within the table apart from the claimed extent, and a confirmed
note with an oversized extent fails closed.
* fix(deno): confirm a truncated __SUI section record by its name
A section record cut short after its 16-byte name was skipped, so a
damaged Deno carrier fell back to native inventory. The name is now read
apart from the rest of the record, and a truncated record that names
d3n0l4nd fails closed. The range checks for a confirmed record move into
their own helper.
* fix(deno): reject nonzero bytes after the last ELF note
When fewer than 12 bytes remained in a note table after its last note,
the walk stopped without looking at them, so a Deno note was selected
with complete coverage beside unaccounted data. The remainder is now
read: zero padding is accepted, and nonzero bytes are recorded as damage,
which fails closed once a SUI note is confirmed and otherwise leaves the
ELF to native inventory, matching the native note parser.
* fix(deno): validate FAT slice identity before choosing the search
The locator took a universal slice's CPU type from its FAT row, so a row
that called an x86_64 slice arm64 skipped that slice's Intel trailer and
let the arm64 payload stand for the whole image. Each slice's header is
now checked against its row with validateFatSliceIdentity. A mismatch
leaves the slice unread, so a universal image whose other slice confirms
Deno fails closed. The Intel fixture header now states
CPU_SUBTYPE_X86_64_ALL so its FAT rows can match it exactly.
* fix(deno): check the Intel trailer marker before judging its length
A trailer length word of 2^53 or more was dropped before its d3n0l4nd
marker was read, so an enclosing app with that length was skipped and a
nested app's range stood for the whole image. The marker is now read
first, and a marker-confirmed trailer whose length runs past the slice,
safe or not, joins the deferred truncated candidates and fails closed
unless an enclosing trailer holds it as data.
* fix(deno): count short ELF note tables against the note allowance
A note table shorter than one note header made a read of its trailing
bytes but counted no note, so many distinct tiny tables could force one
sequential read each past the 256-note allowance. Every nonempty table
now counts at least one entry, both in the up-front estimate and while
scanning, so an image past the allowance records the limit instead of
reading every table. The trailing-byte check moves into its own helper.
* fix(deno): fail closed on ELF extended section numbering beside a SUI note
A zero e_shnum with a nonzero e_shoff moves the section count into
section zero, so the section table was skipped and its SHT_NOTE tables
were never compared. Record it as damage, as PN_XNUM already is, so a
confirmed SUI note fails closed instead of reporting full coverage.
* fix(deno): keep undersized Intel trailers until enclosing ownership is known
A trailer length word below 8 was dropped before its d3n0l4nd marker was
read, so an enclosing app with a length of 0 to 7 was skipped and a nested
app's trailer was selected as the root. The marker is now read first, and a
marker-confirmed trailer too short to hold it is kept as malformed and fails
closed unless an enclosing trailer holds it as data.
* fix(pe): treat non-directory RT_RCDATA branches as unresolved
A duplicate typeId root entry whose target is a data entry was skipped
without marking the walk unresolved, so locatePeResourcePayloads returned
the readable named resource as complete. Route non-directory type
branches through the same unresolved path as unreadable names
directories so the Deno reader fails closed.
* fix(deno): fail closed on a zero-fill d3n0l4nd Mach-O section
denoSectionRange admitted the declared offset and size of a confirmed
__SUI,d3n0l4nd record without checking its section type, so a
S_ZEROFILL, S_GB_ZEROFILL or S_THREAD_LOCAL_ZEROFILL section could be
read as a file payload while MachoImage reports it as not file backed.
Reject zero-fill types with the shared isMachoZeroFillSection predicate
before assigning a physical range.
* fix(deno): bound the d3n0l4nd Mach-O section by its segment file extent
denoSectionRange checked a confirmed __SUI,d3n0l4nd record only against
the slice and file bounds, so a section reaching past its segment's
fileoff/filesize was still read as a complete payload while MachoImage
reports the same section as not file backed. Reject a section outside
its enclosing segment's file extent before admitting the physical range.
* style(deno): apply oxfmt to the DenoStandaloneLocation embedding union
* fix(artifacts): preserve Deno carrier ownership
* fix(artifacts): admit Deno parent segments
* fix(deno): preserve native segment ownership
* test(deno): cover mismatched Mach-O segment widths
---------
Co-authored-by: Matt Van Horn <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: morluto <[email protected]>
* feat(native): recover ELF function ranges from unwind tables
inspect_elf_unwind_ranges and inspect-elf-unwind-ranges read .eh_frame
and .eh_frame_hdr from one local ELF32 or ELF64 image and list the
function ranges its FDEs name, with CIE facts, LSDA and personality,
the containing section and an exact symbol where one starts at the
address. Nothing is executed and no disassembler is needed. Pointer
encodings follow the DW_EH_PE rules with raw zero as null, discarded
FDEs are counted and skipped, the .eh_frame_hdr search table is
compared with the decoded FDEs, and record, row and symbol budgets are
bounded. verify:elf:unwind-ranges compares the reported starts with
llvm-dwarfdump or readelf on a compiled C++ program.
* fix(elf): keep unresolved unwind facts out of reported ranges
Skip FDEs whose initial location is indirect and do not locate
.eh_frame through an indirect eh_frame_ptr, since pointer slots are
not functions. Stop an unbounded header-located scan at a CIE with an
unrecognized augmentation, report ignored duplicate PT_GNU_EH_FRAME
headers as incomplete coverage, and accept a null personality stored
under an indirect encoding.
Keep only symbols at decoded FDE starts, cache name observations per
string-table offset, and decode names only for the returned page under
a fixed 16 MiB text allowance, so repeated or long names no longer
grow memory with the symbol count.
* fix(scripts): run the ELF unwind verifier under owned process trees
Launch probes, compiler drivers, the stripper, the frame dump and the
inspection through createVerifierCommands, as verify-binary-hardening
does, so a timeout or interrupt stops the whole tree. The temporary
directory is removed only after the owned commands have closed.
* docs(elf): move ELF unwind ranges guide under docs/guides/native
The documentation guide assigns analyst workflows to docs/guides/<domain>/.
Move the guide there, place its navigation and directory entries in the
Native binaries group, and update links from the docs index, CLI guide,
spec and authored skill. The public /elf-unwind-ranges route is unchanged.
* fix(elf): give unwind text limits their own recovery and skip indirect tables
Throw a dedicated failure when a page's symbol names exceed the 16 MiB
text allowance, and map it to a resource error that tells the caller
to request a smaller limit rather than raise max_ranges.
Read a CIE augmentation with one bounded NUL search and keep it as a
byte view, decoded only for returned CIEs, so a long augmentation is
never built up character by character.
Leave an .eh_frame_hdr search table with an indirect encoding unread:
the header is incomplete, the comparison is unavailable, and the table
no longer bounds a header-located .eh_frame scan.
* fix(elf): resolve header count and table forms and name IFUNC resolvers
Leave a non-zero .eh_frame_hdr FDE count under an indirect encoding
unresolved, since it is the address of a slot that is not followed, and
mark the header incomplete without reading the table.
Treat a DW_EH_PE_omit table encoding as an omitted search table before
testing the indirect bit, so a header that declares a count without a
table stays present with complete coverage, and accept that form in
output validation.
Match STT_GNU_IFUNC symbols at exact FDE starts after function symbols,
with a limitation that the name is the resolver, not the implementation
it selects at run time.
* fix(elf): require allocated unwind pointer bases
* fix(elf): map unwind header relative base
* fix(elf): validate unwind load mappings
* test(elf): cover mapped nonallocated frame agreement
---------
Co-authored-by: Matt Van Horn <[email protected]>
Co-authored-by: morluto <[email protected]>
* feat(elf): report a Linux kernel image's version banner and embedded build config
inspect_kernel_build_config and inspect-kernel-build-config read one
local vmlinux, raw arm64 Image, gzip bzImage or zImage, or configs.ko
and report its Linux version banner and the IKCONFIG .config embedded
between IKCFG_ST and IKCFG_ED, without executing it. A config is present
only when a gzip member ends exactly at IKCFG_ED, passes its CRC-32 and
ISIZE checks and parses as .config text; framing that fails is reported
malformed, never absent. bzImage gzip payloads are inflated within a
budget and other codecs are named as unsupported, using codec detection
now shared with the kernel module parser. The config search, inflate
sizes and option counts are bounded, scans honor cancellation, and
target-derived text is marked untrusted.
* fix(elf): reject a gzip member whose header checksum fails
When FHCRC is set, compare the low 16 bits of the header CRC-32 before
inflating, so a damaged header no longer yields a present config or an
inflated bzImage payload.
* fix(elf): detect the bzImage codec inside the declared payload
Codec detection now reads the same payload_offset/payload_length slice that
is inflated, so bytes past a short payload cannot name its codec.
* fix(elf): report a framed config that does not decode as malformed
A damaged or cut-off deflate stream between IKCFG_ST and IKCFG_ED was
skipped and could end as an absent config with complete coverage. It is
now malformed with config_gzip_invalid, and a later valid config still wins.
* fix(elf): scan config lines in place and accept long string values
Config text is read one line at a time instead of split into an array, and
a quoted value is validated before it is decoded once. The 4096-character
string cap is removed: Kconfig has no such limit and max_config_bytes
already bounds every line, so a long CONFIG_EXTRA_FIRMWARE list is no
longer reported as malformed.
* fix(elf): let a live cancellation reach the kernel config parser
The parser ran synchronously, so an abort raised during parsing could not
change the signal before it returned. Inflation now runs on the zlib thread
pool and each scan yields to the event loop once per MiB, so the existing
cancellation checks observe a real abort.
* refactor(elf): carry the refused kernel config budget as a typed fact
Budget failures are now KernelBuildConfigBudgetFailure with a knob field,
and the provider builds its recovery advice from that field instead of
searching the diagnostic message for an option name.
* docs(elf): move the kernel build config guide under docs/guides/native
The documentation guide assigns analyst workflows to docs/guides/<domain>/.
Move the guide there, place its navigation and directory entries in the
Native binaries group, and update links from the docs index, CLI guide,
spec and authored skill. The public /kernel-build-config route is unchanged.
* fix(elf): yield while checking one long config line
A config made of one long quoted value never reached the per-line yield, so
the control-byte check, value validation and SHA-256 ran in one synchronous
block. Each now works one MiB window per event-loop turn, and the line
patterns match only a bounded name prefix instead of the whole value.
* fix(elf): return paged config entries that do not pin the decoded text
Entry names and values were V8 slices of the whole decoded config, so a
one-entry page kept the full text alive. Entries in the returned page are
now standalone copies, so retained memory follows the page size.
* fix(elf): yield while kernel marker searches find nothing
IKCFG_ST, IKCFG_ED and banner searches used one Buffer.indexOf over the
rest of the image, so a large image with no marker never yielded. They now
search overlapping one MiB windows and yield after each window without a
match.
* fix(elf): preserve long kernel config names
* fix(elf): frame kernel configs by gzip member boundaries
---------
Co-authored-by: Matt Van Horn <[email protected]>
Co-authored-by: morluto <[email protected]>
* test(javascript): distinguish package scope in route fixtures
* test(apple): separate identity and refusal fixture groups
* fix(electron): preserve uncertainty in indirect listener ownership
* 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 <[email protected]>
* fix(ci): match the observed call coverage probe marker
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: morluto <[email protected]>
* fix: preserve unknown identity in Apple IPS joins
* fix: require known CPU identity for Apple IPS joins
* refactor: centralize x86 Mach-O subtype identity
* docs(apple): explain unknown join identities
* fix(apple): bound architecture aliases to map entries
* feat(sqlite): page selected rows with a stated order and row offset
readRows read `NOT INDEXED LIMIT ?` with no ORDER BY and reported
order: "unspecified", so an agent could not reach rows past row_limit
deterministically: raising row_limit hits the fixed 16 MiB reply budget,
and the sampled order depends on whichever index SQLite happened to scan.
Read the window in a unique, stated order and expose the position:
- rowid tables order by rowid unless all of rowid, _rowid_ and oid are
declared columns, in which case the order stays unspecified.
- WITHOUT ROWID tables order by the primary-key columns, reusing each
key's DESC flag and declared collation from pragma_index_xinfo so the
plan keeps index order instead of materialising a sorter.
- rows.order, rows.row_offset and rows.next_row_offset report the window;
next_row_offset is null once the window reaches the end.
- row_offset (CLI --row-offset) requires table and defaults to 0. A
non-zero offset is refused when the table declares no usable order,
because the skipped prefix would be arbitrary.
The schema now checks next_row_offset against truncated and the offset so
a worker reply cannot claim a page it did not read.
One attribution fix came with it: adding ORDER BY text can push an
over-wide projection past SQLite's SQL-text limit, which fails as
SQLITE_TOOBIG before the result-column limit is reported. Attribute that
case to column capacity too, so a 250k-column request still reads as
capacity rather than a byte budget.
Verified: npm run check:fast, npm run format:check on the touched files,
npm run verify:module-boundaries, npm run docs:check, and 196 tests
across the sqlite boundary, application and CLI/MCP suites. A 1002-row
inspection and in-memory EXPLAIN QUERY PLAN checks confirmed the
WITHOUT ROWID path adds no sorter. Full check:pr and the complete test
suite were not run.
* fix(sqlite): resolve the rowid alias and keep paging claims reachable
Three defects in the row paging added alongside row_offset:
- readRowOrder emitted ORDER BY rowid for every rowid table, but a table
that declares a rowid column shadows that spelling, so the sort used the
stored column and could repeat or drop rows across pages. Order now names
the first alias the table leaves unbound, read from raw name bytes so an
unrelated malformed identifier is not decoded just to choose an order.
- The inventory projection could hide the primary key a WITHOUT ROWID table
orders by, silently degrading a paginated read to unspecified order. The
order key is now read from the table itself rather than from the columns
the caller asked to see.
- A truncated table with no stated order reported a next_row_offset that the
row_offset rule then refused. It is null there, and the reply limitation
no longer claims an unspecified order for windows that have one.
rowEvaluationFailure also probed the first rows of the table instead of the
requested window, so a failure confined to a later page was attributed to
columns that evaluate fine there.
* test(sqlite): assert the concatenated pages
The paging regression checked only counts, offsets and flags, so a second
page that repeated the first page's rows would still pass. Assert the values
read across all three pages instead.
* fix(sqlite): decode row keys in the database encoding
readRowKeyFacts decoded primary-key names as UTF-8 and probed a fixed
hidden set, so two supported schemas could not be paged at all:
- A UTF-16 database stores key names as UTF-16 bytes, so "k" decoded to
NUL-padded text and ORDER BY named an identifier that does not exist.
Pass the detected encoding, as readColumns and the index-metadata path
already do.
- A virtual generated column named rowid (hidden=2) was absent from the
shadow check, so ORDER BY rowid evaluated the generated expression
instead of the rowid and could sort non-uniquely or fail outright.
Include generated columns in the alias probe.
Also stop decoding primary-key names for rowid tables: their ORDER BY
names an alias, never a declared key, so an unselected malformed key
name could abort an otherwise readable selection.
The page-concatenation assertion now narrows SqliteValue before reading
.value, which the null/blob arms do not carry; this was failing the
Static checks typecheck job.
* fix(sqlite): order WITHOUT ROWID rows by the index's own key sequence
`readRowOrder` filed `pragma_index_xinfo` key terms into a map keyed by
column name, so a valid composite key that repeats a column —
`PRIMARY KEY(a COLLATE BINARY, a COLLATE NOCASE DESC)` — collapsed to one
term. The generated clause then overstated uniqueness: it omitted the
BINARY term, so `A`/`a` ties survived, and the `row_offset` paging
advertised a `primary_key` order that the actual key does not establish.
Build the terms from the index rows in `seqno` order instead, keeping
repetitions, and bail to `unspecified` if any declared key column never
appears in the index. The declared collation stays explicit so a caller
column's own collation cannot re-sort the window.
Verified against a real SQLite database that the repeated key is accepted
and that the old clause orders A, a, B while the index orders A, B, a.
* fix(sqlite): accept a null next offset on unordered truncated rows
readRows already withholds next_row_offset when a table shadows rowid, _rowid_ and oid and declares no primary key, because every non-zero offset is refused there. The reply schema still required the offset whenever truncated was set, so the provider rejected its own worker result and the first sample never reached CLI or MCP. Require the offset only when the order is stated.
* fix(sqlite): probe row keys only for ordinary tables and name row_offset
readRowKeyFacts ran for a selected virtual shadow table before readRows
could reject it, so an unavailable module surfaced as SQLite's
"no such module" instead of the selection refusal. Probe the key only
when the selected kind is an ordinary table and leave the order
unspecified otherwise, since no rows are read for that kind.
The unordered-offset refusal also reported the generic selection reason,
which callers see against table. Give it its own reason so the failure is
attributed to row_offset.
* feat(sqlite)!: page selected rows with a stated order and row offset
Selected-row inspection currently reports only rows.order: "unspecified" and
offers no way to reach rows past the first sample deterministically. Main also
refuses non-zero row_offset today, so nothing relies on that omission; this
change makes the stated order and the offset the contract, reports the next
offset the caller feeds back, and keeps the existing byte, heap, statement and
deadline limits.
BREAKING CHANGE: rows.order becomes rowid | primary_key | unspecified, and
rows gains required row_offset and next_row_offset members. A client that
validates selected-table output against the previous schema (where order was
the only permitted value) rejects a now-valid reply. Migration: accept the two
new members and treat order: "rowid" | "primary_key" as the stated order the
offset applies to; a null next_row_offset means the window reached the end.
* docs(sqlite): say a null next offset also means no stated order
The selected-rows limitation described next_row_offset as null only when the window reached the end. An unordered table whose row_limit truncates the sample also publishes null, because an offset there would name a request the row-offset rule refuses, so the Evidence read as if the sample were complete. State the second cause and pin it in the existing regression.
* test(sqlite): drop the limitation wording assertion and state the unordered exception
The boundary test pinned the sentence a caller reads in `limitations`,
so a harmless rewrite of that text failed a paging test whose real
subject is `order`, `truncated` and `next_row_offset`.
docs/development/testing.md asks for facts over frozen explanatory prose,
and the three structured expectations already fail if the paging fact
changes.
The tool contract promised that retained rows always have a unique order
and can be paged. A rowid table that shadows rowid, _rowid_ and oid and
declares no primary key has no such order: it returns
order=unspecified with a null next offset and refuses a non-zero
row_offset. Say so in the description so callers do not plan a
continuation the tool always refuses.
* docs(sqlite): say unordered tables cannot be paged
The description told callers that a truncated table without a stated order
needs "the table read without offsets", but omitting row_offset starts at
zero again: with an unchanged row_limit the next call repeats the first
sample instead of continuing past it. Say that these tables cannot be
paged and point at row_limit for a larger first sample.
* 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.
* fix(sqlite): bound rowid alias comparison by name byte length
readRowKeyFacts decoded every column name before testing it against three short aliases, so a table declaring a multi-megabyte name could exhaust the worker heap even when the caller projected the column away. Reject names that cannot be an alias by their byte length, accounting for the database encoding, before building any string.
---------
Co-authored-by: 2SinceJuly <[email protected]>
Co-authored-by: morluto <[email protected]>
* docs(mcp): name the accepted seed kinds on trace_application_feature
The compact input profile reduces this schema to shallow properties, which
drops the seed union and its kind enums, so agents could not see which
kinds a seed accepts without reading the source. Describe the seed field
with its kinds, match defaults and one example; shallow reduction keeps
per-property descriptions, so the guidance survives the compact profile.
Closes#8205
* test(mcp): isolate seed guidance catalog configuration
---------
Co-authored-by: morluto <[email protected]>
* fix(javascript): keep strict module checks unproven when alias walks exhaust their budget
Fixes#8201.
* fix(javascript): examine queued module sources after a strict walk exhausts its budget
A strict check that exhausts the alias budget stopped the whole walk, so a module branch queued beside the growing alias was never examined. Stop expanding instead, and drain the queued sources.
* fix(javascript): drain exhausted strict walks by selected keys and finite expressions
After exhaustion the limiter widens every key, so draining by normalized paths could match process.foo as mainModule. Keep each queued source's selected keys, expand finite expressions such as sequences and conditionals, and stop only at bindings and calls.
* fix(javascript): follow bindings and calls breadth first in exhausted strict walks
An exhausted strict walk skipped every binding and call, so (flag ? m : r).require with const m = module lost the module branch when r was explored first. Drain the queue breadth first after exhaustion and follow bindings and calls under a second alias selection budget, so a growing alias cannot starve a sibling.
* fix(javascript): bound every state a strict walk creates after exhaustion
After the alias budget is exhausted, paths are no longer normalized, so a
cycle through containers or getters can keep growing its selected path
without crossing a binding or call. Count each new state against the
drain budget instead of only binding and call follow-ups.
* fix(javascript): retain uncertainty at alias cutoff
---------
Co-authored-by: Matt Van Horn <[email protected]>
Co-authored-by: morluto <[email protected]>
* fix(decompile): retain published index on failure
* fix(decompile): preserve publication failure facts
* docs(decompile): explain retained publication receipts
* fix(decompile): retain receipts across provider cleanup chains
* fix(evidence): retain diagnostics through cleanup primary chains
* refactor(evidence): retain receipts once on the returned error
* fix(desktop): release a capture when its start is cancelled after launch
Cancelling start_build_capture during the opened app's screenshot left
the capture running, so the next start was refused. The capture is now
kept for the owner's cleanup, and the next start stops it first.
* fix(desktop): keep a published witness error and tag an app data refusal
A second failure to close a published witness returns the original error,
so its record and cleanup lease stay bound. A reset the app itself
refuses is an AppDataRefusal; only a reply that is not that refusal stays
unsettled. A missing child of a cut view is undecided with the same
description in both evaluators, and the shared cases cover it.
* fix(desktop): align a cut view's missing text with the Python check
A missing child of a truncated view is undecided for the same sentence in both evaluators, including the pointer the view does not contain.
* fix(desktop): keep one published witness cleanup across retries
A published record and its bound cleanup error stay together. A later close failure returns that error, and the unused path that built a fresh adapter error is gone.
* fix(desktop): keep a bundle task read inside the witness result
A cancelled, refused, or failed read of the bundle's task files returns the witness's Result, including a cleanup lease when a handle would not close.
* test(desktop): pass the settle timing when reading a witness bundle
The witness takes the settle timing before the bundle path, so the bundle-read tests keep the path in its own argument.
* fix(desktop): bound a guest file answer and split a long controller argument
A data/get whose base64 answer would exceed a quarter of the V8 heap is refused before it is read. Controller JSON that exceeds Linux's single-argument limit travels as several arguments the controller joins.
* fix(desktop): keep an underreported guest file from raising its read
A data/get answer is admitted only when its base64 body fits in the heap quarter after the copies held during parse. Decoded bytes larger than the computer reported are dropped.
* fix(desktop): grade shipped seeds and keep selected settings and sqlite logs
Seed checks follow the files the bundle ships, so an omitted lock is absent. An incomplete checkpoint is not reported as no change. A selected link keeps its spelling, and a database's wal and journal stay supporting state. Closing the fake computer stops a detached capture.
* fix(desktop): keep added-record checks off ids, dates, and joined timestamps
A position-named record's fields keep their paths, so an identifier, a stamped date, and text past the limit are not suggested checks. A whole-text check does not climb into an ancestor whose text joins a timestamp.
* fix(desktop): bound home fetches and keep unsettled edits readable
Split home-folder reads by the guest answer budget, keep a capacity error
from the capture output, and drop the superseded task-argument refusal.
Grade kept text per line, leave unretained dconf unread, and reserve
by-position notes for XML children. Map a linked database's sidecars
through the selected spelling and stop a detached capture only when its
start identity still owns the pid.
* fix(desktop): describe unretained dconf as unread
The key comparison treats a missing copy as unknown. The comment still
said that copy counted as an empty key list.
* test(desktop): keep a capture capacity error through endedCapture
An output past the guest answer ceiling stays the resource constraint,
with its heap recovery and non-retryable status, instead of a generic
capture-exit message.
* fix(desktop): keep nested save times and formatting runs out of checks
A field check drops save-time leaves before it joins the field, so a nested
timestamp is not part of the suggested text. A text check beside a save time
uses the paragraph when the diff names a formatting run.
* test(desktop): read paragraph pointers from checks that have them
* test(desktop): expect a large task to export intact
The 96 KiB refusal is gone. A prompt past that size reaches the computer
and comes back unchanged, instead of failing before export.
* fix(desktop): keep real edits that save-side heuristics were dropping
Repeated unnamed fields stay content checks. A formula that still has
text is not renumbered with its ExprID. One gained style attribute is
the edit; a repeated fill-in stays upkeep. Phone numbers and plain
clock times stay text, and a decimal is the element's whole text. An
older verifier matches on the task it states, not its evaluator source.
* fix(desktop): cover repeated records and keep timestamps out of checks
An added record whose words are already in the seed still gets the list's
text when that text fits. Readiness is a start reason, so a final check
names seed locks without matching the message. Field text split by a
timestamp stays separate, and a timestamp line is not offered as a guard.
* fix(desktop): compare a cell's joined decimal within tolerance
The whole-element check keeps numeric tolerance, so 437.80 and a long
float still match 437.8, including when a run splits the digits. A
different number or other text does not. Both evaluators read an
element's text for that comparison.
* fix(desktop): grade a number from a complete XML element only
A tolerance check joins an element's text only in an XML file or an XML
archive entry, and leaves that check undecided when the view is cut, so a
dropped descendant cannot turn the kept runs into a passing number. A JSON
object with the same shape stays the value itself. Equivalent decimals and a
split run on a whole cell still match.
* fix(desktop): accept an older grader only when its index helper agrees
The witness still matches a bundle whose evaluator is this REA's, or the older
index expression alone when no path segment is a leading-zero number. A
truncated verifier, a changed grader, or that older helper beside a segment
such as 01 no longer counts as the same task.
* fix(desktop): require the added record and leave save times out of list checks
A record a selector names is checked itself, so its words have to be in that
record. When an unnamed record only repeats words the seed already has, a
list that holds a save time is checked by the text around that time. A later
save of the same records still passes, and a missing or rewritten record fails.
* fix(desktop): judge the older index helper by pointer segments only
A leading zero in a file name or a URL does not change how the older index
helper reads a pointer. The witness still rejects that helper when a pointer
segment is a number such as 01.
* fix(desktop): do not suggest list text the seed already passes
A repeated record is shown by text the seed does not hold. The seed's own
text is not a check. A pointer inside a check value is not a pointer segment
for the older index helper.
* fix(desktop): keep a linked settings file in the folder the link points at
A selected ~/.config alias no longer moves MuseScore settings to that alias.
The seed keeps settings/MuseScore or Documents, which is the path the export
opens the note from.
* fix(desktop): split home fetches by answer size and keep a size refusal
A batch of tiny files stops before each path's envelope exceeds the host
answer budget. A capture output past that ceiling stays a resource error
instead of becoming a generic exit.
* fix(desktop): seed a selected link under the path the app uses
The selected spelling wins, so ~/.config/MuseScore is not rewritten to the
folder it points at and the note stays the one launched document. A database
wal and journal follow that database. A launch argument is respelled the same
way. Teardown signals a detached PID only when its start identity still matches.
* fix(desktop): keep a witness's actions and an incomplete checkpoint
A failed witness save retries the action log it first recorded. A deletion
left unconfirmed between the seed and the end is not reported as no change.
Windows packs npm through a shell, which can start npm.cmd.
* fix(desktop): read verifier pointers from the parsed checks
A brace inside a check value is not an object boundary. A leading-zero
pointer after that value still rejects the older index helper.
* fix(desktop): prefer the most specific home spelling and retarget launch files
A selected parent directory no longer hides a settings folder walked through
a link. A launch filename under that folder is replaced with the seeded
file's absolute path.
* fix(desktop): pack the build runtime through npm's JavaScript entry
npm pack and npm ci no longer spawn npm.cmd through a shell, so a temporary
directory whose path contains spaces stays one argument.
* fix(desktop): spell launch settings and keep a database log exact
A database log matches only that file, so a neighboring folder is not moved.
A launch option and the working directory use the same home spelling as the seed.
* fix(desktop): move a state database's logs with the database
The write-ahead log and journal follow the spelling the database received,
including a walked state link. A neighboring folder stays put. A separate
--config argument uses that spelling too.
* fix(desktop): keep a selected file ahead of a database-log guess
A file selected as itself keeps that spelling even when its name ends in
-journal. Relative launch arguments are resolved in the captured working
directory before that directory moves.
* fix(desktop): keep outside-home database logs and option values
A selected database outside the home keeps its write-ahead log. Launch
options are rewritten only when they name a captured file or settings path.
* fix(desktop): follow a database mapping and own limitation text
A selected database keeps its log when a parent folder moves or the file
sits outside the home. Trash and seed-lock tests compare the owning notice
with the paths, and launch options that are not captured paths stay as written.
* fix(desktop): retarget a settings folder that holds captured files
A nested profile directory is a captured path when a seeded file is inside
it. An option value that only resolves under the home stays as written.
* fix(desktop): keep a program word and a directory's neighbor
The program after env stays the program even when a captured file sits in a
folder of that name. A selected directory is not a database, so a neighboring
file keeps the parent folder's spelling.
* fix(desktop): record whether a selected path is a directory
Capture stores the walked path's kind. A neighboring file of a selected
directory keeps the parent folder's spelling even when that directory never
changes and its children are not content roots.
* fix(desktop): mark containing paths without a pairwise scan
A recorded path marks its known ancestors as directories in one walk up
the path, instead of comparing every recorded path with every other.
* fix(desktop): keep a selected folder that is named like a database log
A folder the caller selected stays a selected root when its name ends in
-journal or -wal. A generated log beside a database file still does not.
* fix(desktop): refuse a build command that exceeds the argument vector
Splitting one argument still leaves the whole command under Linux's E2BIG
limit. A request past that budget is refused before the process starts.
* fix(desktop): remap a captured path in an env assignment
The program word stays the program. A NAME=value assignment keeps its name
and follows a captured path value when that path's spelling moves.
* fix(desktop): keep oversized text out of draft guards
A value or line longer than the draft's text bound is not copied into a
guard, so one large frame cannot fill the task by itself.
* fix(desktop): keep an empty env value empty
An empty assignment is not a path. Resolving it named the working directory
and rewrote the value when that directory's spelling moved.
* fix(desktop): check build runtime export capability
* fix(desktop): provide complete runtime repack command
* fix(desktop): retain runtime probe failures
* test(desktop): identify capture independently of schema preflight
* fix(desktop): preserve document URLs and value formats
* fix(desktop): bound added checks and omit noisy text guards
* fix(desktop): preserve launch URLs and JSON draft values
* fix(desktop): recognize captured file URL documents
* fix(desktop): render captured file URLs for runtime home
* fix(desktop): distinguish phone numbers from save metadata
* fix(desktop): preserve launch URLs and user text
* fix(desktop): preserve local file URL authority
* fix(desktop): preserve local file URL authority
* fix(desktop): render captured file URLs at launch
* fix(desktop): classify numeric timestamps using field context
* fix(desktop): classify numeric timestamps using field context
* fix(desktop): retain context for generated draft values
* fix(desktop): retain context for generated draft values
* test(desktop): cover generated IDs in text guards
* test(desktop): cover generated IDs in text guards
* fix(desktop): isolate launcher URL encoding
* fix(desktop): isolate launcher URL encoding
* fix(desktop): isolate launcher URL encoding
* fix(desktop): retain clean text in noisy record fields
* style(desktop): format the noisy-record draft files with the pinned oxfmt
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* 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
* docs(desktop): clarify task checks before scratch export
* fix(desktop): respell selected paths in file URLs
* test(desktop): group captured document spelling cases
* 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
* 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
* 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): 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
* style(desktop): format capture review fixes
---------
Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
dyld keeps the LC_DYLD_INFO rebase cursor in a uint64_t, so an
ADD_ADDR_ULEB, DO_REBASE_ADD_ADDR_ULEB or single-slot skip operand above
2^63 steps backward. REA tracked the cursor as an unbounded bigint, so the
same stream overflowed past 2^64, failed as "Rebase expansion leaves its
file-backed segment" and left every later slot unknown. The bind stream
already wraps the same way since 576b58cef.
Every advance now wraps at 64 bits. addRun still bounds each emitted slot
to its segment, so a wrapped cursor outside the segment and multi-slot
runs with a backward skip remain refused.
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: morluto <[email protected]>
* feat(apple): join Apple IPS frames to a selected Mach-O or dSYM slice
inspect_apple_ips reported every image UUID as unknown and stated that joined
frames were unobserved. Accept an optional join_path, read its LC_UUID and
__TEXT vmaddr with the existing bounded stable-artifact reader, and match by
canonical UUID plus architecture.
A frame joins only when exactly one slice carries the image UUID and an
agreeing architecture, so arm64 and arm64e never cross, all-zero UUIDs never
join, and a conflicted or unreadable artifact stays typed instead of guessed.
Joined frames report the slice __TEXT vmaddr plus the recorded imageOffset as
a static address, which is the value atos resolves for the same report.
Symbolication is still not performed.
* refactor(apple): remove superseded join integer type import
* fix(apple): share FAT admission and preserve completed join facts
* fix(apple): bind standalone joins to admitted source identity
---------
Co-authored-by: 2SinceJuly <[email protected]>
Co-authored-by: morluto <[email protected]>
* 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 <[email protected]>
* feat(v8): inspect V8 code cache containers and their retained byte patterns
Bytenode-compiled `.jsc` files — the V8 code cache container that `vm.Script`,
`vm.compileFunction` and bytenode 2.x write, and that Node single-executable applications
and Electron apps ship — had no format-native reader. `inspect_binary_layout` shows the
surrounding ELF/PE and `inspect_artifact` inventories the file, but neither decodes the V8
structures, so an agent could not tell which V8 line produced the file, could not learn the
container is metadata rather than source, and had nothing to point at a version-pinned
decompiler. Issue #8543.
`inspect_v8_code_cache` joins the format-native family with `inspect_python_pyc`,
`inspect_erlang_beam` and `inspect_kernel_module`: one explicit local file, bounded parsing,
no execution, no installation, no child process.
**The container is version-dependent, so the reader reports roles with confidence instead
of assuming an ABI.** All eight header words are reported with byte offset, observed value,
the role this reader believes the word has, and whether that role is `confirmed`,
`inferred`, or `unconfirmed`. Only three facts gate admission: the header size this reader
pins, the V8 code cache magic high half `0xc0de` (the low half is
`ExternalReferenceTable::kSize` and varies per build, so it is a build fingerprint rather
than a constant), and a declared payload length that reaches exactly the end of the file.
I derived the field semantics from the producer rather than assuming them, and two findings
changed the design:
- **The word V8 calls the source hash holds the source length on the measured producers.**
A 54-code-unit source stored `54`, and sources with the same code-unit length but
different content stored the same value, including a source whose UTF-8 length was 154.
The reader reports it as `source_length` and the docs say that this name is what the
measurement supports, not a claim about what another build stores there.
- **`vm.compileFunction` sets a high bit in that word.** The reader masks it out and reports
the producer family as `script` or `compile_function`, so the source length stays correct
for both producers.
Embedded strings are reported as **byte-pattern observations with absolute byte ranges**,
labelled as such in the field name and in the limitations. A reported sequence is not proof
V8 retained it as a string, and a missing literal is not proof it is absent.
**Fail-closed boundaries, all covered by tests:** an unknown magic is a typed
`unsupported_target` failure because the file is not a V8 code cache; a short header or a
payload-length disagreement is a typed `invalid_request` failure. Neither yields a partial
parse and neither records Evidence.
Non-goals are explicit in the tool description and the docs: the payload is serialized V8
heap data, not source; recovering logic needs a version-pinned View8-style decompiler that
this tool neither provides nor depends on.
Fixtures are generated from the running V8 instead of committing a version-pinned binary, so
the tests exercise the real producer and stay valid across Node upgrades.
- Semantic owner and earliest changed stage: format-native V8 code cache container decoding.
- CLI and MCP/tool-catalog contract: one new `artifact-provider` tool and one new
`inspect-v8-code-cache` CLI command, registered in `TOOL_CONTRACTS`, the `artifact`
language family, `TOOL_EFFECTS`, the capability declarations and services, and the
capability inventory. `docs:check` passes, so the generated catalog agrees.
- Provider, bridge, target-format, or platform compatibility: no provider runs and no
external tool is required, so the tool is available on every supported Node host.
- Evidence, artifact, provenance, or reconstruction contract: complete Evidence with the
payload byte range as its location; no artifact format, reader, or reconstruction change.
- Generated metadata, package, or installation impact: catalog and document facts
regenerated by the existing checks; no new dependency.
Expected effects:
- [x] Target content is returned as bounded structured facts.
- `src/v8/V8CodeCacheParser.test.ts` (13 cases): real-producer header facts, per-field role
and confidence, source length for both producers, an observation's absolute offset
verified against the file bytes, the limitation text, foreign magic, payload-length
mismatch, short header, missing payload bytes, a different build constant still admitted,
and the observation budget.
- `tests/boundary/mcp/v8CodeCacheMcp.test.ts` (4 cases): advertisement and annotations,
header facts over MCP, relative-path rejection, unsupported magic, malformed container.
- `tests/acceptance/v8CodeCacheCli.test.ts` (4 cases): the public CLI, relative-path
resolution, byte-range observations, and the typed failure.
- `npx vitest run src/contracts src/v8 src/application/v8CodeCache src/domain/v8` — 242 passed
- `npx vitest run tests/boundary/mcp/v8CodeCacheMcp.test.ts tests/acceptance/v8CodeCacheCli.test.ts tests/boundary/filesystem/productCatalog.test.ts tests/acceptance/tools/localToolContracts.test.ts` — 20 passed, 4 skipped
- `npx tsc --noEmit -p .` — exit 0
- `npm run build` — exit 0
- `npm run lint` — 0 errors
- `npm run knip` — exit 0
- `npm run docs:check` — exit 0
- `npx oxfmt --check src tests docs` — exit 0
Gap: the header table is measured on node v24.22.1 / V8 13.6.233.17-node.58 only. No other
V8 build is available here, so a different build's field order is untested; that is exactly
why only three facts gate admission and the remaining roles carry a confidence level rather
than being asserted.
Additive. No existing tool, schema, or behaviour changes. The reader never executes the
selected file, never launches a process, and never accesses the network; its audited effects
are `mutatesSession: true` only, matching the other static format readers.
Human owner: @XiaoBinGan
- [x] I reviewed the final diff and relevant existing code, understand the change and its edge cases, and take responsibility for the complete PR, including AI-assisted work.
- [x] I checked the evidence above, confirmed it supports the current change, and recorded meaningful validation gaps.
- [x] I declare this PR ready for initial maintainer review.
- [x] This PR addresses a concrete problem or an agreed enhancement.
- [x] Material scope and authority questions are resolved, and any required maintainer scope decision covers the final diff.
- [x] The PR has one focused outcome and the title follows `type(scope): outcome`.
- [x] Related issue: #8543.
- [x] Tests cover changed observable behavior and meaningful failure paths.
- [x] Owning docs, contracts, and generated metadata are updated where needed.
- [x] I checked the final diff for secrets, unrelated cleanup, and unsupported claims.
* chore: retrigger title and readiness checks after the title was corrected
* fix(v8): bound the payload scan and use the repository path conventions
Review feedback from @greptile-apps on four points.
**Bound the synchronous payload scan.** The scan is one synchronous pass over the
payload, so the 512 MiB ceiling allowed a single request to hold the process for
several seconds. The ceiling is now 64 MiB, matching the pinned CPython pyc reader,
and the default is 32 MiB. The scan stays synchronous, like the BEAM and ELF note
readers in this family, but the budget that bounds it is no longer large enough to
matter. The new `max_file_bytes` ceiling and default are documented.
**Use the absolute-path schema.** `path` now uses `absoluteLocalPathStringSchema`
instead of `localPathStringSchema`, matching `inspect_erlang_beam`. A relative name
previously passed validation and was resolved against the server's working
directory, so `module.jsc` could select a different file than the caller meant. The
rejection is now at the schema boundary.
**State the extraction step.** The handoff in `docs/v8-code-cache.md` now says a
cache embedded in a single-executable application or an Electron app must be
extracted first, because this reader requires the magic at byte zero and returns
`unsupported_target` for the surrounding ELF/PE executable.
**Load only the owning contract group in tests.** Both tests now import
`V8_CODE_CACHE_TOOL_CONTRACTS` directly instead of resolving the tool through the
full catalog, so a focused test no longer constructs every other tool's schema.
21 cases pass across the parser, MCP boundary and CLI acceptance files; `tsc` 0;
`build` 0; `knip` 0; `docs:check` 0; `oxfmt --check` 0.
* fix(v8): select the tool contract by name in the focused tests
Reverts the catalog-position selection I added for a review suggestion. This
repository has a lint rule (`rea(no-positional-tool-contract)`) that forbids
selecting a tool contract by catalog order, and `toolContract("inspect_v8_code_cache")`
is how every neighbouring focused test selects one. The positional form I tried
introduced two lint errors, so the named form stays.
The review concern was that resolving through the full catalog constructs every
other tool's schema. That cost is real but it is the repository's chosen trade:
catalog ordering must never select an operation or schema, and `toolContract` is
the named accessor that enforces it.
* test(v8): cover the new tool in the absolute-path boundary test
Main added `tests/boundary/mcp/absoluteLocalPathTools.test.ts` after this branch
was opened. It enumerates every tool whose MCP `tools/list` schema must reject a
relative path, and `inspect_v8_code_cache` uses `absoluteLocalPathStringSchema`,
so the tool belongs in that list. Adding it keeps the enumerated field set complete
instead of leaving one absolute-path tool untested.
`tests/boundary/mcp` + v8 + contracts: 256 passing. `tsc` 0; `build` 0; `knip` 0;
`docs:check` 0; `npm run lint` 0 errors.
* perf(v8): read the payload by index instead of allocating per byte
`for (const [index, byte] of payload.entries())` allocates a two-element array for
every byte of the cache payload. On a 1 MiB payload that allocation dominated the
scan.
Measured with a real cache built from this parser, alternated so the two forms run
against the same input:
| payload | entries() | indexed reads |
| --- | --- | --- |
| 1 MiB | 15.5ms | 4.4ms |
That is 3.49x on the scan, and the gap widens on smaller payloads where the
allocation cost is a larger share (7.3x at 64 KiB in a synthetic run of the same
shape). The scan is the whole cost of `inspect_v8_code_cache` once the file is read,
so this is the tool's own hot loop.
Output is unchanged: the parser returns one retained string for the 64 KiB fixture,
and `src/v8` plus the CLI and MCP boundary files stay at 21 passing. `tsc` 0;
`build` 0; `npm run lint` 0 errors.
* fix(v8): admit only decimal tokens on the numeric CLI options
Main added `tests/boundary/cli/cliDecimalArguments.test.ts` after this branch was
opened. It enumerates every numeric CLI option and requires `0x10` and `1e3` to be
rejected, because plain `Number` coercion would read them as 16 and 1000. This
command's three options forwarded the domain fields unchanged, so the test failed on
them:
inspect-v8-code-cache options.maxFileBytes: Parser.parse accepts 1e3
inspect-v8-code-cache options.maxStrings: Parser.parse accepts 0x10
inspect-v8-code-cache options.maxStrings: Parser.parse accepts 1e3
inspect-v8-code-cache options.minStringBytes: Parser.parse accepts 0x10
`cliDecimalField` publishes the input view that parses the decimal token first and
then lets the domain field own bounds, defaults and help, which is what the other
numeric commands use.
`tests/boundary/cli/cliDecimalArguments.test.ts` now passes. `tsc` 0; `build` 0;
`npm run lint` 0 errors; `knip` 0; `docs:check` 0.
* fix(v8): recognize module caches, and advise from remaining headroom
Two review findings, each measured on Node 24.22.1 before changing anything.
**A module cache was reported as a script.** `vm.SourceTextModule.createCachedData()`
sets bit 30 of the source-length word, which the source-length mask `0x1fffffff` leaves
out, and the producer check only tested the function bit at bit 29:
| producer | source word |
| --- | --- |
| `vm.Script` | `0x00000013` for a 19-unit source |
| `vm.compileFunction` | `0x20000009` for a 9-unit source |
| `vm.SourceTextModule` | `0x40000013` for a 19-unit source |
So a real module cache was labelled `script`, and `vm.Script` rejects that container. The
producer enum gains `module` and the module bit is tested first; the source length is
unchanged because both flags sit outside the mask.
**The budget advice recommended a retry that could not work.** Both limits have
ceilings — `max_strings` at 65536 and `min_string_bytes` at 256 — so an admitted file
with more than 65536 runs of 256 bytes reached the branch while every suggested retry
was a rejected input. `observationBudgetRemediation` now reads the remaining headroom
and only recommends a change that is still admissible, falling back to selecting a
smaller artifact when neither can move. The parser message states the fact and leaves
recovery to the provider, which is the only place that can see both budgets.
`MAX_INSPECT_V8_CODE_CACHE_MIN_STRING_BYTES` names the `min_string_bytes` ceiling that
the schema previously spelled inline, so the advice and the schema cannot drift.
`docs/v8-code-cache.md` describes both flag bits. `src/v8` plus the CLI and MCP boundary
files: 28 passing; 7 new cases fail on the base commit. `tsc` 0; `build` 0;
`npm run lint` 0 errors; `knip` 0; `docs:check` 0.
* refactor(v8): decide the budget recovery as data, and test the decision
The remediation tests matched fragments of the advice sentence and banned old phrases.
`docs/testing.md` asks for error codes, recorded limits and recovery behaviour instead of
frozen prose, so a harmless reword could fail those tests while wrong advice passed them.
`observationBudgetRecovery` now returns the decision — which retries remain within the
schema's admitted range, and whether a smaller artifact is the only option — and
`observationBudgetRemediation` renders one sentence from it. The tests assert the
decision, so the wording is free to change and an incorrect choice cannot pass.
This also fixes a bug in the previous shape: it took the `min_string_bytes` branch to mean
"lower it", but that branch is reachable with the value already at the schema minimum,
where lowering is not a legal retry. The decision now carries `canLowerMinStringBytes`
and a `requiresSmallerArtifact` flag for that case.
5 cases, all failing on the base commit, covering both headroom states, either ceiling
alone, and the run-length floor. `src/v8` plus the CLI and MCP boundary files: 28
passing. `tsc` 0; `build` 0; `npm run lint` 0 errors; `knip` 0; `docs:check` 0.
* ci: rerun after the wakaru release download recovered
The only failing job on this branch was `javascript_recovery / real-javascript-recovery`,
which failed on `curl: (22) The requested URL returned error: 504` while downloading
`wakaru-linux-x64.tar.gz`. The release is reachable again (HTTP 200), so this empty
commit re-triggers the lane.
* docs(v8): place the guide in the tree and record the feature
The guide sat at `docs/v8-code-cache.md`, outside the `docs/guides/` tree every other
guide uses, and neither index linked it, so it could not be reached from
`docs/README.md` or the VitePress sidebar.
Moved to `docs/guides/javascript/`, linked from both indexes, and recorded the feature
under `docs/specs/v8-code-cache-inspection/` with `spec.md`, `plan.md` and `tasks.md`.
The spec keeps only the premises that were measured on Node 24.22.1 — the three producer
words and the retained-string layout — rather than restating the implementation.
`npm run docs:check` 0; `npm run format:check` 0; `tsc` 0; `build` 0; `lint` 0 errors;
`knip` 0. V8 reader, CLI and MCP files: 28 passing.
* docs(v8): use the documented spec path and test the advice against its struct
Two review findings.
**The records were at `docs/specs/v8-code-cache-inspection/`.** `AGENTS.md:73` asks for
`docs/specs/<domain>/<feature-slug>/`, so they move to
`docs/specs/v8/code-cache-inspection/`.
**The verification section claimed more than the tests do.** It said the producer cases
were built from real `vm.Script`, `vm.compileFunction` and `vm.SourceTextModule` output,
and that the budget advice was compared with its owning formatter. In fact only
`vm.Script` and `vm.compileFunction` are real producers — the module case sets the
measured bit on a `vm.Script` cache — and only `observationBudgetRecovery` was asserted.
Corrected the claim, and closed the second gap rather than only describing it:
`observationBudgetRemediation` now has four cases holding the sentence to the struct it
renders from. The sentence must not advise a knob the struct closed, and must mention a
smaller artifact exactly when the struct requires one. Verified by making the sentence
advise `max_strings` after the ceiling was reached — the test fails. Wording stays free;
wrong advice does not.
`npm run docs:check` 0; `npm run format:check` 0; `tsc` 0; `build` 0; `lint` 0 errors;
`knip` 0. V8 reader, CLI, MCP, absolute-path and decimal-argument files: 34 passing.
* fix(v8): restore the absolute-path contract entry after the rebase
The rebase onto `main` conflicted in `tests/boundary/mcp/absoluteLocalPathTools.test.ts`:
`main` gained `inspect_api_contract`, `inspect_devicetree`, `inspect_playwright_trace` and
`inspect_python_pyc` in both the contract list and the path-field map while this branch
added `inspect_v8_code_cache`. Both sides are additive, so both are kept.
Without the resolution the branch carried conflict markers, which made `mergeable_state`
`dirty` and every check on it meaningless. Re-verified after the rebase: `tsc` 0, `build` 0,
`format:check` 0, `lint` 0 errors, `knip` 0, `docs:check` 0, and the V8 reader plus its CLI,
MCP, absolute-path and decimal-argument boundary files at 34 passing.
* test(v8): pin the producer from the runtime's own header word
An independent reviewer ran these files on Node 22.23.4, 24.22.1 and 26.12.1. Only 22.23.4
failed, on `separates the compileFunction producer bit from the source length`, expecting
`compile_function` and reading `script`.
The cause is the fixture, not the parser: `vm.compileFunction(...).cachedData` carries
`0x20000009` in the source-length word on Node 24.22.1, with bit 29 set, while Node 22.23.4
does not set that bit at all. Whether a given V8 build marks the word is a build detail, so
asserting one build's spelling made the check version-dependent.
The case now reads the word the runtime produced and asserts the parser maps it correctly,
and keeps the part that must hold everywhere: the source length is the low bits, so it
equals both `word & 0x1fffffff` and the source's length. The flag meanings themselves stay
pinned by the synthetic `withHeaderWord` cases above, which do not depend on any build.
Reverting the parser's `compile_function` arm fails this case and
`keeps the function and module flags distinct`. `src/v8` plus the CLI, MCP and absolute-path
boundary files: 24 and 34 passing. `tsc` 0; `build` 0; `npm run format:check` 0;
`npm run lint` 0 errors; `knip` 0.
* fix(v8): report an unflagged producer word as unknown, not as a script
`vm.compileFunction` marks bit 29 of the source-length word on Node 24.22.1 (`0x20000004`
for a 4-unit source) and does not mark it on Node 22.23.4 (`0x00000004` for the same call).
Whether a V8 build marks a function cache is therefore a build detail, and an unflagged
word is either a `vm.Script` cache or an unmarked function cache. The container carries
nothing that separates the two.
The reader reported `script` for that case, which is a confident claim the bytes do not
support. My previous commit made the test accept whichever value the runtime produced,
which recorded the ambiguity instead of resolving it in the contract. `unknown` is now a
producer value, the unflagged word maps to it, and the observed word stays in
`header.fields` for a caller that trusts a specific build fingerprint. A limitation line
says the same in the report itself.
`header.producer` widens to `["script", "compile_function", "module", "unknown"]`; `script`
is no longer produced, because no unflagged word can be shown to be one.
The spec's producer rule and its verification notes are updated to match. Reverting the
mapping to `script` fails 2 of the 24 `src/v8` cases. `src/v8` with the CLI, MCP and
absolute-path boundary files: 33 passing. `tsc` 0; `build` 0; `docs:check` 0;
`npm run format:check` 0; `npm run lint` 0 errors. `knip` reports one unused export
(`builtinDataEffectMethod`) on `main` as well, so it is not from this change.
* fix(artifacts): retain completed identity across native parser failures
* docs(v8): preserve unknown unflagged cache provenance
---------
Co-authored-by: morluto <[email protected]>
* feat(electron): report V8 cachedData execution boundaries
analyze-javascript-application treated new vm.Script(source, {cachedData}) and vm.compileFunction(..., {cachedData}) as ordinary source. V8 deserializes previously compiled bytecode for those calls, so an Electron preload wrapper that delegates its whole contextBridge and IPC surface to a .cache/.jsc snapshot appeared to have been fully analyzed with zero exposed APIs.
Record each vm compilation with its inert cache path when the cachedData value traces to a readFileSync/readFile of path syntax, and project a cached-data execution unknown plus a loads edge from the owning source. Compilations without a cache stay recorded as no-cached-data so absence remains distinguishable from an unparsed snapshot. Count cachedData executions in the Electron summary and state the coverage limit in the graph limitations.
* 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.
* style(domain): reformat the pending dynamic-code accumulator field
`npm run format:check` fails on this branch, which is what `Static checks` reports: oxfmt wants the `pendingDynamicCode` union on one line. Applying it is the whole change; no behaviour is touched.
* fix(electron): preserve VM callable and cache source uncertainty
* docs(javascript): specify cached-data compilation boundaries
* fix(electron): exclude disabled caches from recovered boundaries
---------
Co-authored-by: 2SinceJuly <[email protected]>
Co-authored-by: morluto <[email protected]>
* fix(evidence)!: keep one Evidence identity across Windows path case
On Windows, an existing caller path now takes its filesystem spelling when
that spelling differs from the resolved path by letter case alone. One file
spelled `C:\Temp\x.bin`, `c:\temp\x.bin` or `C:\TEMP\X.BIN` produces one
`parameters.path`, `normalized_result` path and `evidence_id`, matching the
existing separator and `.` folding. `caller_input_path` keeps the input.
Links, junctions, `subst` drives, 8.3 short names and paths that do not
exist yet keep the caller spelling, so no link is followed and requested
output names keep their case.
BREAKING CHANGE: Windows Evidence produced from a path spelled in a case
other than its filesystem spelling now gets a different `evidence_id`.
Fixes#9373
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(entropy): keep Windows casing lookup optional
* ci(entropy): select Windows identity verification
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: morluto <[email protected]>
* fix(cli)!: retain repeated WASM input paths
BREAKING CHANGE: caller_input_paths values may now be ordered arrays of strings as well as strings. Clients must accept both forms.
* fix(capture): retain safe caller path selections
* fix(evidence): retain auxiliary selections in direct summaries
* test(wasm): separate SDK schema and acquired-input coverage
* fix(electron): model view constructors as window-like surfaces
Electron's static pass recognized a window only through the literal
`BrowserWindow` constructor, so `BaseWindow`, `BrowserView` and
`WebContentsView` produced no node and their literal `webPreferences` were
invisible. IPC registrations reached through such a view's own
`webContents.ipc` were dropped too, because the receiver resolver had the
same single-constructor gate -- VS Code alone registers seven channels this
way, and a shipped sample of four applications put 29 percent of window
construction sites on an unmodeled constructor.
Read webPreferences through one constructor set shared by the construction
inspector and the IPC receiver resolver. The finding only carries the new
`window_constructor` field when the site is not `BrowserWindow`, so existing
projections and their evidence stay byte-identical. A non-BrowserWindow
owner is not admitted for `loadFile`/`loadURL` on itself: only BrowserWindow
exposes those directly.
* fix(electron): keep the view constructor label lookup nullable
Observation labels are nullable in the application graph schema, so the
byLabel lookup failed tsc with TS18047 and took the Static checks job
down with it.
---------
Co-authored-by: 2SinceJuly <[email protected]>
Co-authored-by: morluto <[email protected]>
* fix(javascript): keep export bindings out of module views
Exported symbol bindings reuse the javascript-module node kind, so the modules collection counted one row per export alongside the owning module. On a CommonJS or ESM file with N exports, inspect_analysis_view reported N+1 module units, module pages listed indistinguishable rows sharing the module path, and item selection by selector.path failed with "Multiple modules have path".
Treat a node whose observations carry semantic_role export-binding as an export of its owning module, not a module unit, and exclude it from module entries. Export bindings remain distinct graph entities for export-shape comparison and version matching.
Validation: a domain regression in analysisView.test.ts and a CLI acceptance regression over a real CommonJS analysis both fail on main and pass with this change. Also ran the analysis view, MCP boundary, CLI boundary and JavaScript acceptance suites (125 tests), npm run check:fast, and the MCP catalog schema test after regenerating .cache/mcp-tool-catalog.json.
* test(javascript): run the export-binding view case on the shared CLI
The case starts a compiled CLI process for each of its three commands,
paying startup and tool registration three times per run. The fixture
already provides a file-scoped process for calls with no stdin or explicit
timeout, so use it.
* style(javascript): format the constructed-evidence type in the module-view case
The acceptance case's inline graph type did not match what oxfmt emits for the
80-column limit, so `format:check` refused the tree. Reflow only; the assertions
are unchanged.
* 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 <[email protected]>
Co-authored-by: morluto <[email protected]>
* fix(elf): qualify Linux CORE notes by the declared ABI
* test(mcp): cover source fitting and omitted delivery budgets
---------
Co-authored-by: hhang <[email protected]>
* 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 <[email protected]>
* 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 <[email protected]>
Co-authored-by: Claude Sonnet 5 <[email protected]>
Co-authored-by: morluto <[email protected]>
* fix(javascript): analyze first-party sources before dependencies
Every source in one application analysis charges the same parent result
budget, and sources were offered in lexical inventory order. On an Electron
app.asar, node_modules/ sorts before an electron-vite out-*/ directory, so
dependency files spent the budget and every first-party main, preload, and
renderer file had its semantic analysis not started and its application
projection rejected. The summary then reported zero windows, preloads, and
bridges while electron_semantics still said "recovered".
Offer application JavaScript before JavaScript under node_modules; reported
files keep inventory order. Count Electron boundary facts only from files
whose projection was admitted, and report electron_semantics "partial" (with
a limitation naming the count) when an Electron-loading source was not fully
examined or reported.
Fixes#9321
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(javascript): treat limited Electron examination as partial coverage
A static pass can stop early at the Electron wrapper expansion budget or
another resource limit without failing the worker. Such a source carries no
analysis or projection failure, so its retained IPC or bridge facts still
reported electron_semantics "recovered". Read the owned examination status
and count a limited Electron-loading source as incomplete.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: morluto <[email protected]>
* feat(providers): canonicalize selection diagnostics
Extend candidate statuses with selection source and stable rejections derived from readiness and target support. Automatic selection now keeps every rejected candidate attributable, while explicit and ambiguous outcomes remain fail-closed and deterministic.
* test(providers): surface typed selection errors
Follow the repository test guide: throw the typed Result error before
reading the selected value so a failure reports the provider error.
* fix(javascript): mark unisolated program-var route values unproven
`JavaScriptRouteScan.#resolve` now records the identifier whenever a value
proof depends on a program-scope `var` in a script without a proven isolated
runtime. That binding is a global-object property, so `globalThis.NAME` or an
alias can replace it after the source initializer.
The mark lives in the shared resolver, so every consumer degrades from one
fact instead of each one opting in:
- path segments and navigation destinations stop asserting the source value
and report an unknown interpretation;
- an index flag stays unproven, as before;
- a route container keeps its source route set and reports an incomplete
interpretation, so `var routes=[...]` no longer loses every route.
The source initializer is still followed, which is what lets a container
consumer keep its facts while disclosing the gap.
* fix(javascript): keep literal child routes behind an unisolated children array
Rejecting the whole children configuration marked its records as claimed
by the interpretation, so the automatic-root loop skipped them and the
literal child paths disappeared even though their absolute paths need no
parent proof. Keep the source records reachable and let the walker report
the unproven ancestry as an interpretation gap.
* test(javascript): bound the index-route CLI acceptance matrix
The flag matrix runs sixteen CLI invocations in one test and passed at 20.7 s
against a 30 s default timeout, so it timed out on a loaded runner. The same
matrix is already asserted per case in javascriptRouteScan.test.ts, where each
case is an in-process parse; this file keeps one resolved case and one
unresolved case to prove both branches of the public CLI view.
* fix(javascript): retain ownership of unproven route literals
Keep source ownership separate from runtime value proof so rejected program-var route segments cannot reappear through the generic constant pass. Cover path, route, typed and alias forms, retain navigation controls and independent literals, and verify inventory and public CLI projections.
Run the existing CLI index cases independently so repeated process startup does not consume one combined test deadline. Preserve both cases and all assertions.
* fix(javascript): keep unisolated navigation data out of route literals
An unisolated program-var slot holds data, not a route. Refusing its
runtime identity released the object back to the independent route scan,
which then reported a query payload as a nested route. Report the object
as consumed navigation data and stop following it as a path.
* fix(javascript): follow rejected navigation data without trusting it
Claiming the whole object for a rejected value also claimed its sibling
properties, so an independent route in the same object disappeared.
Keep the property selection: ownership follows the slot, and only the
route consumers refuse an unproven runtime identity.
* fix(validation): stop reporting an exclusive bound as an inclusive minimum
Input issues report minimum and maximum as inclusive bounds, as JSON Schema does, but the projection ignored Zod's inclusive flag. A .positive() field such as inspect_analysis_view view.limit or observe_native_ui pid rejected 0 and reported minimum: 0, naming the rejected value as allowed; .lt() limits had the same problem for maximum. An exclusive bound now keeps the schema's own message (for example 'expected number to be >0') instead of a false inclusive bound. Inclusive bounds are unchanged.
* test(validation): cover exclusive and inclusive bounds through MCP
* fix(cli): advertise the source-location path and URL constraints
trace-web-source-location declared manifestPath, sourceMapPath and sourceMapUrl
as bare strings, so `--schema --json` advertised them with no minLength and no
absolute-path pattern. The service then refused a relative manifest or map with
a post-parse invalid_request naming manifest_path and source_map.path, while
trace-web-module-imports already advertises those constraints from its domain
fields.
Bind the three positionals to webSourceLocationInputSchema so the domain field
owns the bounds, format and help, matching the rule in cliDecimalField that the
domain field owns bounds, defaults, nullability and help. The CLI now refuses a
relative or NUL-bearing value at its own boundary with a field-level message
instead of surfacing a service-level failure.
Add a boundary test that compiles the advertised args schema and asserts the
shared path and non-empty URL constraints, next to the existing sibling case.
* test(cli): isolate the NUL rejection from the absolute-path check
The boundary case for trace-web-source-location used "manifest\0.json", which is
also a relative path. The absolute-path constraint alone rejects it, so the case
would still pass if schema conversion dropped the NUL pattern. Use an absolute
path with an embedded NUL instead, and add the same case for sourceMapPath.
* style(test): wrap input schema assertions for oxfmt
The two unknown-byte cases exceeded the line width and tripped
format:check in CI. No assertion changes.
* fix(cli): admit native data type selectors before opening a target
* fix(cli): advertise the native data type selector choice
* test(cli): reject invalid native data type selectors before target access
* test(cli): cover native data type selector discovery
* fix(cli): reject an empty native data type target before resolution
inspect-native-data-type still declared its positional target as a plain
string after #7956 moved its sibling local-file commands to
localPathStringSchema, so an empty target passed validation and resolved
to the working directory. Use the shared schema and cover the command in
the empty-target admission table.
* refactor(cli): share native data type selector guidance owner
* test(cli): bind native data type selector checks to owning contracts
* fix(wasm): admit compatible 1.0.x WABT releases
REA_WABT_BIN_DIRECTORY admission compared each --version banner to the
literal 1.0.42, so a Homebrew or rolling-distribution WABT that advanced
past that build was refused even though wasm-validate, wasm-objdump and
wasm2wat accept the same flags and emit the same output shape. Callers had
to keep a hand-extracted 1.0.42 tarball alongside the system package.
Admission now accepts the release range ^1.0.42 and records the observed
banner, matching how the Wakaru adapter already admits compatible builds.
The verified release stays the reference: a banner other than 1.0.42 is
retained as tool_profile.release and provider.version and the report states
that only 1.0.42 was verified. All three tools must report the same release,
since one directory is one installation. Managed downloads stay pinned.
Fixes#9197
* fix(wasm): bound WABT admission to releases the report schema accepts
WABT_ACCEPTED_RANGE was "^1.0.42", which also admits 1.1.0 and later 1.x
releases, while wasmArtifactSchema only accepts 1.0.x banners. A configured
tool reporting 1.1.0 therefore passed version admission, ran the inspection,
and then failed at report parsing. Use the explicit ">=1.0.42 <1.1.0"
range so admission and the schema agree, and test the boundary.
Also carry the compatible-release warning into both partial-Evidence
builders. Previously only successful reports stated that REA verified
1.0.42, so a validation rejection or later read failure under a
compatible release silently dropped the uncertainty that a success reports.
Share the text through wabtReleaseLimitation so both paths stay aligned.
* fix(wasm): stop claiming the 1.0.42 commit for a newer admitted release
A caller-supplied 1.0.43 or 1.0.44 was admitted with the observed banner in
tool_profile.release, but source_commit still carried the 1.0.42 revision,
which is a claim about a build that did not run. Emit null for any release
other than the verified 1.0.42; the observed release and each executable
digest still identify what actually ran.
Also spell the accepted range as it is defined (>=1.0.42 <1.1.0) in the guide
and the failure fixture instead of ^1.0.42, which in semver admits 1.1.0.
---------
Co-authored-by: 2SinceJuly <[email protected]>
* fix(objdump): reflect observed launch failure in discovery availability
Distinguish a statically registered standalone command from an executable
whose launch has failed in the active server session. Discovery leaves
unprobed commands optimistically available, reports an observed launch
failure as provider_unavailable with the command remediation, keeps the
tool callable for retries, and clears the failure after a subsequent
successful launch.
Fixes#8826.
* test(reverse): verify overlapping command launch dispatch ordering
* test(reverse): unblock gated test workers before awaiting service closure
* test(reverse): bound lifecycle acknowledgement and join launched workers
* fix(reverse): distinguish host capacity exhaustion from command launch failures
* fix(reverse): preserve host capacity across Linux process ownership preparation
* fix(reverse): exclude process ownership admission failures from command launch state
* fix(reverse): preserve Darwin process ownership preparation error across command launch
* fix(reverse): disambiguate cleanup declaration in provider observation
* fix(reverse): preserve command deadline timeout during Darwin ownership preparation
* fix(reverse): isolate Darwin baseline ps errors and provide platform-aware admission guidance
* fix(reverse): rely on typed baseline admission failures and use owning repair formatter in tests
* test(reverse): use owning formatters for process ownership remediation assertions
* fix(reverse): restrict Linux ownership prerequisite remediation to missing ps and preserve system error codes
* fix(reverse): retain typed process ownership prerequisite with preserved error codes
* fix(reverse): remove unvalidated error label fallbacks and keep admission tags out of system_code
* fix(reverse): keep Windows native runner failures out of command-launch state
* fix(process): preserve current_process_unreported refusal in Linux provider readiness
* fix(process): distinguish no-code ps failure from missing ps and cover rizin launch failure
* fix(windows): classify process_spawn host-control rejections as RuntimeControlUnavailableError
* fix(desktop): keep a failed library owner query unknown
dpkg-query -S exits 1 when some requested path has no owner and 2 when
it could not answer at all. LibraryPackageInventory.packageOwners only
rejected the missing and failed outcomes, so an exit of 2 with empty
stdout parsed as a successful empty owner map and every located library
was recorded in unpackaged_libraries. That turns a failed lookup into an
incorrect ownership fact and drops the reason, while the executable
owner lookup in DebianPackageInventory already treats exit codes above 1
as unknown.
Treat an exit code above 1 as a failed query for the batch, report it in
library_packages_reason, and leave the libraries it covered out of
unpackaged_libraries. Already established missing_libraries are kept.
* fix(desktop): retain completed library ownership queries
* fix(desktop): report ownership gaps per library
---------
Co-authored-by: 2SinceJuly <[email protected]>
Co-authored-by: morluto <[email protected]>
* feat(desktop): capture, draft, export and witness a task inside a build computer
- start_build_capture runs REA's capture inside the computer through a
small controller over computer-exec, after a user-space apt-get update;
mark_build_checkpoint returns once the capture confirms the checkpoint;
stop_build_capture returns the draft between seed and end.
- export_build_environment exports with the requester's task inside the
computer and fetches the bundle through data/get in bounded batches.
- check_build_task grades a computer's data/get with REA's evaluator and,
through seed, after_edit, near_miss and after_reset stages, records a
witness with the action log and the screen recording; act_on_build_computer
gains reset.
- Capture progress lines carry window, checkpoint and refusal events; apps
start in the home; a pack script builds the runtime archive; long tools
send progress notifications.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): make build capture and witness steps retryable, and close the near-miss reset hole
- A capture whose start answer is lost is stopped at once and kept
unreleased until the stop is confirmed; a lost checkpoint answer is
recovered from the capture's own log, and draft and export use the
checkpoints its Evidence records; stop can be repeated to resume
reading its result.
- A witness can check its seed again after a failed read, and keeps its
stages in a saving state until the record is written, so a failed save
can be retried without reading the app again.
- after_edit and near_miss count only click, drag, scroll, type and key,
so a reset no longer stands in for the near-miss edit.
- A .deb build keeps its package file for capture and passes its URL to
export; bundle downloads write each checked file before fetching the
next; an apt-get update failure reports its exit code and log tail;
reset sends progress while it waits.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): keep the Trash out of seeds, bundle folders unwritten and first frames drawn
From the blind Inkscape rebuild:
- Paths in ~/.local/share/Trash get a desktop_trash reason and the
shared_desktop role, so a file trashed before seed is neither owned nor
seeded; the export names it in a limitation.
- The keep helper runs with PYTHONDONTWRITEBYTECODE=1, so starting a
computer from a bundle folder no longer writes __pycache__ into it.
- start_build_computer waits, bounded, until the first frame is not one
colour before returning it.
From review: a .deb that apt also offers exports from apt, since export
passes --package-url only when the capture says the package must come
from its file.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): bound the first-frame wait's retries to the time left, with progress throughout
Each retry of the drawn-first-frame wait runs under an abort that fires at
the 10 s deadline, and the latest frame is returned when time runs out.
connect takes the first frame itself while its progress heartbeat still
runs, reporting "waiting for the desktop to draw". The wait moves to
DrawnScreenshot.ts.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): serialize each build computer's calls, keep one recording id, and separate unreadable checks from failures
- One queue per computer: actions, capture steps and witness checks run one
at a time, so two overlapping after_edit checks can no longer both be filed
and let the witness hold without a near miss. Close cancels the running and
queued calls; different computers still run concurrently.
- The witness keeps one recording request id per computer, so a seed retried
after a lost reply recovers the recording it started.
- A check that could not read its file is undecided, not a failure, so a
temporary read error can't satisfy seed, near_miss or after_reset; such a
stage is refused and can be checked again.
- The export's file listing is written to a file and read back in bounded
batches, so a large bundle no longer overruns exec's 1 MiB output.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): treat failures on cut views as undecided, require a decided seed failure at export, and keep a close's cancellation
- A presence check that misses its line, row or pointed value in a view cut
short is undecided rather than failed, since the data may sit in the part
left out; a witness stage can no longer be proved by such a failure.
- Export counts only decided failures on the seed, and refuses a task whose
every seed failure is undecided.
- A step whose turn was aborted by a close or the caller returns cancelled
even when its work finished, so start_build_capture no longer reports
success for a computer being removed.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): keep a step's saved result when a cancellation arrives during its commit
Reporting cancelled for any step whose turn was aborted after its work
replaced a completed export or witness save with a bare cancellation, losing
what was saved and leaving the witness unreadable. Steps now return what they
completed; only start_build_capture, which commits nothing, checks its turn
after the post-launch screenshot and reports cancelled. Asking again for a
saved witness's final check returns its record.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): refuse a saved witness's repeated check for another folder
Asking again for the check that ended a witness returned its saved record
whatever directory came with it, so a caller naming another folder got
success though nothing was saved there. A different folder is now refused,
as an unfinished seed's retry already is.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* test(desktop): assert the facts of cut-view and undecided outcomes, not their sentences
The new cut-view, missing-value, undecided-refusal and witness tests froze
whole descriptions, so a harmless rewording broke them. They now check each
outcome's id, passed and undecided exactly and the facts its message must
carry (file, pointer, line, row, stage) separately.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* feat(desktop): read XML with a plain DOCTYPE, and check an element's whole text
- Both readers accept a DOCTYPE with only a name and an optional PUBLIC
or SYSTEM identifier, record it as the view's doctype, and never fetch
the DTD or expand entities beyond XML's five; an internal subset, an
external entity and a parameter entity stay refused alike.
- text_equals and text_contains compare the concatenated text of the
element (or text node or attribute) a pointer reaches, as XPath
string() does, with optional normalize_whitespace; both evaluators
agree, and a text check on a truncated element is undecided.
- Whitespace-only text between elements is dropped only when the element
holds no other text, so text split into runs keeps its spaces.
- The draft suggests text_equals for an element whose text changed.
So an AbiWord .abw reads as XML, and its paragraph's text is checked
whole however AbiWord splits it into <c> runs, as Writer's spans are.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): draft whole-text checks on the paragraph, read only text as text, and skip asctime stamps
- A changed value is read at both ends, so text that became a run still
marks its paragraph, and the drafted text_equals moves up out of mixed
content to the nearest ancestor named by an attribute, such as
AbiWord's p by its xid.
- Text checks accept only a text node or an attribute value; element
names and doctype fields fail, in both evaluators.
- isTimestamp also recognises asctime dates (Www Mmm dd hh:mm:ss yyyy).
- XML files are drafted with the keyed diff the archive entries use, so
no suggestion reaches an element by position; the shared logic moves to
appTaskDraftNamed.ts.
- Text-check tests assert facts, not whole sentences.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): keep checks on a changed attribute inside a paragraph whose text check replaces its runs'
A drafted whole-text check on a paragraph hid every change inside it, so a
link whose label and href both changed drafted only the paragraph's text and
the new destination went unchecked. The text check now covers text and runs
added or removed whole; a changed name or attribute of an element both
versions hold keeps its own check.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): read an attribute called name or attributes as an attribute when the draft keeps its check
The owner of `.../attributes/name` was taken as `.../attributes`, so a
changed `name` attribute inside a paragraph with a text check was dropped
again. The owner is now read by segment, attributes first.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): draft text checks from text in runs added whole, and name a text element by its own id before its container
Text typed into a run the diff lists whole was never read, so the draft kept
only a presence check on the run, which wrong text also passed. Such text now
marks the nearest element both ends hold, or, when none can take the check,
the added element's own text. The climb also names an element by an
identifier of its own (id, xml:id, xid) before a larger container, so a
headline under a long layer keeps a whole-text check that survives re-split
runs.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* test(desktop): expect the whole-text check where #8988's pairing test meets text checks
On this branch a changed element's text is drafted as `text_equals` on the
element, so the identity pairing test now expects that check, under the
same `[@role=body]` key.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* feat(skills): build a computer-app environment from a request
The product skill gains references/desktop-build.md, a procedure that
takes Claude from a request such as "an Inkscape environment where the
agent changes a poster's headline" to a witnessed bundle and a report,
through start_build_computer, act_on_build_computer, the build capture
tools, export_build_environment and check_build_task: questions asked
once upfront, pauses only when blocked, a seed made in the app, content
checks by selector, a witness with a near miss and a reset in a fresh
computer, and solver runs only within a budget. setup installs the new
reference; skillVersion 61.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(skills): fold the blind Inkscape rebuild's friction into the build skill
key takes an array of one chord's keys; check for a text cursor before
typing in apps with single-key shortcuts; in Save As, click Documents and
type only the file name; anything saved before seed, the Trash included,
is part of the seed; dismiss first-run dialogs before seed; a bundle's app
starts with the seed's files but need not open the document; Inkscape's
window class is Inkscape.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(skills): fold the held-out AbiWord build's friction into the build skill
Name the act fields and link the guide; a region screenshot becomes the
latest frame; a drawn caret is not focus; confirm dialogs with Return;
check the dialog's folder when the app shows only a name; an XML file the
draft shows as lines was read as text, so use short line checks; and keep
text checks to the value the edit types, since some apps save a reloaded
document differently. Later witness stages take no directory.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(skills): state the export's seed rule, the Trash exception and draft refusals exactly
The export refuses a task unless at least one check fails on the seed and
every check passes at the end; a file moved to the Trash is left out of
the seed, so trash a stray file rather than starting over; a null draft
names its cause in draft_refusal; and a line check on a format read as
text only approximates a value, so bound it and call it approximate.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(skills): teach the build skill to use DOCTYPE and whole-text checks
The skill now checks text an app splits into runs whole with text_equals or
text_contains, finds an AbiWord paragraph by its xid, and drops the Writer
revision-id workaround.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(skills): point text checks at the paragraph, and say what the scrub replaces
From the AbiWord rebuild: point a text check at the paragraph or another
element the edit keeps, never at a run a reopened document splits; how to
name an unchanged element; the export replaces the host name, user@host
and home path while a bare user name stays; AbiWord's entry and class.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): keep every text check on a cut element undecided, in both evaluators
The XML reader drops text deeper than its depth limit but keeps later
siblings, and an element's whole text joins both sides, so a text_contains
on a cut element could match text the file never holds ("at 3" + deep
"WRONG" + ":30 PM" read as "at 3:30 PM"). Any text check on an element of a
cut view is now undecided again, as #9050 had it, in REA's evaluator and the
exported verifier; a text node or attribute value stays decided. The text
check tests assert facts rather than sentences.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* feat(desktop): open the seed's document at launch, and say when a seed was made for the task
An exported app now opens the seed's one document at launch when its desktop
entry's Exec takes files, so a solving agent starts in the document rather
than on the app's start screen; a command that already names a file is kept,
and several documents or an Exec without a file code keep the launch and say
why. `--synthetic-seed`, which the build export passes, words the README and
limitations for a seed made in the build computer instead of asking to keep
the user's data local.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): count only a document the launch names, absolute or from its working directory, as already opened
A settings path under the home, such as geany's --config, kept the seed's
document from opening, and a relative document argument was missed and
appended again. An argument now counts only when it resolves, from the
captured working directory, to a file the capture gave the document role.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* test(desktop): check the seed and launch wording against their formatters, and the facts on their own
The tests froze the README's and limitations' sentences, so a harmless
rewrite broke them. The wording now comes from exported formatters
(`seedWording`, `seedLimitation`, `launchDocumentNote`,
`NO_LAUNCH_DOCUMENT`), the tests compare against those, and check the facts
separately: which document opens, the respelled path in the command, the
README naming it, `--synthetic-seed` and the Evidence's `synthetic_seed`.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* refactor(desktop): plan the export's seeds and launch in their own module
The merged export module passed the file and function size limits; the seed
digests and the launch command now come from `DesktopExportLaunch.ts`.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): keep a reset applied when the screenshot asked after it fails
Once data/reset and the ready wait succeed, act_on_build_computer's reset
is applied in the action log and returns success; a screenshot asked of it
that is then refused or throws is named in the result's screenshot_error,
with no frame, so the next after_reset stage is not refused. Cancellation
still propagates.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): keep checks for elements without text beside a text edit, and normalize DOCTYPE system id line endings
A review found two gaps.
- A paragraph's whole-text check covered every element added or removed
inside it, including ones holding no text: an image frame, a line break.
A solver who typed the text but never inserted the image passed. Such an
element now keeps its own presence or absence check.
- The Python reader kept a DOCTYPE system literal's CRLF where TypeScript
normalizes it to LF. Both now report LF.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): wait out long build commands, fail a reset that restores nothing, and tie the witness to its bundle
A review found five gaps.
- Commands in the computer answer only when they end, but fetch stops
waiting for headers after 300 s. So a slow export, draft or start always
failed. A command with its own deadline now waits on node:http, bounded
by that deadline.
- after_reset held whenever some check failed, so a reset that changed
nothing passed on the near-miss state. Each check must now come out as it
did at the seed.
- A failed or partial apt-get update left lists that looked complete, so a
second start skipped the update. The update is now recorded only when it
succeeds, and rerun otherwise.
- A witness on a bundle computer graded whatever task it was given. Its
seed stage now refuses a task whose steps or checks differ from the
bundle's.
- A task too large for one command argument (96 KiB) is refused before the
export runs, instead of failing with E2BIG in the computer.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): advertise a checkpoint-name pattern that compiles in every JS regex mode
main's advertised-pattern test compiles every input schema pattern in v
mode, which refuses an unescaped hyphen in a character class. The
checkpoint name's hyphen is now escaped, and it means the same in u mode.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* test(desktop): import textSyntaxFormat once in the state format tests
main's mixed-case JSON suffix fix and this branch both added the import,
and the merge kept both.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): keep a completed reset, an unread seed, and a failed apt update
A cancel after data/reset no longer drops the restore from the action log,
so a near miss cannot treat the restored seed as an edit. An unreadable
seed check that later passes leaves after_reset undecided instead of
failing the witness. apt-get update is kept only when Error-Mode reports
success and a package list was fetched. The controller reply schema lives
with the controller, and capture progress lines are read by their domain
parser.
* fix(desktop): keep a whitespace-only run under the paragraph text check
A run whose only text is whitespace is still text the paragraph check
reads. It no longer gets its own presence check, which a later save of
the same words without that run would fail. Images and breaks, whose
text is empty, still keep their own checks.
* fix(desktop): keep an unread reset and a cut text check undecided
after_reset stays undecided when any check was not read, even if another
check still fails and the unread result's boolean matches the seed. A
text check for a child past a truncated view is undecided in both
evaluators. A reset whose reply is lost blocks a near miss until a later
input or a reset whose reply arrives.
* fix(desktop): retry a witness recording and a published record's cleanup
A recording that fails to end or download is not written into the witness
folder, and a manifest already received is fetched again without ending
the recording twice. A folder that is published while its handles fail to
close keeps that record and retries the same cleanup instead of writing
the folder again.
* fix(desktop): keep a published witness error and tag an app data refusal
A second failure to close a published witness returns the original error,
so its record and cleanup lease stay bound. A reset the app itself
refuses is an AppDataRefusal; only a reply that is not that refusal stays
unsettled. A missing child of a cut view is undecided with the same
description in both evaluators, and the shared cases cover it.
* fix(desktop): align a cut view's missing text with the Python check
A missing child of a truncated view is undecided for the same sentence in both evaluators, including the pointer the view does not contain.
* fix(desktop): keep one published witness cleanup across retries
A published record and its bound cleanup error stay together. A later close failure returns that error, and the unused path that built a fresh adapter error is gone.
* fix(desktop): keep a bundle task read inside the witness result
A cancelled, refused, or failed read of the bundle's task files returns the witness's Result, including a cleanup lease when a handle would not close.
* fix(desktop): check build runtime export capability
* fix(desktop): provide complete runtime repack command
* fix(desktop): retain runtime probe failures
* fix(desktop): recognize captured file URL documents
* fix(desktop): render captured file URLs at launch
* fix(desktop): isolate launcher URL encoding
* test(desktop): move launch execution to boundary suite
* style: fix desktop launch test formatting
* style: format native command path schema
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
Co-authored-by: morluto <[email protected]>
* feat(desktop): capture, draft, export and witness a task inside a build computer
- start_build_capture runs REA's capture inside the computer through a
small controller over computer-exec, after a user-space apt-get update;
mark_build_checkpoint returns once the capture confirms the checkpoint;
stop_build_capture returns the draft between seed and end.
- export_build_environment exports with the requester's task inside the
computer and fetches the bundle through data/get in bounded batches.
- check_build_task grades a computer's data/get with REA's evaluator and,
through seed, after_edit, near_miss and after_reset stages, records a
witness with the action log and the screen recording; act_on_build_computer
gains reset.
- Capture progress lines carry window, checkpoint and refusal events; apps
start in the home; a pack script builds the runtime archive; long tools
send progress notifications.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): make build capture and witness steps retryable, and close the near-miss reset hole
- A capture whose start answer is lost is stopped at once and kept
unreleased until the stop is confirmed; a lost checkpoint answer is
recovered from the capture's own log, and draft and export use the
checkpoints its Evidence records; stop can be repeated to resume
reading its result.
- A witness can check its seed again after a failed read, and keeps its
stages in a saving state until the record is written, so a failed save
can be retried without reading the app again.
- after_edit and near_miss count only click, drag, scroll, type and key,
so a reset no longer stands in for the near-miss edit.
- A .deb build keeps its package file for capture and passes its URL to
export; bundle downloads write each checked file before fetching the
next; an apt-get update failure reports its exit code and log tail;
reset sends progress while it waits.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): keep the Trash out of seeds, bundle folders unwritten and first frames drawn
From the blind Inkscape rebuild:
- Paths in ~/.local/share/Trash get a desktop_trash reason and the
shared_desktop role, so a file trashed before seed is neither owned nor
seeded; the export names it in a limitation.
- The keep helper runs with PYTHONDONTWRITEBYTECODE=1, so starting a
computer from a bundle folder no longer writes __pycache__ into it.
- start_build_computer waits, bounded, until the first frame is not one
colour before returning it.
From review: a .deb that apt also offers exports from apt, since export
passes --package-url only when the capture says the package must come
from its file.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): bound the first-frame wait's retries to the time left, with progress throughout
Each retry of the drawn-first-frame wait runs under an abort that fires at
the 10 s deadline, and the latest frame is returned when time runs out.
connect takes the first frame itself while its progress heartbeat still
runs, reporting "waiting for the desktop to draw". The wait moves to
DrawnScreenshot.ts.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): serialize each build computer's calls, keep one recording id, and separate unreadable checks from failures
- One queue per computer: actions, capture steps and witness checks run one
at a time, so two overlapping after_edit checks can no longer both be filed
and let the witness hold without a near miss. Close cancels the running and
queued calls; different computers still run concurrently.
- The witness keeps one recording request id per computer, so a seed retried
after a lost reply recovers the recording it started.
- A check that could not read its file is undecided, not a failure, so a
temporary read error can't satisfy seed, near_miss or after_reset; such a
stage is refused and can be checked again.
- The export's file listing is written to a file and read back in bounded
batches, so a large bundle no longer overruns exec's 1 MiB output.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): treat failures on cut views as undecided, require a decided seed failure at export, and keep a close's cancellation
- A presence check that misses its line, row or pointed value in a view cut
short is undecided rather than failed, since the data may sit in the part
left out; a witness stage can no longer be proved by such a failure.
- Export counts only decided failures on the seed, and refuses a task whose
every seed failure is undecided.
- A step whose turn was aborted by a close or the caller returns cancelled
even when its work finished, so start_build_capture no longer reports
success for a computer being removed.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): keep a step's saved result when a cancellation arrives during its commit
Reporting cancelled for any step whose turn was aborted after its work
replaced a completed export or witness save with a bare cancellation, losing
what was saved and leaving the witness unreadable. Steps now return what they
completed; only start_build_capture, which commits nothing, checks its turn
after the post-launch screenshot and reports cancelled. Asking again for a
saved witness's final check returns its record.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): refuse a saved witness's repeated check for another folder
Asking again for the check that ended a witness returned its saved record
whatever directory came with it, so a caller naming another folder got
success though nothing was saved there. A different folder is now refused,
as an unfinished seed's retry already is.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* test(desktop): assert the facts of cut-view and undecided outcomes, not their sentences
The new cut-view, missing-value, undecided-refusal and witness tests froze
whole descriptions, so a harmless rewording broke them. They now check each
outcome's id, passed and undecided exactly and the facts its message must
carry (file, pointer, line, row, stage) separately.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* feat(desktop): read XML with a plain DOCTYPE, and check an element's whole text
- Both readers accept a DOCTYPE with only a name and an optional PUBLIC
or SYSTEM identifier, record it as the view's doctype, and never fetch
the DTD or expand entities beyond XML's five; an internal subset, an
external entity and a parameter entity stay refused alike.
- text_equals and text_contains compare the concatenated text of the
element (or text node or attribute) a pointer reaches, as XPath
string() does, with optional normalize_whitespace; both evaluators
agree, and a text check on a truncated element is undecided.
- Whitespace-only text between elements is dropped only when the element
holds no other text, so text split into runs keeps its spaces.
- The draft suggests text_equals for an element whose text changed.
So an AbiWord .abw reads as XML, and its paragraph's text is checked
whole however AbiWord splits it into <c> runs, as Writer's spans are.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): draft whole-text checks on the paragraph, read only text as text, and skip asctime stamps
- A changed value is read at both ends, so text that became a run still
marks its paragraph, and the drafted text_equals moves up out of mixed
content to the nearest ancestor named by an attribute, such as
AbiWord's p by its xid.
- Text checks accept only a text node or an attribute value; element
names and doctype fields fail, in both evaluators.
- isTimestamp also recognises asctime dates (Www Mmm dd hh:mm:ss yyyy).
- XML files are drafted with the keyed diff the archive entries use, so
no suggestion reaches an element by position; the shared logic moves to
appTaskDraftNamed.ts.
- Text-check tests assert facts, not whole sentences.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): keep checks on a changed attribute inside a paragraph whose text check replaces its runs'
A drafted whole-text check on a paragraph hid every change inside it, so a
link whose label and href both changed drafted only the paragraph's text and
the new destination went unchecked. The text check now covers text and runs
added or removed whole; a changed name or attribute of an element both
versions hold keeps its own check.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): read an attribute called name or attributes as an attribute when the draft keeps its check
The owner of `.../attributes/name` was taken as `.../attributes`, so a
changed `name` attribute inside a paragraph with a text check was dropped
again. The owner is now read by segment, attributes first.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): draft text checks from text in runs added whole, and name a text element by its own id before its container
Text typed into a run the diff lists whole was never read, so the draft kept
only a presence check on the run, which wrong text also passed. Such text now
marks the nearest element both ends hold, or, when none can take the check,
the added element's own text. The climb also names an element by an
identifier of its own (id, xml:id, xid) before a larger container, so a
headline under a long layer keeps a whole-text check that survives re-split
runs.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* test(desktop): expect the whole-text check where #8988's pairing test meets text checks
On this branch a changed element's text is drafted as `text_equals` on the
element, so the identity pairing test now expects that check, under the
same `[@role=body]` key.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* feat(skills): build a computer-app environment from a request
The product skill gains references/desktop-build.md, a procedure that
takes Claude from a request such as "an Inkscape environment where the
agent changes a poster's headline" to a witnessed bundle and a report,
through start_build_computer, act_on_build_computer, the build capture
tools, export_build_environment and check_build_task: questions asked
once upfront, pauses only when blocked, a seed made in the app, content
checks by selector, a witness with a near miss and a reset in a fresh
computer, and solver runs only within a budget. setup installs the new
reference; skillVersion 61.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(skills): fold the blind Inkscape rebuild's friction into the build skill
key takes an array of one chord's keys; check for a text cursor before
typing in apps with single-key shortcuts; in Save As, click Documents and
type only the file name; anything saved before seed, the Trash included,
is part of the seed; dismiss first-run dialogs before seed; a bundle's app
starts with the seed's files but need not open the document; Inkscape's
window class is Inkscape.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(skills): fold the held-out AbiWord build's friction into the build skill
Name the act fields and link the guide; a region screenshot becomes the
latest frame; a drawn caret is not focus; confirm dialogs with Return;
check the dialog's folder when the app shows only a name; an XML file the
draft shows as lines was read as text, so use short line checks; and keep
text checks to the value the edit types, since some apps save a reloaded
document differently. Later witness stages take no directory.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(skills): state the export's seed rule, the Trash exception and draft refusals exactly
The export refuses a task unless at least one check fails on the seed and
every check passes at the end; a file moved to the Trash is left out of
the seed, so trash a stray file rather than starting over; a null draft
names its cause in draft_refusal; and a line check on a format read as
text only approximates a value, so bound it and call it approximate.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(skills): teach the build skill to use DOCTYPE and whole-text checks
The skill now checks text an app splits into runs whole with text_equals or
text_contains, finds an AbiWord paragraph by its xid, and drops the Writer
revision-id workaround.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(skills): point text checks at the paragraph, and say what the scrub replaces
From the AbiWord rebuild: point a text check at the paragraph or another
element the edit keeps, never at a run a reopened document splits; how to
name an unchanged element; the export replaces the host name, user@host
and home path while a bare user name stays; AbiWord's entry and class.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): keep every text check on a cut element undecided, in both evaluators
The XML reader drops text deeper than its depth limit but keeps later
siblings, and an element's whole text joins both sides, so a text_contains
on a cut element could match text the file never holds ("at 3" + deep
"WRONG" + ":30 PM" read as "at 3:30 PM"). Any text check on an element of a
cut view is now undecided again, as #9050 had it, in REA's evaluator and the
exported verifier; a text node or attribute value stays decided. The text
check tests assert facts rather than sentences.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): keep a reset applied when the screenshot asked after it fails
Once data/reset and the ready wait succeed, act_on_build_computer's reset
is applied in the action log and returns success; a screenshot asked of it
that is then refused or throws is named in the result's screenshot_error,
with no frame, so the next after_reset stage is not refused. Cancellation
still propagates.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): keep checks for elements without text beside a text edit, and normalize DOCTYPE system id line endings
A review found two gaps.
- A paragraph's whole-text check covered every element added or removed
inside it, including ones holding no text: an image frame, a line break.
A solver who typed the text but never inserted the image passed. Such an
element now keeps its own presence or absence check.
- The Python reader kept a DOCTYPE system literal's CRLF where TypeScript
normalizes it to LF. Both now report LF.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): wait out long build commands, fail a reset that restores nothing, and tie the witness to its bundle
A review found five gaps.
- Commands in the computer answer only when they end, but fetch stops
waiting for headers after 300 s. So a slow export, draft or start always
failed. A command with its own deadline now waits on node:http, bounded
by that deadline.
- after_reset held whenever some check failed, so a reset that changed
nothing passed on the near-miss state. Each check must now come out as it
did at the seed.
- A failed or partial apt-get update left lists that looked complete, so a
second start skipped the update. The update is now recorded only when it
succeeds, and rerun otherwise.
- A witness on a bundle computer graded whatever task it was given. Its
seed stage now refuses a task whose steps or checks differ from the
bundle's.
- A task too large for one command argument (96 KiB) is refused before the
export runs, instead of failing with E2BIG in the computer.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): advertise a checkpoint-name pattern that compiles in every JS regex mode
main's advertised-pattern test compiles every input schema pattern in v
mode, which refuses an unescaped hyphen in a character class. The
checkpoint name's hyphen is now escaped, and it means the same in u mode.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* test(desktop): import textSyntaxFormat once in the state format tests
main's mixed-case JSON suffix fix and this branch both added the import,
and the merge kept both.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(desktop): keep a completed reset, an unread seed, and a failed apt update
A cancel after data/reset no longer drops the restore from the action log,
so a near miss cannot treat the restored seed as an edit. An unreadable
seed check that later passes leaves after_reset undecided instead of
failing the witness. apt-get update is kept only when Error-Mode reports
success and a package list was fetched. The controller reply schema lives
with the controller, and capture progress lines are read by their domain
parser.
* fix(desktop): keep a whitespace-only run under the paragraph text check
A run whose only text is whitespace is still text the paragraph check
reads. It no longer gets its own presence check, which a later save of
the same words without that run would fail. Images and breaks, whose
text is empty, still keep their own checks.
* fix(desktop): keep an unread reset and a cut text check undecided
after_reset stays undecided when any check was not read, even if another
check still fails and the unread result's boolean matches the seed. A
text check for a child past a truncated view is undecided in both
evaluators. A reset whose reply is lost blocks a near miss until a later
input or a reset whose reply arrives.
* fix(desktop): retry a witness recording and a published record's cleanup
A recording that fails to end or download is not written into the witness
folder, and a manifest already received is fetched again without ending
the recording twice. A folder that is published while its handles fail to
close keeps that record and retries the same cleanup instead of writing
the folder again.
* fix(desktop): keep a published witness error and tag an app data refusal
A second failure to close a published witness returns the original error,
so its record and cleanup lease stay bound. A reset the app itself
refuses is an AppDataRefusal; only a reply that is not that refusal stays
unsettled. A missing child of a cut view is undecided with the same
description in both evaluators, and the shared cases cover it.
* fix(desktop): align a cut view's missing text with the Python check
A missing child of a truncated view is undecided for the same sentence in both evaluators, including the pointer the view does not contain.
* fix(desktop): keep one published witness cleanup across retries
A published record and its bound cleanup error stay together. A later close failure returns that error, and the unused path that built a fresh adapter error is gone.
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: morluto <[email protected]>
Co-authored-by: morluto <[email protected]>
2026-11-08 22:19:28 +08:00
3797 changed files with 360654 additions and 44057 deletions
description:Reverse engineer native, managed, Electron/JavaScript, packaged, firmware, and browser targets with REA, and build agent environments (AgentEnv bundles) of web applications from a live instance or a capture. Use shipped-artifact or requested runtime evidence to explain features, compare versions, decompile code, or guide a reconstruction. Skip REA for ordinary source-repository architecture analysis.
description:Reverse engineer native, managed, Electron/JavaScript, packaged, firmware, and browser targets with REA, and build agent environments (AgentEnv bundles) of web applications from a live instance or a capture, and of Linux desktop apps with a task from a request. Use shipped-artifact or requested runtime evidence to explain features, compare versions, decompile code, or guide a reconstruction. Skip REA for ordinary source-repository architecture analysis.
metadata:
version:"60"
version:"62"
---
# REA
@@ -17,9 +17,9 @@ readiness or provider commands.
Installing this skill supplies instructions; it does not register the REA MCP
server, install analysis engines, or add tools to an already-running agent.
The exception is a Claude Code or Codex plugin install: the plugin bundles
this skill and registers its own `rea` server, outside the state that
`rea setup` and `rea doctor` manage.
The exception is a Claude Code, Codex, or Cursor Marketplace plugin install:
the plugin bundles this skill and registers its own `rea` server, outside the
state that `rea setup` and `rea doctor` manage.
If REA tools are available and their registration is not known to be stale,
proceed directly to the target. Do not run diagnostics or setup before every
@@ -97,6 +97,12 @@ explicit path or endpoint and do not need it.
- Bun `--compile` executable: `open_binary`, then `inspect_artifact`; the Bun
module graph is the application layer. Extract stored modules and analyze the
JavaScript. See [Bun standalone executables](https://github.com/morluto/rea/blob/main/docs/bun-standalone.md).
- Deno `deno compile` executable: `open_binary`, then `inspect_artifact`; the
embedded virtual filesystem is the application layer. Extract stored files and
analyze compatible JavaScript. Environment-file values and CA certificate bytes
are retained in local Evidence with their counts and byte lengths. The npm
snapshot length is reported and not decoded, and the executable is not run. See
Inspect saved SAZ with `inspect_web_network_capture` and `format: "saz"`;
@@ -158,6 +168,7 @@ explicit path or endpoint and do not need it.
Facts retain all-recorded-events-before-selection scope. Other qlog layouts
and PCAP correlation are unsupported.
- User-owned Electron runtime already open: `list_electron_targets`. Before an electron-main Inspector attempt, check packaged fuses with `inspect_electron_fuses`; disabled `EnableNodeCliInspectArguments` means the app ignores `--inspect`.
- Shipped application exposure for a local `.app`, install directory, ASAR, or executable: `audit_release_exposure` / `rea audit-release-exposure`. Each check has a stable id, a status, and fixed remediation. Unknown and unassessed checks stay in the report. This does not establish that an application is secure. Credential-marker hits report the rule id and location only. See [release exposure](https://github.com/morluto/rea/blob/main/docs/release-exposure.md).
Script-Fu's prompt `ts> ` starts a line, so print a newline before each
marked line, as `\nREA` does. A text layer's text is
`(car (gimp-text-layer-get-text layer))`.
- Blender `.blend`, objects with their type and location, as `json`:
```json
{
"id": "objects",
"file": "Documents/scene.blend",
"argv": [
"blender",
"-b",
"--factory-startup",
"{file}",
"--python-expr",
"import bpy, json; print('REA ' + json.dumps({o.name: {'type': o.type, 'location': [round(v, 4) for v in o.location]} for o in bpy.data.objects}, sort_keys=True))"
See [desktop installations](https://github.com/morluto/rea/blob/main/docs/desktop-applications.md).
## Release exposure
Use `audit_release_exposure` (`rea audit-release-exposure PATH`) when the question is what static analysis of a shipped `.app`, install directory, ASAR, or executable recovers. Each check has a stable id, a status, the observation behind it, and fixed remediation. Unknown and unassessed checks stay in the report. The report does not establish that the application is secure.
`text.credential_markers` reports a rule id and location only. Matched bytes, previews, lengths, and hashes of matched bytes are omitted.
`native.hardening`, `macho.hardened_runtime`, and `android.manifest` are not assessed here. Their next probes are `inspect_binary_hardening`, `inspect_signature`, and `inspect_android_package`.
See [release exposure](https://github.com/morluto/rea/blob/main/docs/release-exposure.md).
## Go build metadata
Use `inspect_go_binary` for an explicit local ELF, PE or Mach-O (universal
files need `architecture`) when the
question concerns the embedded compiler version, module/dependency versions,
local replacements or build settings. No `open_binary`, Go installation or
local replacements, build settings, or static Go string literals packed without
Use `inspect_python_pyc` for an explicit local CPython 3.7–3.13 `.pyc` when the
@@ -464,8 +549,7 @@ the framing LF still fits. Cutter measures, signs, and writes a fitting
response in bounded chunks. The bridge searches only the newly read bytes
for the framing LF. The client unescapes each socket chunk into one response
string before parsing it, so this ceiling does not keep the paged wire frame
and another concatenated copy. Invalid values are configuration errors and do not fall back to 16777216. Set the variable in the environment that starts REA, restart REA,
and start Hopper or Cutter from that same environment. An out-of-band bridge
and another concatenated copy. Invalid values are configuration errors and do not fall back to 16777216. `bridge.max_request_bytes` can be changed at runtime with `update_analysis_settings`. The launch seed is `REA_BRIDGE_MAX_REQUEST_BYTES`. Start Hopper or Cutter from that same environment so the bridge keeps the ceiling it started with until that bridge starts again. An out-of-band bridge
without the setting stays at 16777216 bytes. The client reports that mismatch
during discovery only after the hello proves a live authenticated bridge, and
does not send a status request or the command. A stale descriptor is ignored.
@@ -836,3 +920,24 @@ Layout labels describe record shapes, not a detected compiler version; unknown
or ambiguous layouts are unsupported. Bytecode is retained, not decoded, and the
description:Reverse engineer native, managed, Electron/JavaScript, packaged, firmware, and browser targets with REA, and build agent environments (AgentEnv bundles) of web applications from a live instance or a capture, and of Linux desktop apps with a task from a request. Use shipped-artifact or requested runtime evidence to explain features, compare versions, decompile code, or guide a reconstruction. Skip REA for ordinary source-repository architecture analysis.
metadata:
version:"62"
---
# REA
Use REA when a claim depends on a shipped binary or package, decompilation,
requested runtime evidence, or comparison with
behavior not established by available source. For ordinary analysis of a
complete source repository, use normal repository tools and do not run REA
readiness or provider commands.
## Connect only when needed
Installing this skill supplies instructions; it does not register the REA MCP
server, install analysis engines, or add tools to an already-running agent.
The exception is a Claude Code, Codex, or Cursor Marketplace plugin install:
the plugin bundles this skill and registers its own `rea` server, outside the
state that `rea setup` and `rea doctor` manage.
If REA tools are available and their registration is not known to be stale,
proceed directly to the target. Do not run diagnostics or setup before every
investigation. Use the connected server's actual tool list and input schemas;
a skill installed from repository main may describe capabilities absent from
an older npm release. Keep complete inline Evidence when that server does not
advertise retained references.
If tools are absent, a call is blocked by the client, or a required provider is
unavailable, read [connection and recovery](references/connection-and-recovery.md).
Diagnose the observed blocker, continue useful supported work, and resume the
original task after repair. Client approval, shell PATH, registration and engine
availability have different remedies; avoid repeating setup for all of them.
If the host times out without a REA result, do not repeat the unchanged call or
assume work stopped. Read the recovery guide's deadline distinction before retrying.
If the MCP connection drops, retry that call once only when it is safe to
repeat or known not to have started. If it may already have changed the
target, or it fails again, tell the user and ask them to restart the MCP
client. If REA could not start its connection, retry once, then tell the user
and ask them to restart the MCP client. Do not retry a startup failure that
returns the same unchanged result. For plugin installs, follow
original record ordinals when useful; inspect producer fields and byte/number
sidecars without fetching recorded URLs or claiming live attribution. HAR/SAZ/qlog
old-generation heap is `REA_CAPTURE_DECODER_OLD_GENERATION_MIB` (default 192 MiB,
floor 8, maximum 16384), set where REA starts. Host memory and `NODE_OPTIONS` do not
raise it. See
[historical captures](https://github.com/morluto/rea/blob/main/docs/guides/web/web-network-captures.md) for the exact upstream
profile and credential exclusions. Qlog support starts with aioquic 1.5.0
JSON 0.3; stream coverage and endpoint loss declarations do not recover bodies
or establish network loss. Prefer detail summary, then kind qlog trace/stream
pages; select one trace, stream ID and direction for contribution/range pages.
Facts retain all-recorded-events-before-selection scope. Other qlog layouts
and PCAP correlation are unsupported.
- User-owned Electron runtime already open: `list_electron_targets`. Before an electron-main Inspector attempt, check packaged fuses with `inspect_electron_fuses`; disabled `EnableNodeCliInspectArguments` means the app ignores `--inspect`.
- Shipped application exposure for a local `.app`, install directory, ASAR, or executable: `audit_release_exposure` / `rea audit-release-exposure`. Each check has a stable id, a status, and fixed remediation. Unknown and unassessed checks stay in the report. This does not establish that an application is secure. Credential-marker hits report the rule id and location only. See [release exposure](https://github.com/morluto/rea/blob/main/docs/release-exposure.md).
parameters or string_literals through `inspect_analysis_view`
with the returned `parent_evidence_id`, using exact filters, batch indices and
selected fields. Parameters accept `method_indices`, avoiding a scan of all
parameter pages. Missing collections in historical Evidence remain unknown.
Versions 24, 27, 29, 31 and 39; no Unity, IL2CPP
binary or disassembler is run. Unrecognized magic (possibly encrypted) and
other versions are unsupported. Optional matching `binary_path` enables static native addresses and type references; query `method_addresses` with indices or addresses/states, preserving every shared-body method and both artifact identities. Without a linked binary native counts remain unknown. Byte-pattern fallback is inferred; no runtime behavior or same-build pairing is proved. See the
Read this guide only for an observed connection, command, approval or provider
blocker. Keep the user's target and question in view. Explain what prevented the
operation and the next useful step; after repair, continue the investigation.
Commands below use an installed `rea`. If none is available, use the package
invocation in `SKILL.md` when acquiring REA is authorized. Follow the connected
server's schemas and the installed CLI's help rather than assuming repository
main and an older release expose the same arguments. Tool-name prefixes belong
to the client: find the canonical REA operation in its actual tool listing and
server association before concluding that it is absent. A shared prefix alone
does not establish which server an approval wildcard covers. See
[client tool names and discovery](https://github.com/morluto/rea/blob/main/docs/getting-started/client-configuration.md#client-tool-names-and-discovery).
| No REA tools in the active session | If REA was installed as a client plugin, follow the plugin section below. Otherwise inspect registration with `rea doctor --client codex --json`. A skill installation alone does not register MCP. |
| Missing, malformed or stale registration | Prepare a scoped setup plan; apply only authorized changes with backups. |
| Aligned registration, but no connected tools | Restart/reconnect the client. Doctor checks registration; it cannot establish the current agent's connection. Inspect the client's launch error if reconnection fails. |
| MCP connection dropped or REA could not start | If the connection drops, retry that call once only when it is safe to repeat or known not to have started. If it may already have changed the target, or the same failure returns, tell the user REA's connection failed and ask them to restart the MCP client. If REA could not start its connection, retry once, then tell the user and ask them to restart the MCP client. Do not retry a startup failure that returns the same unchanged result. Run `rea doctor` when REA could not start or tools stay missing after restart. Doctor cannot restore a live connection by itself. |
| `MCP tool call requires approval, but approval policy is never` | Codex rejected the call before REA executed it. Do not install an engine or repeat setup to solve this client-policy decision. See the approval section below. |
| REA returns `provider_unavailable` or an unavailable tool entry | Read that operation's provider diagnostics or `binary_session.result.tool_availability`. Expand each group's tool names and inherit the shared client requirements unless overridden. Use the operation's availability, reason, and remediation; a complete tool catalog does not imply every operation is available. Request `{"detail": "complete"}` only for provider diagnostics. |
| `rea` is missing in a child shell, or Node is incompatible | Inspect the actual shell PATH and launcher/runtime. A prefix installation may be absent from a login shell's PATH; this does not establish a broken MCP registration. |
If the client cannot launch its shell/sandbox helper or read the installed skill,
inspect its launch diagnostic. For example, Codex can refuse helper aliases when
CODEX_HOME is under `/tmp`; use an authorized profile outside the OS temporary
directory. REA setup cannot repair the client's executor. Preserve the sandbox
instead of silently disabling it to make an analysis command work.
Static JavaScript and ordinary ZIP inventory need neither Hopper nor Ghidra.
HAR inspection is offline and does not require a live browser. Native
decompilation, Android analysis and firmware extraction have their own
bring-your-own prerequisites. Use supported inventory/static evidence when it
answers part of the question, and name the unavailable evidence separately.
Do not substitute another target. Label observations from ordinary shell tools
as such; only actual REA output supplies REA Evidence.
## Distinguish client and server deadlines
A host message such as `timed out awaiting tools/call` with no REA result means
the client's deadline expired before a result arrived. It does not establish
that REA finished, canceled successfully, or released all resources. Do not
repeat the unchanged analysis. Inspect available session/provider state and
retained Evidence only when useful; an idle `binary_session` is not proof that
every target-free operation stopped. Never invent a missing Evidence reference.
Allow the host tool deadline to cover execution, cancellation/cleanup, and
response delivery. In Codex, the setting is
`mcp_servers.<server-id>.tool_timeout_sec` in **seconds**, not
`startup_timeout_sec`. Select a value for the actual operation budget and
Script-Fu's prompt `ts> ` starts a line, so print a newline before each
marked line, as `\nREA` does. A text layer's text is
`(car (gimp-text-layer-get-text layer))`.
- Blender `.blend`, objects with their type and location, as `json`:
```json
{
"id": "objects",
"file": "Documents/scene.blend",
"argv": [
"blender",
"-b",
"--factory-startup",
"{file}",
"--python-expr",
"import bpy, json; print('REA ' + json.dumps({o.name: {'type': o.type, 'location': [round(v, 4) for v in o.location]} for o in bpy.data.objects}, sort_keys=True))"
See [desktop installations](https://github.com/morluto/rea/blob/main/docs/desktop-applications.md).
## Release exposure
Use `audit_release_exposure` (`rea audit-release-exposure PATH`) when the question is what static analysis of a shipped `.app`, install directory, ASAR, or executable recovers. Each check has a stable id, a status, the observation behind it, and fixed remediation. Unknown and unassessed checks stay in the report. The report does not establish that the application is secure.
`text.credential_markers` reports a rule id and location only. Matched bytes, previews, lengths, and hashes of matched bytes are omitted.
`native.hardening`, `macho.hardened_runtime`, and `android.manifest` are not assessed here. Their next probes are `inspect_binary_hardening`, `inspect_signature`, and `inspect_android_package`.
See [release exposure](https://github.com/morluto/rea/blob/main/docs/release-exposure.md).
## Go build metadata
Use `inspect_go_binary` for an explicit local ELF, PE or Mach-O (universal
files need `architecture`) when the
question concerns the embedded compiler version, module/dependency versions,
local replacements, build settings, or static Go string literals packed without
segments and provenance. `source_exhaustive: false` means unreturned symbols
may exist.
To see which functions or Objective-C methods a Mach-O actually calls in one
run, use `observe_native_calls` with explicit breakpoints and a bounded
`duration_ms`/`max_events`. It launches a new process under LLDB, so the target
runs and may change files or use the network. Hardened-runtime targets need the
`get-task-allow` entitlement.
Function breakpoints use exact LLDB runtime names: pass an `inspect_macho` export
or import `runtime_name` (`main`), not its symbol-table `name` (`_main`). REA does
not translate breakpoint names. Set `module` to the image
file name when selecting that image only; otherwise loaded system images also
match. Inspect retained locations and module paths before attributing a call.
Use `binary_session` with no arguments to check the open target, selected
provider, and alignment. Its default summary `result.tool_availability` carries
the complete tool inventory grouped by current status. Expand each group's
`names` and inherit the inventory-level client requirements unless a group
overrides them; availability, reasons, remediation, and modes remain inline.
Provider candidates keep availability and target-support status, code, and
reason. `omitted_fields` names what the summary leaves out; `{"detail":
"complete"}` adds each unavailable candidate's `remediation`, provider
diagnostics, candidate capability sets, and tool surface, `effects`, and
`annotations`. Those arrays name true flags from fixed
vocabularies, so an omitted known flag is false. `tools/list` remains the
canonical source for full tool schemas when target or provider state changes.
Provider capability groups carry provider identity and ordered limitations.
Prepend each set's `shared_limitations` to a group's `limitations` to recover the
operation's full ordered list. Candidate capabilities, returned with complete
detail, describe possible providers. Top-level session capabilities describe the current route, and
`analysis_provider_binding` identifies the selected provider.
## Continue editable Ghidra analysis across processes
For a task spanning agent sessions, check `binary_session` after selecting
Ghidra: the current provider's `capabilities[].shared_limitations` states whether
Program checkpoints are enabled or the session is ephemeral. Successful native
queries distinguish initial analysis from an actually restored packed Program;
do not infer that restoration happened merely because checkpoint mode is enabled.
In ephemeral mode, choose a private absolute directory and set
`REA_GHIDRA_CHECKPOINT_DIR` in the environment starting REA. Restart the MCP server
after changing that launch configuration; no current tool call or runtime
analysis-setting update enables it. Stock ELF/PE/Mach-O imports on Linux/macOS
are supported; see the [checkpoint setup and limitations](https://github.com/morluto/rea/blob/main/docs/getting-started/installation.md#automatically-checkpoint-an-owned-ghidra-program).
With the setting enabled, normal opens restore compatible saved Programs
automatically. Initial completed analysis is saved, and `close_binary` saves
later metadata edits; no separate save/restore tool is needed. Repair a reported
save failure and retry close. Cancellation or process failure can lose edits
after the last successful save; unfinished initial analysis cannot resume.
Keep the same checkpoint configuration for later processes. Evidence/Unknown
history and immutable REA query snapshots have separate persistence workflows.
## Reuse caller-selected Ghidra analysis
When the caller supplies an expanded project, configure `REA_GHIDRA_PROJECT_PATH`
(the `.gpr` marker) and optionally `REA_GHIDRA_PROGRAM_NAME` (one root program),
then restart/reconnect before `open_binary`. CLI uses the same configuration.
REA opens only a fingerprint-bound owned copy with `-process -noanalysis -readOnly`;
original project files are not opened by the engine or saved. Database edits,
import overrides and repair scripts are unavailable in this mode. Treat original
analysis completeness/settings as unknown and prior edits as caller-project state.
The program's recorded import digest must match the target; changed project content
requires reopening for a fresh profile. See the
[setup and preparation guide](https://github.com/morluto/rea/blob/main/docs/getting-started/installation.md#reuse-an-existing-ghidra-project).
## Ghidra startup deadline recovery
`provider_timeout` during the first Ghidra query can cover import, auto-analysis,
bridge startup, and readiness. The default 330,000 ms is configurable through
`REA_GHIDRA_STARTUP_TIMEOUT_MS`; a smaller query still needs the same cold import.
When the error names `ghidra.startup_timeout_ms`, call
`update_analysis_settings` with `action: "set"` and a larger value within the
reported bounds. Resolve retained cleanup before the next startup attempt.
No REA restart is needed; an already accepted startup keeps its original
budget. Follow the error's session-specific restart guidance, and preserve useful Evidence before closing
and reopening the target when remediation requires that lifecycle step.
For a per-function timeout, update `ghidra.decompile_timeout_seconds` and retry
the same function on the existing Program. Explicit `null` removes that
per-function deadline. Inspection is optional when the error supplies the
setting and bounds. Runtime updates disappear when REA exits; environment
changes only seed future REA processes.
The client's tool-call deadline must allow startup plus the requested query;
server settings and progress notifications cannot override it. CLI analysis
avoids the MCP client deadline, but retains the provider deadline. For example:
```sh
REA_GHIDRA_STARTUP_TIMEOUT_MS=1800000 rea analyze /absolute/path/to/app.exe --provider ghidra --json
```
Thirty minutes is an example budget, not a completion guarantee. Keep host CPU
and memory controls in place. Disable specific expensive analyzers only when
their missing coverage is acceptable and retain the resulting limitations.
Exported C and embedded resources remain useful independent evidence, but a
timeout does not establish complete analysis or absence of code. See the
[deadline and client guide](https://github.com/morluto/rea/blob/main/docs/reference/mcp-contracts.md#ghidra-first-query-deadlines-and-recovery).
## Bridge request ceiling
Hopper and Cutter share `REA_BRIDGE_MAX_REQUEST_BYTES`. Unset, both sides use
16777216 bytes. The payload budget excludes the terminating LF. A decimal
integer from 7464 to 536870887 raises or lowers that ceiling. The floor fits
the largest bounded symbol-name command. A tight Cutter status still returns
its session id and up to 64 completion receipts. Its path is included when it
fits, and is unknown when it does not, so an older descriptor path is not
paired with the new generation. The maximum is the encoded payload, one byte under Node's string ceiling so
the framing LF still fits. Cutter measures, signs, and writes a fitting
response in bounded chunks. The bridge searches only the newly read bytes
for the framing LF. The client unescapes each socket chunk into one response
string before parsing it, so this ceiling does not keep the paged wire frame
and another concatenated copy. Invalid values are configuration errors and do not fall back to 16777216. `bridge.max_request_bytes` can be changed at runtime with `update_analysis_settings`. The launch seed is `REA_BRIDGE_MAX_REQUEST_BYTES`. Start Hopper or Cutter from that same environment so the bridge keeps the ceiling it started with until that bridge starts again. An out-of-band bridge
without the setting stays at 16777216 bytes. The client reports that mismatch
during discovery only after the hello proves a live authenticated bridge, and
does not send a status request or the command. A stale descriptor is ignored.
Discovery fails if any such live ceiling differs, even when another live
session matches. A command response discarded for that ceiling stays an
unknown execution with `output_truncated`. A hello
that never sends its authenticated frame does not hold the accept loop.
At most 8 connection threads run; a further accept is closed, and a failure
to start a thread does not stop the listener. `completed_connection` is the
longest prefix of connections that have terminated. Dense scalar replies are
encoded in bounded batches.
The 25000 ms Cutter request deadline and the 256-candidate discovery budget
still have no runtime override.
## Selected native facts and call graphs
For a large `get_call_graph`, or repeated `procedure_address`,
`procedure_callees`, `procedure_callers` and `resolve_containing_procedure`
queries, select `detail: "summary"` when the first 64 recorded observations
suffice. Read further `native-observation` pages from the returned parent Evidence
ID. Graph message IDs index that page's exact-text dictionary; keep level,
source index, failed nodes, and partial/unknown relationship coverage.
Read referenced source context through `native-context` facets `overview`,
`analysis-profile`, `limitations` and `result-limitations`. Reuse inspected
context/profile values only for the same digest; until caveats are read,
interpretation beyond the returned facts remains unknown. Complete Evidence
remains retained and exportable. Small complete replies can cost fewer tokens
than additional context/page calls; count the completed task and discovery.
## Static memory and original file bytes
For Ghidra, `read_bytes` reads initialized analysis memory, including loader
fixups; it does not read a running process. Check `returned_bytes` and `complete`
before decoding a table. An incomplete read is missing coverage, not a zero-filled
tail. Use `list_segments` to inspect the selected block's initialization and address-space
facts, and retain unavailable facts as unknown.
Use `address_to_file_offset` to anchor one observed address to original file
bytes. A mapping for the first address does not establish a contiguous mapping
for an entire table, and loader fixups can make analysis bytes differ from source
bytes. Keep source ranges and analysis-memory observations separate.
A pointer table can refer to writable slots populated by a constructor rather
than directly to immutable resources. An uninitialized block or an observed zero
does not establish the slot's later runtime value. Follow `xrefs` and the relevant
writer's assembly or pseudocode; distinguish recorded assignments from inferred
execution order. Do not replace unreadable bytes with zeros or conclude that a
resource is absent without the necessary initialization evidence.
## DOS MZ with Ghidra
For an admitted DOS MZ target, use `inspect_native_load_image` to check measured
loaded bytes, file mappings, relocations and the entry against the immutable
snapshot. Keep `mismatch` and `unsupported` explicit. Verified import covers the
reported static image, not runtime DOS or PC-98 hardware behavior.
For a large load-image report, select `detail: "summary"` and query its
`parent_evidence_id` with `inspect_analysis_view` kind `native-load-image`.
Choose a collection such as `relocations` or `mappings`, select only needed
fields, and page with offset/limit. Original indices preserve same-address
alternatives. Null collections remain unrecorded or inapplicable; paging does
not upgrade unsupported verification. CLI requires `--evidence_output` for
summary delivery so complete source Evidence remains accessible.
For Ghidra decompilation, `procedure_pseudo_code` also accepts `procedure: "file-offset:0x..."`; CLI `rea decompile <path> --file-offset=0x... --provider ghidra` resolves the source byte offset internally. An unresolved 0x-prefixed numeric procedure selector also falls back to a unique file mapping. Existing symbols and analysis addresses win; multiple mappings fail as ambiguous. Preserve the returned file-offset and address locations and raw selection observations. ELF virtual addresses are distinct from file offsets; never assume an arbitrary offset plus image base is an analysis address.
Complete function body ranges use inclusive ends and can contain gaps. Do not
treat the enclosing span as code.
For packed targets, retain the original and separately derived artifact identities;
decompiling an unpacking stub does not recover the unpacked program.
When reading Objective-C dispatch metadata, distinguish class implementation
methods from protocol requirements: class methods report null requirement
flags, while decoded protocol declarations retain required and optional facts.
Property attributes remain raw name/value tokens; use the property's
`atomicity` field for the decoded `N` marker or the default atomic behavior.
Null class/meta flags, ivar counts, atomicity, or read-only values mean the
selected provider did not establish that fact. Symbol names alone do not prove
that a class is a root class or has no ivars.
## Managed PE/CLI
Start with `inspect_managed_artifact`. REA's canonical managed inspection is
execution-free: do not claim it loaded, reflected, executed, or resolved the
assembly. Keep managed/native boundaries and unavailable reconstruction facts
explicit. A bring-your-own reconstruction oracle is separate from the canonical
parser and must not become an implicit setup dependency.
For identity/classification questions, `inspect_managed_artifact` can explicitly
select `detail: "summary"` on the first MCP call. It retains complete metadata
and returns a linked overview with full classification/evidence, nullable
module/MVID, frameworks, metadata coverage/issues/limitations and returned-row
counts. Default complete requests remain unchanged. Read original rows with
`inspect_analysis_view`, source `normalized_result.parent_evidence_id`, and
for setup, supported layouts, and host restrictions. Preserve binary identity
and distinguish derived metadata from Ghidra database observations. Recovered
types and native pseudocode are not original C# source. Windows Ghidra P0 has
no database mutation authority.
## Packages and extraction
Use `inspect_artifact` for application bundles, archives, ZIP/APK/IPA/MSIX/AppX,
ASAR, or DMG inputs when the artifact graph and findings help answer the
question. It returns the complete artifact graph inline. Cite graph manifest
IDs when using them.
For an IPA or a macOS `.app`, ZIP, or DMG, pass the inventory Evidence to
`project_apple_application_graph`. It reports application roots and nested
bundles by path convention: app extensions, XPC services, frameworks, login
items, system and driver extensions, and plug-ins. It also lists privileged
helpers, launchd plists, symlinks, and each bundle's `info_plist_path` and
executable candidates. Roles are path conventions, not parsed plists; read the
listed plists with `inspect_plist`.
After `open_binary` on a `.app` or Mach-O, use `trace_dylib_resolution` to see
which file each `@rpath`, `@loader_path` and `@executable_path` load reaches for
every executable in the bundle. It also lists missing or weak loads and earlier
`@rpath` candidates that are absent. Narrow large bundles with `roots` or
`architecture`. System paths stay undetermined because the dyld shared cache
provides them.
Use `extract_artifact` when materialized files from a supported format are needed.
It extracts the active target and accepts optional `integrity_policy`; see the [MCP contracts](https://github.com/morluto/rea/blob/main/docs/reference/mcp-contracts.md).
It materializes all regular files into a fresh temporary directory chosen by
REA. Symlinks and encrypted entries are inventory facts, not extractable files.
The result reports the materialized directory; callers do not choose the
destination. No separate permission grant is needed.
For classic iNES cartridges, `open_binary` recognizes the byte signature without
an executable architecture. `inspect_artifact` retains header facts and each
PRG/CHR file unit's offset, length and SHA-256; its nested inventory's
`ines.reconstruction` verifies the actual ordered region bytes against the
whole-file digest. Extraction includes the original header, optional trainer
and uninterpreted trailing bytes. These file units are not mapper/CPU bank
maps. NES 2.0, UNIF, dirty legacy headers and truncated payloads are explicitly
refused. No emulator or 6502 analysis is performed. See the
Native DMG traversal automatically uses a read-only mount on supported macOS
hosts, with an owned temporary mount directory and cleanup. It needs no approval
flag. Unsupported hosts or mount failures remain explicit limitations; do not
claim child inventory when only root identity is available. DMG extraction is
unavailable through `extract_artifact`. To access DMG files on macOS, create a
read-only mount with your own tools, select files from that mount for analysis,
and detach the mount you created when finished. REA's inspection mount is
temporary, not a retained export of the image's contents.
For headerless DOS COM, explicitly open with `format: "dos-com"` (CLI
`--target-format dos-com`). Inspect the load image before trusting function
analysis: the entry is `0x10100`, file offset zero, with imposed real-mode segment
context. PSP/stack/device state remains unmodeled. See [DOS guide](https://github.com/morluto/rea/blob/main/docs/providers/ghidra-dos.md).
Use `inspect_byte_entropy` to triage high- and low-entropy ranges before extraction.
For other headerless images (embedded firmware without ELF/Mach-O/PE/MZ
headers), open with `format: "raw-binary"`, `architecture` (`x86`, `x86_64`,
`arm`, `arm64`, `6502`, or `m68k`), and `entry_point` (`0x` hex). File offset 0 is mapped at
that address. Hopper and IDA refuse this interpretation; Ghidra uses
`BinaryLoader` with a generic language: big-endian for `m68k`, little-endian
otherwise. Big-endian ARM, Thumb-only, and Cortex-M variants are unsupported.
`6502` selects NMOS 6502; the entry and complete file mapping must fit within
`0x0000..0xffff`. 65C02, 65816, cartridge containers, bank switching, and mapper
semantics are unsupported. `m68k` requires an even entry and decodes the 68040
superset of the 68000 family; Amiga/Atari containers and vector-table entry
discovery are unsupported. Windows Ghidra P0 admits raw images only for `6502`. See [raw binary guide](https://github.com/morluto/rea/blob/main/docs/providers/raw-binary.md).
Intel HEX, S-record, UF2 and DfuSe files are containers, not raw images:
`inspect_artifact` lists address regions (UF2 per familyID, DfuSe per element
with its alternate setting), and `extract_artifact` writes each region. Open an
extracted region as `raw-binary` with its start address as `entry_point`. UF2
`integrity_policy`. See [UF2 and DfuSe](https://github.com/morluto/rea/blob/main/docs/uf2-dfuse.md).
### Function annotations in Ghidra
Fresh-import sessions admit annotations; existing-project reuse is read-only.
Use `annotate_native_function` for one function name and/or entry comments, with at least one explicit change. Review its annotation readback and refreshed dossier inline. Changes are atomic; empty comments clear them, omitted fields preserve them. Later MCP calls observe edits. Normal close saves them when owned Program checkpoint mode is enabled; ephemeral mode discards them. CLI `annotate-native-function` follows the same setting when closing after its updated result. Select the [checkpoint workflow](#continue-editable-ghidra-analysis-across-processes) before edits that must survive a later process. Original executable bytes are unchanged; edits invalidate immutable snapshots. Windows P0 does not admit database mutations. For naming only, `set_address_name` / `set_addresses_names` also work with Ghidra: they name function entries or data addresses (primary labels) and return compact per-address success booleans instead of a dossier. Ghidra rejects batch addresses that request the same name or a name another selected address holds; `raw_result.failures` gives each failure's reason.
A provider switch or different target can retire the active Ghidra database and discard
its edits, even if the new provider fails on a later call. Same-target,
same-profile reopen keeps the active database. Retained annotation Evidence
records what happened; it does not restore the edits.
For `compare_functions`, obtain `analyze_function` Evidence before and after the
edit while the MCP session is open; annotation Evidence is not a comparison
input. Export the needed Evidence before close if comparison will happen later.
Authenticated same-artifact coordinates preserve renamed entries as
`matched / same-entry`, with the canonical `entry_address` inline; name and
exact-text changes remain separate dimensions. This requires compatible
architecture, provider-owned mapping and selected-image commitments. Equal
| Which selected native entry is reached, with which integer argument registers? | `observe_native_calls` on the active Mach-O, with exact function/Objective-C breakpoints and a duration/event bound | macOS with LLDB and required debugger access. Launches a new owned process, stops at entries, records and continues; target execution may write files, show UI or use the network. It never attaches to an existing process. Inspect termination and partial coverage after the run. |
| What does caller-authored instrumentation observe? | `list_frida_devices` / `list_frida_processes` when discovery is needed; `start_frida_session`, `load_frida_script`, then status/unload/close | Requires compatible installed Frida bindings and selected device access; the real REA verifier covers Linux. Attach selects an existing PID; spawn selects a program and starts it paused until `resume_frida_session`. Scripts execute in the target and can change memory, files, network activity and behavior. This is not passive inspection. |
| Which debugger state or controlled execution explains the behavior? | `start_gdb_session` and `gdb_console`, or `start_rizin_debug_session` and `rizin_debug_command` | Persistent debugger workflows are Linux-only. GDB starts a debugger; console commands choose/load/attach/run targets. Rizin debug start launches the selected target and requires a durable `inferior_tty`. Both consoles are unrestricted and can change target memory/registers, run shell commands, write files and use the network. |
| How do terminal output, process lifetime and selected files differ between runs? | `capture_process_scenario`, then `compare_process_captures` with compatible recorded inputs | New captures require Linux or macOS and a working native PTY backend; macOS also needs `xcrun`/Swift for ownership inspection. Capture launches an owned command with ordinary user permissions. Observation paths select snapshots, not a filesystem sandbox. Comparing retained captures does not execute targets and is also available on Windows. |
| What does an existing desktop window show, or what changes after a declared UI action? | `observe_native_ui`; use `capture_native_ui_scenario` only for requested actions | macOS 14+ with required Screen Recording/Accessibility access. Select an existing PID/window. Observation does not launch or own the app; scenario actions can change its data or trigger network activity, and leave its state as-is. |
Close each persistent session with its matching tool. `close_frida_session`
unloads scripts and detaches; it does not kill the target. A still-paused spawn
is resumed only after instrumentation cleanup is confirmed. GDB and Rizin close
can detach a live inferior and let it continue only after verifying its durable
output terminal. When disposition or cleanup is unresolved, preserve the owned
session and reported recovery; do not assume that closing the debugger terminated
the target. Process capture and LLDB call observation instead own their launched
processes and report their termination/cleanup outcome. Cancellation is not proof
that cleanup completed.
Unrestricted GDB/Rizin commands and relevant start/close operations retire an
active binary-analysis session in the same MCP server. Close an active Ghidra
session explicitly first; after target effects settle, reopen the artifact before
new static analysis. A Cutter bridge command is likewise unrestricted and may
affect its GUI/target; it is not a passive native observation substitute.
To test a static hypothesis across two builds, retain compatible static
comparison Evidence and `compare_process_captures` Evidence. Supply explicit
finding/dimension mappings to `correlate_static_and_runtime`. It does not ingest
arbitrary Frida, LLDB or debugger traces, auto-match names, or prove causality
from cochange. Preserve those traces as their own Evidence and leave unobserved
behavior unknown. Record truncation, unmatched breakpoints, changed target
identity and cleanup uncertainty as limitations, rather than treating no retained
event as proof that the behavior never occurs.
For same-session MCP comparisons, pass each capture inline or reuse it as
- Build and test desktop tasks from recorded application state, including downloaded Linux apps, reusable build computers and multiple tasks per capture.
- Inspect more shipped artifacts offline: standalone application bundles, embedded firmware, NTFS acquisitions, API contracts and V8 code-cache containers.
- Keep native and runtime investigations grounded in retained Evidence, with paged results, explicit coverage and recoverable resource limits.
### Changes
- Analyze targets and create private runtimes on fixed local ReFS volumes as well as NTFS, preserving persistent ACL, file identity, snapshot and cleanup checks. [#10086](https://github.com/morluto/rea/pull/10086). Thanks @QianNangong.
- Capture, export and witness desktop tasks inside a build computer; keep an agentenv computer available through MCP and prepare a build host from one command. [#8954](https://github.com/morluto/rea/pull/8954), [#9041](https://github.com/morluto/rea/pull/9041), [#9045](https://github.com/morluto/rea/pull/9045), [#9049](https://github.com/morluto/rea/pull/9049), [#9199](https://github.com/morluto/rea/pull/9199). Thanks @earakely-scale.
- Install desktop apps from SHA-256-pinned AppImages or upstream archives, retain selected package dependencies, and isolate explicitly declared build secrets. [#9215](https://github.com/morluto/rea/pull/9215), [#9112](https://github.com/morluto/rea/pull/9112), [#9225](https://github.com/morluto/rea/pull/9225). Thanks @earakely-scale.
- Choose seed/end checkpoints, open seed documents at launch, build several tasks from one capture, and evaluate unreadable app state through headless checks or a screen judge. [#9037](https://github.com/morluto/rea/pull/9037), [#9054](https://github.com/morluto/rea/pull/9054), [#9216](https://github.com/morluto/rea/pull/9216), [#9217](https://github.com/morluto/rea/pull/9217), [issue morluto/rea#9676](https://github.com/morluto/rea/issues/9676). Thanks @earakely-scale.
- Read gzip and XML desktop documents and draft assertions for nested fields, attribute text, child text and member counts. [#9050](https://github.com/morluto/rea/pull/9050), [#9111](https://github.com/morluto/rea/pull/9111), [#9114](https://github.com/morluto/rea/pull/9114), [#9132](https://github.com/morluto/rea/pull/9132), [#9185](https://github.com/morluto/rea/pull/9185). Thanks @earakely-scale.
- Inventory and extract .NET single-file, Bun standalone and Deno compile executables, with caller-selected entries and output destinations. [#3787](https://github.com/morluto/rea/pull/3787), [#4031](https://github.com/morluto/rea/pull/4031), [#6998](https://github.com/morluto/rea/pull/6998), [#8527](https://github.com/morluto/rea/pull/8527), [issue morluto/rea#2352](https://github.com/morluto/rea/issues/2352), [issue morluto/rea#3490](https://github.com/morluto/rea/issues/3490). Thanks @gensokyo-dev, @jsk1004ha, @matbrgz, @mvanhorn, @Rinkia, @Zhong-fan.
- Read UnityFS bundles, Godot 3 PCK archives, WeChat mini-program packages, Zstandard ZIP members and classic iNES regions. [#7699](https://github.com/morluto/rea/pull/7699), [#7781](https://github.com/morluto/rea/pull/7781), [#8148](https://github.com/morluto/rea/pull/8148), [#7780](https://github.com/morluto/rea/pull/7780), [#2816](https://github.com/morluto/rea/pull/2816). Thanks @blankym, @gensokyo-dev, @ShuaixinHuang, @XiaoBinGan, @Zhong-fan.
- Inspect and extract Intel HEX, Motorola S-record, U-Boot uImage/FIT, ESP flash, UF2 and DfuSe firmware regions while retaining their container declarations. [#7136](https://github.com/morluto/rea/pull/7136), [#7234](https://github.com/morluto/rea/pull/7234), [#7771](https://github.com/morluto/rea/pull/7771), [#7498](https://github.com/morluto/rea/pull/7498), [#8228](https://github.com/morluto/rea/pull/8228), [#8230](https://github.com/morluto/rea/pull/8230). Thanks @555aaditya, @Entrpi, @gensokyo-dev, @iamcipherdev.
- Make resource-budget failures actionable across CLI/MCP and read per-call limits from the selected operation snapshot. [#9706](https://github.com/morluto/rea/pull/9706), [#9889](https://github.com/morluto/rea/pull/9889), [issue morluto/rea#9881](https://github.com/morluto/rea/issues/9881).
- Ship self-contained Claude Code/Codex plugin installs and a Cursor Marketplace plugin; add CodeBuddy and LM Studio setup registration. [#7514](https://github.com/morluto/rea/pull/7514), [#9878](https://github.com/morluto/rea/pull/9878), [#2492](https://github.com/morluto/rea/pull/2492), [#8611](https://github.com/morluto/rea/pull/8611), [issue morluto/rea#2490](https://github.com/morluto/rea/issues/2490). Thanks @cainiao1992, @cpruijsen, @GamerDubz, @gensokyo-dev.
- Inspect Windows Shell Link files and qualify recognized Python 3.14/3.15 pyc headers; choose Rizin decoder and map address for one-shot commands. [#7859](https://github.com/morluto/rea/pull/7859), [#8927](https://github.com/morluto/rea/pull/8927), [#8991](https://github.com/morluto/rea/pull/8991), [issue morluto/rea#8206](https://github.com/morluto/rea/issues/8206). Thanks @Entrpi, @mvanhorn, @XiaoBinGan, @xuanhieu.
- Register REA with Cline through its native MCP client settings during setup. [#10128](https://github.com/morluto/rea/pull/10128), [issue morluto/rea#10127](https://github.com/morluto/rea/issues/10127). Thanks @bramrahmadi.
### Fixes
- Keep UnityFS directory and payload reads within the declared bundle, reject undersized declarations, and prevent appended bytes from replacing the inventory or increasing its work budget. [58a0511660](https://github.com/morluto/rea/commit/58a051166047442b947c604c3f7ccde6707ee9df).
- Validate runtime configuration before doctor reports readiness or registration writes files; preserve the source of malformed stored settings and proposed overrides. [#10102](https://github.com/morluto/rea/pull/10102).
- Retry owned inspection descriptors before fresh process queries, preserve physical failures alongside cancellation, and retain cleanup authority until release succeeds. [#10113](https://github.com/morluto/rea/pull/10113), [#10141](https://github.com/morluto/rea/pull/10141), [#10109](https://github.com/morluto/rea/pull/10109).
- Keep distinct linker-map occurrences when source-map declarations overlap, including path spellings that normalize to the same location. [aa017c61ff](https://github.com/morluto/rea/commit/aa017c61ff3803dea3b823664ae255c4059c19d4), [f668c86271](https://github.com/morluto/rea/commit/f668c862717fd1c63c649a04709191bbe86e4479).
- Preserve remote OpenCode registrations and Hermes YAML anchor consumers instead of treating REA-like text as proof of local ownership. [#5152](https://github.com/morluto/rea/pull/5152), [issue morluto/rea#3325](https://github.com/morluto/rea/issues/3325). Thanks @coygeek, @gensokyo-dev, @xu91102.
- Validate PE certificate alignment, name structures and RDN grouping, and distinguish legacy Windows targets before PE header admission. [#9276](https://github.com/morluto/rea/pull/9276), [#9625](https://github.com/morluto/rea/pull/9625), [#10048](https://github.com/morluto/rea/pull/10048), [#10070](https://github.com/morluto/rea/pull/10070), [issue morluto/rea#10003](https://github.com/morluto/rea/issues/10003), [issue morluto/rea#9946](https://github.com/morluto/rea/issues/9946). Thanks @AixSxT, @coygeek, @gensokyo-dev, @N0zoM1z0, @ShuaixinHuang, @wunianze666-netizen.
- Retain UTF-8 ZIP names in desktop exports, resolve nested desktop-entry IDs, and preserve reader inspection failures. [#9431](https://github.com/morluto/rea/pull/9431), [#9563](https://github.com/morluto/rea/pull/9563), [#9773](https://github.com/morluto/rea/pull/9773), [issue morluto/rea#9449](https://github.com/morluto/rea/issues/9449), [issue morluto/rea#9558](https://github.com/morluto/rea/issues/9558), [issue morluto/rea#9766](https://github.com/morluto/rea/issues/9766). Thanks @2SinceJuly, @ShuaixinHuang, @xu91102.
- Report the actual link-time kernel banner, list ELF initializers in images with COPY relocations, and leave cross-code-page shortcut-icon comparisons unknown. [#9690](https://github.com/morluto/rea/pull/9690), [#10084](https://github.com/morluto/rea/pull/10084), [#9691](https://github.com/morluto/rea/pull/9691), [issue morluto/rea#9688](https://github.com/morluto/rea/issues/9688), [issue morluto/rea#9689](https://github.com/morluto/rea/issues/9689). Thanks @Plastic-time, @thunderq10.
- Retain actionable debugger/provider failures: reconnect stale JEB MCP sessions, preserve GDB inferior-TTY checks, and report invalid IDA regex syntax. [#3754](https://github.com/morluto/rea/pull/3754), [#5539](https://github.com/morluto/rea/pull/5539), [#9544](https://github.com/morluto/rea/pull/9544), [issue morluto/rea#5099](https://github.com/morluto/rea/issues/5099). Thanks @akushonkamen, @coygeek, @flitzrrr, @Plastic-time.
- Preserve admitted EVM and pwntools replies and completed SQLite member facts through acquisition/finalization failures. [d77cf61](https://github.com/morluto/rea/commit/d77cf6186631743025a1bafb8c5a27262da6128e), [e813c4c](https://github.com/morluto/rea/commit/e813c4c344d2414d1b8dfe42a421792d91b01dcc), [669e617](https://github.com/morluto/rea/commit/669e617eec5422ebce1ab271af0fc7b51e90dc11).
- Attribute keyed-archive errors to the archive argument, distinguish launcher failures from registry errors, and validate capture limiters before starting work. [#7731](https://github.com/morluto/rea/pull/7731), [#7843](https://github.com/morluto/rea/pull/7843), [#7947](https://github.com/morluto/rea/pull/7947), [issue morluto/rea#7292](https://github.com/morluto/rea/issues/7292), [issue morluto/rea#7597](https://github.com/morluto/rea/issues/7597), [issue morluto/rea#7630](https://github.com/morluto/rea/issues/7630), [issue morluto/rea#7638](https://github.com/morluto/rea/issues/7638). Thanks @coygeek, @gensokyo-dev.
- Read legacy Erlang atom tables correctly, exclude Harmony symlinks from unavailable files, and preserve uncertain JavaScript with-scope member writes. [#7844](https://github.com/morluto/rea/pull/7844), [#7886](https://github.com/morluto/rea/pull/7886), [#7914](https://github.com/morluto/rea/pull/7914), [issue morluto/rea#7418](https://github.com/morluto/rea/issues/7418), [issue morluto/rea#7545](https://github.com/morluto/rea/issues/7545), [issue morluto/rea#7602](https://github.com/morluto/rea/issues/7602). Thanks @coygeek.
- Preserve known artifact metadata without hashes, report missing Ghidra project programs as configuration errors, and retain unbacked file-offset diagnostics. [#7932](https://github.com/morluto/rea/pull/7932), [#8222](https://github.com/morluto/rea/pull/8222), [#8308](https://github.com/morluto/rea/pull/8308), [issue morluto/rea#7341](https://github.com/morluto/rea/issues/7341), [issue morluto/rea#8216](https://github.com/morluto/rea/issues/8216), [issue morluto/rea#8244](https://github.com/morluto/rea/issues/8244). Thanks @coygeek, @kaoru0822-kitauji, @macjayz.
- Scope Hopper recovery to the caller lifetime and preserve Mach-O host-slice, Godot digest, browser-resource and selected-slice distinctions. [#9905](https://github.com/morluto/rea/pull/9905), [#10016](https://github.com/morluto/rea/pull/10016).
- Retain partial OpenAPI Evidence, reject unrepresentable adb activity arguments, and preserve worker, Frida and owned-command failure/cancellation facts through cleanup. [#9908](https://github.com/morluto/rea/pull/9908), [#9918](https://github.com/morluto/rea/pull/9918), [#10055](https://github.com/morluto/rea/pull/10055), [#9963](https://github.com/morluto/rea/pull/9963), [#9964](https://github.com/morluto/rea/pull/9964).
- Decode escaped HAR, qlog and Playwright JSON strings without retaining the fragmented strings that inflated decoder heap usage. [#10139](https://github.com/morluto/rea/pull/10139), [issue morluto/rea#10132](https://github.com/morluto/rea/issues/10132). Thanks @fcumaoglu, @mecrimino.
- Preserve physical web-worker launch failures as the primary error when cancellation overlaps; retain cancellation separately from the exact launcher observation. [#10145](https://github.com/morluto/rea/pull/10145).
- Classify adb wireless-debugging mDNS serials as TCP while preserving USB precedence and unknown device kind. [#10134](https://github.com/morluto/rea/pull/10134). Thanks @rudycelekli.
- Normalize lipo’s i386 CPU spelling before Mach-O slice selection and recognize the `__chk_fail` FORTIFY handler import. [#10136](https://github.com/morluto/rea/pull/10136), [#10036](https://github.com/morluto/rea/pull/10036), [issue morluto/rea#10025](https://github.com/morluto/rea/issues/10025). Thanks @2SinceJuly, @coygeek, @rudycelekli.
- Retain process CLI cleanup owners until delivery, preserve known local retry leases alongside unknown prior cleanup, and align lifecycle boundaries with tagged cancellation facts. [#10147](https://github.com/morluto/rea/pull/10147). Thanks @wunianze666-netizen.
- Count only syntactically valid CSP nonce/hash sources when reporting browser policy. [#10135](https://github.com/morluto/rea/pull/10135). Thanks @rudycelekli.
- Reject ELF tables inside the declared header and unmatched NT_FILE mapping pathnames; keep PE imports within their declared directory bounds. [#10133](https://github.com/morluto/rea/pull/10133), [#10138](https://github.com/morluto/rea/pull/10138), [#10044](https://github.com/morluto/rea/pull/10044). Thanks @rudycelekli, @ShuaixinHuang, @wunianze666-netizen.
- Classify private nested Swift types by their own kind rather than their enclosing declaration. [#10085](https://github.com/morluto/rea/pull/10085), [issue morluto/rea#7501](https://github.com/morluto/rea/issues/7501). Thanks @BeqaKs, @Rinkia.
### ⚠ BREAKING CHANGES
- Settings clients must replace `effect`, `rea_restart_required`, `restart_required` and `restartRequired` with `update_mode` (`runtime`, `restart`, `parameter`) and `pending_restart`. Parameter-supplied limits cannot be changed by the runtime setter; restart-mode values retain their startup value until restart. [#9885](https://github.com/morluto/rea/pull/9885), [be20bdfb26f7](https://github.com/morluto/rea/commit/be20bdfb26f75a573a9cad49d091f5a27f8fc745), [aee1178839dd](https://github.com/morluto/rea/commit/aee1178839dddcc54de42cce6d5d91b7a913034b). Thanks @gensokyo-dev, @N0zoM1z0.
- Process and desktop callers must inspect `details.secondary_failures`/`secondaryFailures`: mixed batches keep physical/provider failures primary and retain typed cancellation with its original cause. Retained descriptors can produce `cleanup_incomplete`; follow the returned recovery instead of treating it as ordinary cancellation. Custom adapters must consume owner-issued inspection/sampling facts and retain their cleanup scopes until release succeeds. [#10141](https://github.com/morluto/rea/pull/10141), [#10113](https://github.com/morluto/rea/pull/10113), [#10109](https://github.com/morluto/rea/pull/10109).
- `rea mcp add --command` now accepts a local stdio launcher only: HTTP(S) executable URLs are rejected before settings writes, and package acquisition is removed. Configure remote transports in each client’s native settings and install/select the local launcher before registration. [#10102](https://github.com/morluto/rea/pull/10102).
- Runtime-instance graph identities now require `identity.capture_evidence_id` in addition to the capture digest and runtime key. Node IDs digest that identity, so regenerate reconciliation Evidence and rebuild node-keyed lookups even when the digest and runtime key are unchanged. [#10063](https://github.com/morluto/rea/pull/10063).
- Retained process symlinks require their stored target bytes; recapture legacy observations that only described a link without retaining its target. [#10082](https://github.com/morluto/rea/pull/10082).
- Function dossiers must record Swift literal inventory and its omissions together. Regenerate one-sided observations through a canonical provider; both-absent unsupported observations remain valid. [#10068](https://github.com/morluto/rea/pull/10068).
- Conformance manifest schema v2 separates exact string markers from fragments and rejects the old ambiguous `strings` field. Regenerate unversioned fixtures with `npm run build:fixtures`. [#9977](https://github.com/morluto/rea/pull/9977).
- Unavailable native/BEAM/Go/Rust view collections return `items.rows: null`, `coverage.status: "unavailable"`, `coverage.total: null` and `coverage.exhausted: false`; recorded empty collections still return `rows: []`. Refresh schemas, stop paging unavailable data and inspect `unknowns`; older saved views retain their recorded facts. [#9893](https://github.com/morluto/rea/pull/9893), [#8291](https://github.com/morluto/rea/pull/8291), [issue morluto/rea#8276](https://github.com/morluto/rea/issues/8276). Thanks @Xinglan233.
- Desktop witnessing requires the current generated verifier: re-export old bundles using their original Evidence/task inputs. Pre-6.9 captures missing required `state.baseline_non_files` are rejected before state inspection, drafting or export; capture the application again to obtain contemporaneous SQLite member facts. [#9898](https://github.com/morluto/rea/pull/9898), [#9470](https://github.com/morluto/rea/pull/9470).
- New browser captures record `save: []` for request actions that save nothing, changing scenario/Evidence digests. Keep historical digests with historical captures and use freshly recorded digests for new ones. [#9896](https://github.com/morluto/rea/pull/9896).
- Selected SQLite rows require `row_offset` and `next_row_offset`; `rows.order` is `rowid`, `primary_key` or `unspecified`. A null next offset means either no continuation or no stated order: an unordered sample can still have `truncated: true`. Inspect both `order` and `truncated` before concluding the sample is complete. [#9331](https://github.com/morluto/rea/pull/9331), [issue morluto/rea#8234](https://github.com/morluto/rea/issues/8234). Thanks @2SinceJuly, @555aaditya.
- Windows entropy Evidence now uses the admitted filesystem path spelling, so fresh analysis of a differently cased caller path can change `evidence_id`. Invalidate or re-key cached associations against the fresh Evidence, retaining old IDs only for their original observations. [#9393](https://github.com/morluto/rea/pull/9393). Thanks @HusnainMaroof.
- `caller_input_paths` values may be ordered arrays of strings as well as strings. Update strict consumers to preserve repeated selections. [#9626](https://github.com/morluto/rea/pull/9626), [#9624](https://github.com/morluto/rea/pull/9624).
- `observe_javascript_runtime` adds required `execution_context_id` fields to scripts and contexts. `context_key` is an opaque capture-local lifetime identity, not a reusable protocol ID, and Evidence identities change. Refresh schemas and recapture when new lifetime associations are needed. [#9471](https://github.com/morluto/rea/pull/9471), [issue morluto/rea#3382](https://github.com/morluto/rea/issues/3382). Thanks @coygeek.
- Refresh cached `inspect_address_context` schemas: unavailable facets may carry typed input or resource-constraint errors while successful sibling facets remain available. [#8444](https://github.com/morluto/rea/pull/8444), [#8424](https://github.com/morluto/rea/pull/8424), [#8419](https://github.com/morluto/rea/pull/8419).
- `analyze_function`/`trace_native_values` require `native_value_flow.limitations` on available and unavailable variants. Ghidra memory effects cover explicit high-p-code `LOAD`/`STORE` operations only; empty effects do not prove no memory access. The `value_flow_effect_scope` profile commitment invalidates older snapshots: retain embedded historical Evidence, reopen without the snapshot and save a fresh one. [#8651](https://github.com/morluto/rea/pull/8651).
- Ghidra byte searches require `skipped_overlays`; loaded overlays no longer count as not loaded, and adjacent initialized blocks are searched with explicit coverage. Reopen targets and save fresh analysis snapshots after the profile/search-policy changes; historical Evidence stays importable. [#8608](https://github.com/morluto/rea/pull/8608), [#8376](https://github.com/morluto/rea/pull/8376), [#8840](https://github.com/morluto/rea/pull/8840), [issue morluto/rea#8374](https://github.com/morluto/rea/issues/8374), [issue morluto/rea#8563](https://github.com/morluto/rea/issues/8563), [issue morluto/rea#8607](https://github.com/morluto/rea/issues/8607).
- `compare_functions` adds `function_match.method: "same-evidence"` when both inputs authenticate to one Evidence ID and entry comparison is unavailable. Accept the new matched variant, retain unknown dimensions and do not infer complete equivalence; one observation cannot have contradictory changed dimensions or hashes. [#8442](https://github.com/morluto/rea/pull/8442), [issue morluto/rea#8367](https://github.com/morluto/rea/issues/8367).
- Kuna `analyze_function` can return `native_value_flow: {available: false, reason, limitations}` instead of null. Check `available` and preserve the provider reason; null means procedure-scoped unrecorded unknown, not confirmed absence. The workflow-profile digest changes: older trace Evidence remains history but must be rerun to satisfy current replay. [#8785](https://github.com/morluto/rea/pull/8785), [issue morluto/rea#8783](https://github.com/morluto/rea/issues/8783).
- The opt-in hot/cold MCP catalog no longer preloads `inspect_analysis_view`. Discover it before use or batch it with its producer; the default full catalog remains available. [#9151](https://github.com/morluto/rea/pull/9151). Thanks @gensokyo-dev.
- `batch_decompile` and retained batch views now use `items[].status: "unavailable"` with `reason: "no_pseudocode"` for null pseudocode, replacing the former `error`/`capability_unavailable` item. Empty or non-text values remain `status: "error"` with `unreadable_output`. Both count toward `failed`; preserve item limitations and accept historical error items. [#8737](https://github.com/morluto/rea/pull/8737), [issue morluto/rea#8736](https://github.com/morluto/rea/issues/8736).
- `import_evidence_bundle`/CLI `evidence-import` now reject snapshot-shaped JSON with `invalid_request` instead of `evidence_integrity_mismatch`. Restore it with `open_binary.snapshot_path` or a compatible CLI `--snapshot`; bundle imports require a canonical bundle from `export_evidence_bundle`. [#8867](https://github.com/morluto/rea/pull/8867).
- REA-launched Chromium keeps its sandbox. If launch fails on root/container hosts, first enable host sandbox support, such as a suitable non-root user or namespace/setuid facilities. The explicit `REA_BROWSER_NO_SANDBOX=true` fallback disables Chromium’s sandbox and does not establish an REA isolation boundary. [#8738](https://github.com/morluto/rea/pull/8738), [issue morluto/rea#8722](https://github.com/morluto/rea/issues/8722). Thanks @chordpli, @hkeysai.
- Fresh objdump Evidence changes provider identity from `gnu.objdump`/`GNU objdump` to `{id: "objdump.command", name: "objdump command", version: null}`; launch errors use the same ID and architecture-list subjects may be null. Update provider-keyed caches/readers and retain the selected executable parameters without assuming GNU semantics; historical Evidence stays readable. [#8824](https://github.com/morluto/rea/pull/8824), [issue morluto/rea#8823](https://github.com/morluto/rea/issues/8823).
- Kuna verification policy is separate from analysis identity. Earlier snapshots and resumable directory exports are incompatible even with the same executable. Preserve old files, reopen without the snapshot and save a fresh one or choose a new export directory; the [Kuna guide](https://github.com/morluto/rea/blob/main/docs/kuna-provider.md) explains extracting/importing embedded Evidence as history. [#8699](https://github.com/morluto/rea/pull/8699).
- `inspect_analysis_view` rejects mismatched native parent predicates with `invalid_request` at `source.predicate_type`: function/inventory/observation views require `rea.analysis`, and matching views require `rea.native-function-matching`. Select the original correctly typed Evidence or rerun its producer; patching an existing predicate invalidates its identity. [#8451](https://github.com/morluto/rea/pull/8451).
- `trace_feature` can succeed when xrefs exist but owner lookup is unavailable. Accept `references[].containing_procedure.status: "unavailable"` with provider/reason fields instead of requiring a `found` member, and inspect `residual_unknowns`; `truncated: false` does not establish resolved ownership. [#8368](https://github.com/morluto/rea/pull/8368).
- `close_binary` retains investigation records; use `reset_investigation` to clear them. Cross-process recovery requires an explicitly acknowledged Evidence-bundle checkpoint. [#8361](https://github.com/morluto/rea/pull/8361), [issue morluto/rea#8347](https://github.com/morluto/rea/issues/8347). Thanks @gensokyo-dev.
- `compare_functions` adds the matched `function_match.method: "same-entry"` variant with required `entry_address` when authenticated artifact, architecture, mapping, slice and entry commitments agree despite a rename. Refresh schemas and keep name/identity changes separate from entry correspondence. [#8263](https://github.com/morluto/rea/pull/8263), [issue morluto/rea#8213](https://github.com/morluto/rea/issues/8213). Thanks @gensokyo-dev.
- `inspect_native_load_image` NativeAOT `metadata_recovery` uses a status-discriminated union: complete reports require `reason: null`; partial/not-applicable reports carry `{code, ...}` instead of prose reasons. Branch on codes and their fields while preserving historical/optional-extension strings. Invalid budgets now produce typed input/resource errors. [#8053](https://github.com/morluto/rea/pull/8053).
- `trace_javascript_semantics` adds `influences` edges and moves direct literal-argument seeds from call-site to expression nodes. Relevant unknown dependencies can change `found` to `partial`: handle that status and inspect unknowns. Reanalyze retained application graphs to obtain the new edges; old graphs stay readable. [#7939](https://github.com/morluto/rea/pull/7939), [issue morluto/rea#7500](https://github.com/morluto/rea/issues/7500). Thanks @Rinkia.
- `inspect_managed_members` changes `signature_resolution` from `type-names-v1` to `type-names-v2` and appends readable names to signature operands, such as `class:4 (Fixture.Program)`. Branch on the resolution version, accept annotated v2 strings and preserve historical v1/null displays. Signature digests are unchanged; `InternalCall` methods remain runtime-provided boundaries. [#5309](https://github.com/morluto/rea/pull/5309), [#9547](https://github.com/morluto/rea/pull/9547), [issue morluto/rea#5149](https://github.com/morluto/rea/issues/5149). Thanks @elricfd, @gensokyo-dev, @ln-bodicherla, @Plastic-time.
- MCP calls default to 1,024 combined array elements/object members; excess input is rejected before dispatch with nonretryable `resource_constraint`, `resource: "transport"` and `reported_limits.max_input_elements`. For larger inline calls, set `REA_MCP_MAX_INPUT_ELEMENTS` to a digit-only positive decimal safe integer (1–9007199254740991) and restart, or select retained/file inputs. Fractional/exponent spellings are invalid; inputs are never truncated. [#7905](https://github.com/morluto/rea/pull/7905), [issue morluto/rea#7882](https://github.com/morluto/rea/issues/7882). Thanks @gensokyo-dev.
- `inspect_macho` text names/paths can be null for non-UTF-8 bytes, with `name_bytes_hex`, `segment_bytes_hex` and `path_bytes_hex` alternatives; unsafe numeric sizes/offsets/alignments can be null with exact companion fields, and delayed-init `options` are numeric. Refresh schemas and preserve these facts; absent byte fields in older Evidence remain unrecorded. [#7952](https://github.com/morluto/rea/pull/7952), [issue morluto/rea#7391](https://github.com/morluto/rea/issues/7391). Thanks @coygeek.
- Environment file-source errors move from the generic `evidence` issue path to `capture` for `environment-build` and `source.path` for environment bundle build/export. Missing/non-file/invalid UTF-8/JSON sources return `invalid_request` with `invalid_value`/`invalid_format` issues and `inspect_input_contract` recovery; repair the selected file. [#7900](https://github.com/morluto/rea/pull/7900), [issue morluto/rea#7523](https://github.com/morluto/rea/issues/7523). Thanks @coygeek.
- Selected unpaired UTF-16 surrogates now make TOON, Markdown, CSV and TSV output fail with `invalid_request` and a nonzero exit even after a successful operation; Markdown also refuses NUL. Use `--format json` or `--format yaml` to preserve these values rather than accepting substituted text. [#7852](https://github.com/morluto/rea/pull/7852), [issue morluto/rea#7592](https://github.com/morluto/rea/issues/7592), [issue morluto/rea#7593](https://github.com/morluto/rea/issues/7593). Thanks @coygeek.
- Reconstruction `evidence_bundle` inputs reject authenticated standalone records as `invalid_request` with field-level issues instead of accepting/misclassifying them as damaged bundles. Bundle-file import/export/comparison reports `invalid_request`/`invalid_input` with `details.reason: "wrong-kind"`. Supply a canonical bundle from `export_evidence_bundle`; tampered data remains an integrity failure. [#7895](https://github.com/morluto/rea/pull/7895), [issue morluto/rea#7562](https://github.com/morluto/rea/issues/7562). Thanks @coygeek.
- Environment coverage can use `method-replay` for non-GET safe reads. Clock-generating write plans without a global clock anchor now return `served: false`, `reason: "unknown-clock"` instead of being served. Extend reason handling and recapture usable start times/completed durations before replaying those writes. [#7834](https://github.com/morluto/rea/pull/7834), [63220edcc3b4](https://github.com/morluto/rea/commit/63220edcc3b47d44e72a7d77518172d81961e67a), [issue morluto/rea#7387](https://github.com/morluto/rea/issues/7387). Thanks @coygeek.
- Ghidra returns `not-in-listing` for in-memory addresses lacking listed instructions or defined data, replacing the former `undecodable` interpretation. Accept the new status and recreate snapshots after the raw-profile change; retain old embedded Evidence as history. [#7823](https://github.com/morluto/rea/pull/7823), [issue morluto/rea#7490](https://github.com/morluto/rea/issues/7490). Thanks @Entrpi.
- `capture_process_scenario`/CLI process capture rejects `columns: 1` before target launch; update both initial terminal widths and every resize to at least 2. Sensitive interaction `data` is empty with `sensitive`/`input_bytes`, and raw echoes are redacted. Recapture both sides before comparing older private-input Evidence. [#7848](https://github.com/morluto/rea/pull/7848), [#7804](https://github.com/morluto/rea/pull/7804), [issue morluto/rea#7582](https://github.com/morluto/rea/issues/7582), [issue morluto/rea#7782](https://github.com/morluto/rea/issues/7782). Thanks @coygeek, @gensokyo-dev.
- Managed setup registrations bind the selected absolute runtime and REA entry script, including package-runner cache paths. Keep that installation available; unknown registrations need confirmed manual repair. Setup no longer returns `prewarm` or emits its progress events. [c77735fa83a6](https://github.com/morluto/rea/commit/c77735fa83a66552ec45eef435ab3bb36026ecfe). Thanks @gensokyo-dev.
- macOS Hopper launch paths/arguments reject double quotes, backslashes and line breaks before handoff. Select literal-safe prepared paths/loader arguments or a compatible provider. [#7732](https://github.com/morluto/rea/pull/7732), [issue morluto/rea#7727](https://github.com/morluto/rea/issues/7727). Thanks @gensokyo-dev.
- New JavaScript application graphs omit the derived package `renderer` property. Use `renderer_declarations` for source fields and package-load edges for the selected candidate; historical Evidence remains readable. [5cf7133f823b](https://github.com/morluto/rea/commit/5cf7133f823bce2a3e42a6e1d0c8418e5ff9908d).
- Replace Cutter `--json=true/false` with `--json-output=true/false`; bare `--json` still selects CLI result formatting. Replace `rea compare-bundles` with `rea compare`; MCP `compare_bundles` is unchanged. [567ea4b1e8bb](https://github.com/morluto/rea/commit/567ea4b1e8bb7dda67563d06f1b4977c2d92995a), [3515b262218f](https://github.com/morluto/rea/commit/3515b262218f2d4ba94ce6a7bda9766747b3d84d).
- JEB engine refusals report `execution_failure` rather than `unsupported_target`; inspect retained upstream diagnostics for recovery. [c4cdb7658b0e](https://github.com/morluto/rea/commit/c4cdb7658b0eb5ef16b30bd8127479bce8c21233).
- ASAR file occurrences may contain `unpacked: false`. Accept an optional boolean; absent historical fields remain unknown. [60834e391c3f](https://github.com/morluto/rea/commit/60834e391c3f81eb2343eba288b735c3bf3bde61).
- Non-loopback MCP HTTP binds require `MCP_ALLOWED_HOSTS` or `rea mcp --http --allowed-hosts`. Bundle names must be unchanged HTTP hostnames within the 63-octet DNS-label limit. [#7172](https://github.com/morluto/rea/pull/7172). Thanks @DeryFerd, @gensokyo-dev.
- `binary_session {}` is compact by default and omits provider availability diagnostics/remediation, target-support diagnostics/capabilities and tool-group surface/effects/annotations. Request `{"detail":"complete"}` for these facts and recovery guidance. [#7127](https://github.com/morluto/rea/pull/7127), [issue morluto/rea#7109](https://github.com/morluto/rea/issues/7109). Thanks @kaoru0822-kitauji.
- Native input errors now include `details.operation` and field-level `details.issues`. Supply a nonempty `trace_native_values.procedure`; CLI `trace-native-values` rejects an empty procedure before opening the target as `VALIDATION_ERROR` with `fieldErrors`, while MCP reports `invalid_request` at `procedure`. [#7889](https://github.com/morluto/rea/pull/7889), [issue morluto/rea#7572](https://github.com/morluto/rea/issues/7572). Thanks @coygeek.
- Readable JavaScript members that normalize to one logical path now stop `analyze_javascript_application` with `artifact_operation_failed`, `details.reason: "path"` and a partial inventory instead of selecting one occurrence. Inspect the inventory and analyze a copy without collisions; all-unavailable colliding sources remain unknown. [#7864](https://github.com/morluto/rea/pull/7864), [issue morluto/rea#7626](https://github.com/morluto/rea/issues/7626). Thanks @coygeek.
- `trace_web_module_imports` with an empty/relative `REA_BROWSER_EXECUTABLE` now reports `capability_unavailable` with `invalid_browser_executable` instead of `missing_browser_executable`, retaining `details.diagnostics.executable`. Set an absolute Chromium path; an unset selection still means missing. [#7903](https://github.com/morluto/rea/pull/7903), [issue morluto/rea#7639](https://github.com/morluto/rea/issues/7639). Thanks @coygeek.
- Unresolvable `import-reference-source` roots, capture scenario files and web-network capture/Protobuf descriptor paths (`ELOOP`/`ENAMETOOLONG`) now report `invalid_request`/`invalid_input` with responsible field/host-code issues rather than generic execution/unreadable-output failures. Repair the symlink chain or choose a shorter path. [#7847](https://github.com/morluto/rea/pull/7847), [#7814](https://github.com/morluto/rea/pull/7814), [issue morluto/rea#7607](https://github.com/morluto/rea/issues/7607), [issue morluto/rea#7636](https://github.com/morluto/rea/issues/7636). Thanks @Abelo9996, @coygeek.
- `close_binary` with `snapshot_path` on a profileless/archive target now returns nonretryable `capability_unavailable` instead of `evidence_integrity_mismatch`, writes no snapshot and leaves the session open. Export needed Evidence, then close again without `snapshot_path`. [#7837](https://github.com/morluto/rea/pull/7837), [issue morluto/rea#7311](https://github.com/morluto/rea/issues/7311). Thanks @coygeek.
- adb local-file refusals occur before device commands: missing/non-file/uninspectable `local_path`/`apk_path` and file-blocked `output_directory` return `invalid_request` with field issues/host codes; output-producing calls formerly used `unreadable_output`. Supply regular source files and a directory path whose parents are directories. [#7885](https://github.com/morluto/rea/pull/7885), [issue morluto/rea#7549](https://github.com/morluto/rea/issues/7549), [issue morluto/rea#7550](https://github.com/morluto/rea/issues/7550). Thanks @coygeek.
- Evidence writes failing with `EFBIG`, `ENOSPC` or `EDQUOT` now report nonretryable `resource_constraint`, `details.reason: "storage-limit"`, path and optional `system_code` instead of `execution_failure`/`io`. Raise file-size allowance or free space/quota before retrying; a failed staged-file cleanup remains `cleanup_incomplete`. [#7887](https://github.com/morluto/rea/pull/7887), [issue morluto/rea#7573](https://github.com/morluto/rea/issues/7573). Thanks @coygeek.
- PE signers add optional nullable `unsigned_attribute_oids`: fresh null means absent, an OID array means present, and omission in old Evidence is unrecorded. Unsigned attributes produce `coverage.status: "partial"` because nested signatures/timestamp contents remain undecoded; accept the new field and limitations. [#7920](https://github.com/morluto/rea/pull/7920), [issue morluto/rea#7767](https://github.com/morluto/rea/issues/7767). Thanks @Rinkia.
- Valid ASN.1 exceeding certificate nesting/OID allowances now returns nonretryable `resource_constraint` rather than `invalid_request`/`invalid_input`, with `max_asn1_depth`/`max_oid_bytes`. These format/resource bounds have no runtime override; retain the diagnostics and use an independently suitable parser when necessary. [#9932](https://github.com/morluto/rea/pull/9932).
- `trace_call_path`/`build_call_path` require discovery and observed-result fields in `search_scope`. An exhausted observed graph can still yield `goal_status: "unknown"` when provider discovery is incomplete. Refresh result schemas and workflow-profile expectations instead of equating graph exhaustion with complete discovery. [#8443](https://github.com/morluto/rea/pull/8443).
- `inspect_native_instruction` adds `containing_instruction` for `not-instruction-boundary` and `containing_data` for `data`, with address/extent/identity/provenance. Accept these facts; omission in old Evidence is unrecorded. The Ghidra profile changes: reopen and save a fresh snapshot. [#8777](https://github.com/morluto/rea/pull/8777).
- `trace_native_values` adds required `page_truncated` and tagged `traversal` coverage. `truncated` combines page omission with traversal truncation; exactly filling a budget alone no longer proves truncation. Inspect page coverage before fetching another page and traversal reason before raising budgets. [#8309](https://github.com/morluto/rea/pull/8309), [issue morluto/rea#8283](https://github.com/morluto/rea/issues/8283). Thanks @kaoru0822-kitauji.
- `batch_decompile.addresses` has a separate hard ceiling of 20,000 entries. Split larger batches into calls of at most that size; increasing the configurable MCP transport allowance cannot remove this operation limit. [#5937](https://github.com/morluto/rea/pull/5937), [issue morluto/rea#5930](https://github.com/morluto/rea/issues/5930). Thanks @dakjdakd.
- `close_binary` can retire the target while retaining cleanup owners: `cleanup_incomplete` may have `recovery.kind: "retry_cleanup"` despite `retryable: false`. Keep the same connection open and call `close_binary` again without `snapshot_path` to retry owned cleanup. [#8329](https://github.com/morluto/rea/pull/8329), [issue morluto/rea#8211](https://github.com/morluto/rea/issues/8211). Thanks @kaoru0822-kitauji.
- Unconfirmed cleanup in `inspect_address_context`, `get_call_graph` or `trace_call_path` now produces tool-level `cleanup_incomplete` rather than a successful partial result or cancellation. Keep admitted partial Evidence and follow the returned same-session cleanup recovery. [#8417](https://github.com/morluto/rea/pull/8417), [#8516](https://github.com/morluto/rea/pull/8516), [issue morluto/rea#8369](https://github.com/morluto/rea/issues/8369), [issue morluto/rea#8416](https://github.com/morluto/rea/issues/8416). Thanks @kaoru0822-kitauji.
- `inspect_ntfs_journal` omits detailed `mftMetadata.record_headers` by default and leaves continuation at the first omitted record. Select `mft_record_limit` from 1 to 32 when those details are needed; inventory counts and explicit coverage remain inline. [1a8a9b749a](https://github.com/morluto/rea/commit/1a8a9b749a86e4b065a6b3ee6b8bad3b2761bac6). Thanks @gensokyo-dev, @N0zoM1z0.
- Desktop state directories now use `application.state_directories[].exists_at_start: null` plus `existence_error` when inspection fails (for example, access denial). Accept the tri-state field and preserve unknown instead of treating it as absence. [#9899](https://github.com/morluto/rea/pull/9899).
Full contribution record: [1848 commits and 1073 merged PRs](https://github.com/morluto/rea/blob/rea-agents-6.9.0/docs/releases/6.9.0.contributions.json).
| PE certificates | Authenticode WIN_CERTIFICATE entries and PKCS#7 signer/X.509 identity fields | Built-in static PE inspection; [PE certificates](docs/pe-certificates.md) |
| PE imports | Import and delay-load DLL names, ordinals, IAT RVAs and pefile import hash | Built-in static PE inspection; [PE imports](docs/pe-imports.md) |
| Windows shortcuts | Shell Link target, arguments, working dir, ExtraData and Tracker droid facts | Built-in static LNK inspection; [Windows shortcuts](docs/guides/native/windows-shortcut.md) |
| ELF notes | PT_NOTE/SHT_NOTE owners, types, GNU build-id and ABI-tag fields | Built-in static ELF inspection; [ELF notes](docs/elf-notes.md) |
| Binary hardening | PIE, NX, RELRO, CFG and related header flags for ELF, PE and Mach-O | Built-in static header inspection; [binary hardening](docs/binary-hardening.md) |
@@ -213,7 +215,7 @@ Additional tools and host support depend on the target:
| EVM bytecode | Dispatch selectors, byte offsets, inferred arguments and mutability | Local raw/hex carrier on Linux x64; [offline EVM guide](docs/guides/evm/evm-bytecode.md) |
| SQLite databases | Tables, columns, indexes, views, triggers and selected ordinary-table rows | Local database/WAL snapshot; Node.js 24.x >=24.15 or 26+ with native SQLite limits |
| Recorded Linux crashes | Raw notes, every recorded thread's registers/signals and optional mapping candidates | Caller-supplied pwntools on Linux x64; optional GDB/pwndbg; [recorded crashes](docs/guides/native/recorded-crashes.md) |
| JavaScript / Electron | Modules, imports, source maps, routes, IPC and native add-on relationships | Node.js and npm; Linux: procps-compatible `ps`; [application analysis](docs/guides/javascript/javascript-application-workflows.md) |
| Websites | Page structure, scripts, network observations and requested screenshots | A Chrome-family browser; [browser analysis](docs/guides/web/browser-observation.md) |
| Saved network captures | Requests, responses, exposed payloads and source locations | HAR or Fiddler Everywhere SAZ; mitmdump on Linux for native mitmproxy; [capture guide](docs/guides/web/web-network-captures.md) |
@@ -230,6 +232,7 @@ Additional tools and host support depend on the target:
| Packages and resources | File inventories, digests, plists, Apple bundle anatomy and extracted resources | [Artifact and JavaScript guide](docs/guides/javascript/javascript-artifact-reconstruction.md), [Apple applications](docs/guides/apple/apple-application-analysis.md) |
| Process behavior | Terminal output, interactions, exit and filesystem observations, and run comparisons | Linux/macOS with a native PTY; [process capture](docs/guides/runtime/process-capture.md) |
[Watch the demo on X](https://x.com/reatools/status/2112574822584918075?s=20)
### Desktop app environments
REA can also build an environment of a Linux desktop app, which an agent uses through screenshots, the mouse and the keyboard. From a one-line request, such as "Build me a Gnote environment where the agent adds olive oil to the shopping list note", your agent installs the app on a local build computer, writes the task, exports the environment and checks the task in a fresh computer. The machine that builds it needs Linux on amd64 or arm64, Docker and the agentenv framework version the guide pins.
[Set up desktop app environments](docs/desktop-build-computer.md)
Security fixes are provided for the latest published version of REA. Upgrade to the latest release before reporting a problem that may already have been resolved.
## Security boundary
REA is a local reverse-engineering and automation tool. CLI and MCP use the
same application workflows and run with the host process's permissions.
REA is not a general-purpose sandbox or a multi-tenant authorization service.
### Threat model
- **Trusted authority:** the operating-system account running REA, the caller
or agent host that selects operations, and configured analysis providers,
their executables and extensions, and service endpoints. An MCP client can
invoke the operations exposed to it; its host must enforce the user's tool
is fingerprinted and copied into an owned runtime for read-only processing;
Ghidra does not open or save the original database through that workflow.
- [IDA analysis](docs/providers/ida-provider.md) is read-only. An attached GUI
database is never saved or closed by REA; owned headless sessions have a
separate lifecycle.
- Cleanup must stay within resources REA owns. Failed or unverified cleanup
can retain a workspace or session for retry; closing a session
does not imply that every investigation artifact has been erased.
`rea setup` shows planned configuration and installation changes and requires
approval before applying them. Ghidra, Java and IDA are user-managed. Approved
Linux Hopper installation authenticates the package's detached PGP signature
against the pinned publisher identity and checks the signed launcher against
the selected release's digest. The release feed's display version is separate
from that authenticated identity. See [installation and publisher verification](docs/getting-started/installation.md#hopper).
### Limits and caller responsibilities
- **Host isolation:** private runtimes and capability tokens do not protect
against malicious software already running as the same operating-system
user, or a compromised trusted client or provider. Use an appropriately
isolated account, VM or restricted environment when handling hostile targets.
- **Resource bounds:** timeouts, cancellation, streaming and size limits are
operation-specific. They are not a universal CPU, memory, disk or wall-time
limit on every target and third-party tool. Enforce hard host resource limits
externally when needed. Failures of a documented boundary or cancellation
control still warrant investigation.
- **Data and network exposure:** Evidence, caches, checkpoints, exports and
captures can retain private target data, paths, URLs and memory contents.
Local execution does not make these outputs safe to publish. Requested
network operations, agent hosts and provider features such as
[IDA Free's cloud decompiler](docs/providers/ida-provider.md) can communicate
outside the host. Review their data handling and secure any exposed endpoint.
- **Interpretation:** analysis, reconstruction and generated environments can
be incomplete or incorrect. Identity checks, signatures and successful
analysis do not establish that a target, provider or generated program is
safe to execute. Review the Evidence and proposed actions before relying on
them.
### Vulnerability scope
Please report suspected violations of these boundaries privately. Examples
include unintended code execution during data-only inspection, escaping a
documented extraction/output boundary, bypassing bridge authentication or
target/session identity checks, unauthorized effects on an unrelated resource,
and disclosure of transport credentials or explicitly marked sensitive values.
An attacker-triggered bypass of a documented resource or cancellation control
can also be security-relevant without code execution.
Incorrect analysis, unsupported formats, crashes, hangs and high resource use
without a security-boundary impact are generally ordinary bug reports. Effects
explicitly requested through an execution or interaction workflow are not by
themselves a vulnerability. A dependency advisory needs its actual REA exposure
assessed; a complete exploit or diagnosis is not required to report a concern.
If you are unsure whether a boundary is affected, use the private channel below.
## Reporting a vulnerability
Do not disclose vulnerabilities, proof-of-concept exploits, credentials,
@@ -21,30 +132,3 @@ unrelated local paths from logs or examples.
The maintainer will acknowledge the report, assess its scope, and coordinate
remediation and disclosure timing with the reporter. Please allow time for
provider-dependent behavior to be reproduced safely.
## Security boundary
REA authenticates its Hopper and Ghidra bridges with per-session random
capability tokens. POSIX uses current-user Unix sockets; Windows Ghidra uses
authenticated loopback TCP with a private NTFS runtime whose DACL admits only
the current user and SYSTEM. See [Windows Ghidra P0](docs/providers/windows-ghidra-p0.md#private-runtime-transport-and-lifecycle).
Tokens are transferred through private session descriptors rather than process
arguments or environment variables.
This is not a sandbox and does not protect against malicious processes already
running as the same operating-system user. Opening an untrusted binary
delegates parsing and analysis to the selected local provider with that user's
permissions.
Ghidra sessions use a packaged Java `HeadlessScript`, an isolated temporary
project, and private home, cache, and runtime directories. REA imports only the
requested target, bounds startup and protocol messages, requests bounded CPU
and heap settings, and deletes the temporary project when the session closes.
These controls limit accidental persistence and resource use; they do not make
Ghidra a sandbox. REA does not open or modify user-owned Ghidra projects.
`GHIDRA_INSTALL_DIR` and optional `JAVA_HOME` identify user-managed
installations. Setup can add those values to detected client configuration only
after showing its plan and receiving approval. REA does not download, install,
upgrade, or modify Ghidra or Java.
On Linux, REA accepts release metadata only from Hopper's HTTPS endpoint, restricts package URLs to Hopper's public download origin, bounds the package size, and compares the downloaded bytes with Hopper's published checksum before requesting installation. The published SHA-1 value is a corruption check, not a modern package signature; HTTPS origin validation remains part of the trust boundary. Dependency installation is delegated without shell evaluation to `apt-get`, `dnf`, or `pacman`, directly as root or through `pkexec`. REA never invokes `sudo`.
@@ -1961,19 +1996,42 @@ public final class ReaGhidraBridge extends HeadlessScript {
limitations.add(
"Def-use links do not cross function boundaries and may omit memory aliasing, call side effects, or unresolved indirect flow."
);
limitations.add(
"Memory read/write effects cover high-p-code LOAD and STORE operations only; direct memory address varnodes in other operations are not included. An empty effect list does not establish that no memory is accessed. Recorded instruction references may provide complementary evidence."
);
limitations.add(
"Memory read/write effects include stack and temporary memory operations; they do not identify persistent state or business meaning."
);
limitations.add(
"Operation, input, and def-use limits can truncate large functions; inspect truncation, omitted lower bounds, and known omitted counts before drawing conclusions."
"Operation, input, and def-use limits can truncate large functions; inspect the effective limits, limits_reached and omitted counts before drawing conclusions."
);
limitations.add(
"Known omitted-edge counts cover links omitted from retained operations; omitted operations may contain additional uncounted links."
?"In MCP, use update_analysis_settings with setting "+setting+
", action set and a larger integer value from 1 to "+Integer.MAX_VALUE+
", then repeat the selected function analysis on this Program without restarting REA. "+
"For a one-shot CLI retry, set "+environment+
" to a larger count in the environment that starts the next rea invocation, then repeat the selected analysis; the previous invocation's Program is no longer active."
:"The setting is at its Java integer maximum; select a smaller function for this projection.";
returnsetting+" reached its effective value of "+effective+". "+adjustment+
" These controls do not raise the decompile deadline, bridge transport or MCP client limits.";
return"If the diagnostic reports payload exhaustion, the native payload allowance is already at its supported maximum; no higher value is accepted. Use analyze_function for available listing facts or explicitly select a smaller function for decompilation.";
return"If the diagnostic reports payload exhaustion, in MCP raise ghidra.decompile_max_payload_mib with update_analysis_settings "+
"(integer MiB from 1 to "+MAX_DECOMPILE_PAYLOAD_MIB+
") and repeat the selected operation on this Program without restarting REA. "+
"For a one-shot CLI retry, set REA_GHIDRA_DECOMPILE_MAX_PAYLOAD_MIB to a larger MiB value in the environment that starts the next rea invocation, then repeat the selected operation; the previous invocation's Program is no longer active. "+
"Runtime null or an omitted launch seed restores the installed Ghidra default. This does not raise the decompile deadline, high-pcode projection budgets or MCP client limits.";
raiseCaptureFailure("input-limit","Native parser exceeded its recursion budget at byte offset "+str(start)+".")fromNone
raiseCaptureFailure("unsupported","Native parser reached Python's recursion limit at byte offset "+str(start)+". max_nesting_depth does not raise that upstream limit.")fromNone
raiseCaptureFailure("format","Malformed native record at byte offset "+str(start)+".")fromNone
ifnotisinstance(state,dict):
raiseCaptureFailure("format","Native flow record must be a dictionary at byte offset "+str(start)+".")
records.append({"ordinal":len(records),"location":{"kind":"byte-range","offset":start,"bytes":handle.tell()-start},**project_record(state,request["sensitive_values"]),"limitations":["Fields are original native states without FlowReader migration. Binary fields contain exact producer bytes; reported UTF-8 strings for those fields are derived display views. Unknown flow/version extensions are retained without interpretation."]})
records.append({"ordinal":len(records),"location":{"kind":"byte-range","offset":start,"bytes":handle.tell()-start},**project_record(state,request["sensitive_values"],request["budgets"]["depth"]),"limitations":["Fields are original native states without FlowReader migration. Binary fields contain exact producer bytes; reported UTF-8 strings for those fields are derived display views. Unknown flow/version extensions are retained without interpretation."]})
return{"ok":False,"reason":"unsupported","message":"Native projection reached Python's recursion limit; max_nesting_depth does not raise that upstream limit.","pointer":""}
ifisinstance(error,MemoryError):
return{"ok":False,"reason":"resource-limit","message":"Native decoder exhausted its 768 MiB address-space budget.","pointer":""}
guardAXIsProcessTrusted()else{tryfail("accessibility-denied","Grant Accessibility to the REA host in System Settings > Privacy & Security, then retry")}
guardletitem=(selectedChildas?[AXUIElement])?.firstelse{tryfail("accessibility-unavailable","Selected accessibility child was not returned")}
element=item
}
varowner:pid_t=0
guardAXUIElementGetPid(element,&owner)==.success&&owner==request.pidelse{tryfail("element-owner-mismatch","Selected element belongs to another process")}
letownerStatus=AXUIElementGetPid(element,&owner)
guardownerStatus==.successelse{tryfail("accessibility-unavailable","Cannot identify accessibility element owner: AXError \(ownerStatus.rawValue)")}
guardowner==request.pidelse{tryfail("element-owner-mismatch","Selected element belongs to another process")}
encoded=json.dumps({"ok":False,"reason":"output-limit","message":"Complete artifact result exceeds the 64 MiB reply budget."}).encode("utf-8")
iflen(encoded)>policy["output_bytes"]:
encoded=json.dumps({"ok":False,"reason":"output-limit","message":"Complete artifact result exceeds producer_limits.output_bytes="+str(policy["output_bytes"])+"."}).encode("utf-8")
raiseLayoutFailure("unsupported-target",f"ELF section {index} ({raw_header.sh_type}) is SHF_COMPRESSED; logical metadata and its source coordinates are not decoded by this profile.")
raiseLayoutFailure("format",f"ELF SHN_XINDEX resolves to ordinary index {section_name_index}; the escape requires an index at least SHN_LORESERVE (0xff00).")
consumed_strings=set()
ifsection_name_index!=0:
ifsection_name_index>=section_count:
raiseLayoutFailure("format",f"ELF section-name table index {section_name_index} lies outside the declared section count {section_count}.")
raiseLayoutFailure("unsupported-target",f"ELF section {index} (SHT_STRTAB) is SHF_COMPRESSED; logical metadata and its source coordinates are not decoded by this profile.")
self.assertEqual(raised.exception.args[0],f"ELF section 1 ({section_type}) is SHF_COMPRESSED; logical metadata and its source coordinates are not decoded by this profile.")
`inspect-artifact` inventories a `deno compile` executable. `extract-artifact` writes the embedded virtual files through the existing safe output tree. Neither operation runs the executable or the Deno runtime.
```sh
rea inspect-artifact ./app --json
rea extract-artifact ./app --json
```
In MCP, open the executable with `open_binary`, then call `inspect_artifact`. Use `detail: "summary"` for a large virtual filesystem and `inspect_analysis_view` to page retained Evidence.
The recognized carriers are a Mach-O `__SUI` section named `d3n0l4nd`, an Intel Mach-O `<~sui-data~>` trailer, an ELF `PT_NOTE` or `SHT_NOTE` owned by `SUI` with type `0x53554901` and descriptor name `d3n0l4nd`, and a PE `RT_RCDATA` resource named `D3N0L4ND`. These are structural layouts. The metadata JSON has no Deno version field, so the compiler version stays unknown unless another record establishes it. Other layouts, including older eszip trailers, are unsupported.
Root metadata reports the carrier, entry point, argv, V8 flags, permission options, unstable configuration, application name, self-extracting marker, virtual-filesystem case sensitivity, and the npm snapshot byte length. The npm snapshot is not decoded. Each virtual file is one occurrence with its logical path, size, and SHA-256. Transpiled output, CJS export analysis, and source maps are separate occurrences beside that file. Each has the same `index` as the file and its own `role`. A region normally has the logical path of the file plus a `.deno-*` suffix. When that path belongs to another stored file, the region moves to an indexed path such as `deno-vfs/0.vfs-transpiled`, so read the reported `logical_path` instead of building it from the file name. They are not linked by `parent_occurrence_id`, which follows directories and expanded containers. Symlink targets stay recorded path parts and are never followed.
`Metadata.env_vars_from_env_file` holds values from a compile-time `--env-file`, and `ca_data` holds certificate bytes. Inventory retains the variable names and values, certificate bytes, counts, and byte lengths in local Evidence, so sharing that Evidence shares them. Extraction does not write them as files.
A virtual filesystem that cannot be listed reports `vfs_listing` as `unsupported` and leaves inventory and extraction coverage unknown, because its files are not examined.
Remote modules, redirects, and specifiers contribute counts and logical paths when their stored form is unambiguous. When it is not, they stay present with a byte length and an `unsupported` member listing. A module whose specifier is missing uses an indexed `deno-remote/<index>` path, which still exposes its stored bytes, and modules with resolved specifiers keep their paths. Colons, backslashes, and control characters in a specifier are percent-encoded in the logical path, so `http://localhost:8000/mod.ts` becomes `deno-remote/localhost%3A8000/mod.ts`. Percent escapes in a logical path use uppercase hex, and a specifier that reaches a path already taken uses an indexed path. `source_path` keeps the original specifier. A remote specifier is indexed the same way when a path component exceeds 255 UTF-8 bytes or the logical path exceeds 512 UTF-8 bytes, counting the `.deno-*` suffix of its stored regions. Unsafe, deep, or over-long virtual names use `deno-vfs/<index>` under the same rule and retain the original spelling when it fits the name allowance. A descendant does not present its own name as a complete source path when an ancestor spelling was dropped.
A length that overruns the section, or a closing marker that does not match, is malformed. An opening marker that is absent is unsupported. A confirmed section that cannot be parsed fails closed and does not fall through to a plain native inventory. An Intel Mach-O sentinel inside a confirmed trailer payload is data of that payload, so an Intel Deno executable stored as a virtual file does not replace the app that stores it. Trailers side by side with different bytes fail closed. A file is confirmed only by its carrier name, so an unrelated native file with malformed tables keeps its native inventory. That inventory does not establish that the file holds no Deno section.
See [limits and recovery](design/limits-and-recovery.md) for the named allowances. Synthetic tests cover the carriers and malformed declarations. Run `npm run verify:deno:standalone` for a local Deno compile of the host and, when the compiler can produce them, Linux, Windows, and Intel macOS targets. Deno is a fixture producer only and is not installed by REA. The produced executable is not run.
Layout reference: Deno `cli/lib/standalone/binary.rs` and `cli/lib/standalone/virtual_fs.rs` at 2.9.8, with libsui 0.16.4 section placement.
| `asar.max_header_bytes` | Next archive operation, including full JavaScript application analysis |
| `asar.max_listing_bytes` | Next expanded ASAR listing, independently of packed header bytes |
| `ghidra.startup_timeout_ms`, `hopper.startup_timeout_ms` | Next provider startup attempt |
| `ghidra.decompile_timeout_seconds`| Next operation on the existing Ghidra Program; `null` means unbounded |
| `ghidra.decompile_max_payload_mib` | Next native decompile; `null` preserves the selected provider's payload default |
| `ghidra.value_flow_max_operations`, `ghidra.value_flow_max_inputs_per_operation`, `ghidra.value_flow_max_edges` | Next function value-flow projection on the existing Program |
| `jadx.operation_timeout_ms` | Next JADX operation |
| `regex.matching_deadline_ms`, `regex.worker_heap_request_mib` | Next search / new regex worker |
| `capture.decoder_old_generation_mib` | Next owned capture decoder |
| `javascript.analysis_timeout_ms`, `javascript.max_heap_mb` | Next source analysis / new JavaScript worker |
| `wasm.command_timeout_ms` | Next WABT operation |
| `native.matching_representation_bytes` | Next `match_native_functions` request |
### Precedence and lifetime
@@ -202,6 +209,48 @@ for an unknown or omitted value elsewhere.
A write does not extend active work, relaunch a provider, clear retained
cleanup, or invalidate successful Evidence.
Expanded ASAR listing admission defaults to 64 MiB and has its own launch seed,
| `REA_DEBUGGER_MAX_PENDING_COMMANDS` | `src/domain/analysisResourceLimits.ts` | 16 | 1 | 256 | debugger manager construction | Queue capacity of the manager created at startup. `update_mode: "restart"` and `pending_restart: false`. |
| `REA_DEBUGGER_MAX_SESSIONS` | `src/domain/analysisResourceLimits.ts` | 8 | 1 | 64 | debugger manager construction | Session-pool size of the manager created at startup. `update_mode: "restart"` and `pending_restart: false`. |
| `REA_HOPPER_DEMO_DIALOG_TIMEOUT_MS` | `src/domain/hopperStartupFailure.ts` | 10000 | 1 | 2147483647 | Linux private-display adapter start | The helper reads the process environment. Prose restart. |
| `REA_JADX_ACTIVE_PROCESSOR_COUNT` | `src/android/JadxConfiguration.ts` | unset (JVM default) | 1 | 2147483647 | JADX JVM launch | `-XX:ActiveProcessorCount` is fixed for that JVM. Prose on invalid values. |
| `REA_JADX_HEAP_MIB` | `src/android/JadxConfiguration.ts` | unset (JVM default) | 1 | `Number.MAX_SAFE_INTEGER` | JADX JVM launch | `-Xmx` is fixed for that JVM. Prose on invalid values. |
| `REA_KUNA_CACHE_MAX_BYTES` | `src/kuna/KunaConfiguration.ts` | 4294967296 | 1048576 | `Number.MAX_SAFE_INTEGER` | Kuna cache construction | The cache is sized once for the provider. Prose restart. |
| `REA_KUNA_JOBS` | `src/kuna/KunaConfiguration.ts` | 1 | 1 | 32 | Kuna run-slot construction | Worker-pool width for the provider. Prose restart. |
| `REA_MCP_DOCTOR_DEADLINE_MS` | `src/config/mcpStartupBudgets.ts` | 30000 | 1 | 2147483647 | MCP server startup | Startup phase of the MCP server itself. Prose. |
| `REA_MCP_FIRST_CATALOG_BUDGET_MS` | `src/config/mcpStartupBudgets.ts` | 20000 | 1 | 2147483647 | MCP server startup | Startup phase of the MCP server itself. Prose. |
| `REA_MCP_INITIALIZE_BUDGET_MS` | `src/config/mcpStartupBudgets.ts` | 15000 | 1 | 2147483647 | MCP server startup | Startup phase of the MCP server itself. Prose. |
| `REA_MCP_MAX_INPUT_ELEMENTS` | `src/config/mcpInputBudget.ts` | 1024 | 1 | `Number.MAX_SAFE_INTEGER` | MCP server creation | Argument admission for the server process. Prose restart. |
| `REA_MCP_MAX_RESPONSE_BYTES` | `src/config/mcpResponseBudget.ts` | 10485760 when unset | 10485760 | `Number.MAX_SAFE_INTEGER` | MCP server creation | Stdio receive buffer and the paired client buffer. Prose restart. |
| `REA_MCP_TOOLS_LIST_BUDGET_MS` | `src/config/mcpStartupBudgets.ts` | 5000 | 1 | 2147483647 | MCP server startup | Startup phase of the MCP server itself. Prose. |
| `REA_RECONNECT_TIMEOUT_SECONDS` | `src/hopper/BridgeLauncher.ts` | 30 | 30 | 30 | written into the Hopper bridge helper | Not a caller setting. It is `HOPPER_SHUTDOWN_TIMEOUT_MS / 1000`. |
| `REA_SWIFTC_COMPILE_TIMEOUT_MS` | `src/config/providerStartupTimeout.ts` | 120000 | 1 | 2147483647 | Darwin ownership-helper reader construction | Captured at process start for that helper compile. Prose restart. |
## Restart-only non-numeric settings
No floor or maximum. They are read from the launch environment at startup.
Recovery text tells the caller to change the launch environment, which takes
effect on the next process. They stay
restart-only because they choose a command, a binary, a filesystem root, the
browser sandbox, secret patterns, the provider, or the shape of the MCP server
| Native structural input ceilings | PE resources, imports, load config, certificates, exports, initializers, binary hardening, Windows shortcuts | One setting per ceiling, or a shared input budget only where the owners match. |
| Native plist | `MAX_NATIVE_PLIST_BYTES`, decode bytes, nesting depth | Separate from JSON depth. |
| SQLite inspection | concurrent inspections, input bytes, diagnostic bytes | Pool width stays restart-only if it sizes the worker set; byte budgets can become settings. |
| Producer command allowances | fixed command deadline and retained output where the text says there is no runtime override | Distinct from per-call `producer_limits`. |
| Hopper caller-scan retention and page allowances | `HOPPER_CALLER_SCAN_RETENTION_BYTES`, `HOPPER_CALLER_PAGE_ALLOWANCE_BYTES` | Explicitly not raised by the scan time budget. |
`user` names no file. A name without an extension, such as `Makefile`, must
also stand on its own rather than end a longer path, as in
`Makefile - Editor`. A file under `~/.cache` or `~/.config/dconf` is never
named by a title. The capture's checkpoint acknowledgements rank files by the
same rule.
Application names, which select dconf groups, come from the package, the
executable, the desktop entry's id and `StartupWMClass`, and the window match.
@@ -70,11 +144,21 @@ whether the file was present, the SHA-256 of its retained bytes, and a view:
| `dconf` | The `GVariant` header | Groups whose path names the application, plus the names of the other groups |
| `zip` | A zip local file header, `PK\x03\x04`, whatever the name | Its file entries in archive order, each with its own format and view |
| `json` | A `.json` name or a leading `{` or `[` that parses | The parsed value, from files up to 4 MiB nested up to 500 levels |
| `xml` | An XML name or declaration that parses | An element tree (`name`, `attributes`, `children`); text kept exactly, DTDs never read |
| `xml` | An XML name, a declaration or a leading element that parses | An element tree (`name`, `attributes`, `children`); text kept exactly, DTDs never read |
| `keyfile` | Only `[group]`, `key=value` and comment lines, with at least one group | Groups of `key` → string value, from files up to 4 MiB |
| `text` | Other UTF-8 text | Lines, up to 5,000 |
| `binary` | NUL bytes or invalid UTF-8 | None |
A gzip file is read as the document inside it; see
[compressed documents](#compressed-documents).
Text that opens with an element, such as a Freeplane map's
`<map version="freeplane 1.7.0">` with no declaration, or with a DOCTYPE that
has no internal subset and then an element, such as a KmPlot plot's
`<!DOCTYPE kmpdoc>` before `<kmpdoc version="4">`, is XML when the whole text
is well formed, whatever the file is called, a `.txt` file included;
otherwise it stays text. The exported verifier makes the same choice.
A JSON value holding a number JavaScript would round, such as an integer above
2^53, is not read rather than shown rounded.
@@ -128,7 +212,15 @@ could misstate its committed rows. The capture records them under its content
roots, which include both beside a database selected on its own, when its
baseline walk listed every directory, read every path, entered no other
device, did not stop early and recorded what it left out, and wherever they change; an excluded log, and any elsewhere, is
unknown, since an unchanged file and a missing one look alike. A table
unknown, since an unchanged file and a missing one look alike. After an
incomplete checkpoint, a database or sidecar without an explicit
later change remains unconfirmed even if a later walk completes. REA does not
combine that older file with a newly recorded member of the SQLite set. The
historical versions remain listed; the affected SQLite view has a `view_error`.
A baseline non-file at a sidecar path, or a non-directory ancestor, cannot
establish an absent file. Captures without the required baseline non-file
inventory must be made again before inspection.
A table
that cannot be read keeps its `error` in place of its rows, and changes to that
database are marked incomplete. dconf
databases are read with `dconf dump /` against a private file profile, so the
@@ -185,6 +277,37 @@ so an archive of hundreds of thousands of entries costs no more to read
than one of a thousand. An archive whose central directory cannot be read has a
| `lines_present` | `lines`| Every listed line is a whole line of the text view |
| `lines_absent` | `lines`| None of the listed lines is |
| `line_count` | `exact` or `contains`; `equals`, `at_least` or `at_most` | The number of lines equal to `exact`, or holding `contains`, compares so |
| `value_equals` | `pointer`, `value`; optional `tolerance`| The pointer names a value equal to `value`, or within `tolerance` of it |
| `value_absent` | `pointer`| The pointer names no value |
| `text_equals` | `pointer`, `value`; optional `normalize_whitespace`, `text_attribute` | The XML element's [whole text](#whole-text) equals `value` |
| `text_contains` | `pointer`, `value`; optional `normalize_whitespace`, `text_attribute` | The XML element's whole text holds `value` |
| `member_count` | `pointer`; optional `matching`; `equals`, `at_least` or `at_most` | The number of [members of the array](#counting-members), or of those `matching` names, compares so |
| `row_present` | `table`, `where` | A row of the SQLite table has every column in `where` equal |
| `row_absent` | `table`, `where` | No row does |
| `row_count` | `table`; optional `where`; `equals`, `at_least` or `at_most` | The number of rows, or of rows matching `where`, compares so |
The rules both evaluators follow:
- An absent file reads as empty: no lines, values or rows. So `lines_absent`,
`value_absent` and `row_absent` pass on it and a count is 0.
`value_absent` and `row_absent` pass on it and a count of lines or rows is
0; `member_count` fails, since its pointer names no array.
- A file that has no view (binary, over the read limit, or unreadable) fails
every check but `file_present` and `file_absent`. Line checks need a text
view, row checks a SQLite view and text checks an XML view, in a file or an
@@ -129,7 +556,9 @@ The rules both evaluators follow:
`{"groups": {"GROUP": {"KEY": "VALUE"}}}`, so a setting is
`/groups/GROUP/KEY`; XML as `{"root": {"name", "attributes", "children"}}`;
SQLite as `{"tables": {"TABLE": {"columns", "rows"}}}`; a zip archive as
`{"entries": [{"name", "format", "view", …}]}`.
`{"entries": [{"name", "format", "view", …}]}`; a gzip file, such as a
Gnumeric `.gnumeric`, as the document inside it, so its pointers are those
of the same document uncompressed.
- With `tolerance`, a non-negative number, `value` must be a number, and the
value found passes when it is a number, or a string that is a decimal
numeral such as `"5.0000000000000000"`, at most `tolerance` from `value`.
@@ -141,20 +570,106 @@ The rules both evaluators follow:
- A check decided by what a view leaves out (`lines_absent`, `value_absent`,
`row_absent`, the counts, `value_equals` of a whole object or array, and
the text checks of an element) fails when that view is truncated; a check
satisfied by what the view holds is decided on it.
satisfied by what the view holds is decided on it. An element's text is not:
the reader may have dropped text from between the runs it kept, so
`text_contains` fails on a truncated view even where the kept runs hold its
value. A text node or attribute value is whole, and decided.
The export refuses, naming the check, a file outside the owned paths, a file
spelled with a leading slash, `.` or `..`, a pointer into a file that is text
at the seed, as the bundle ships it, or at the end checkpoint, the empty
pointer (the whole file), a value that names the captured home, or the
pointer (the whole file) except in `member_count`, a `matching` that is no
selector, a value that names the captured home, or the
capture host's name or `user@host` anywhere (which the
[export removes from the seed](desktop-application-export.md#host-name-and-user)), a
`tolerance` with a value that is not a number, an empty `text_contains`
value, or one of only whitespace with `normalize_whitespace`, and a value that
looks like a date and time, in ISO 8601 or`asctime`'s form. Other volatile
value, or one of only whitespace with `normalize_whitespace`, an empty
`text_attribute`, a count without exactly one of`equals`, `at_least` and
`at_most`, and a value that looks like a date and time, in ISO 8601 or
`asctime`'s form. Other volatile
content, such as a
counter the app bumps on every save, is for the author to leave out.
## Readers
For a file REA cannot interpret itself, such as an app's binary document,
the task can define `readers`: 1 to 8 commands using the app's own reading
capability. Each reader has a unique `id` of lowercase letters, digits and
hyphens, a home-relative `file`, an `argv` array and `output`, either `json`
or `lines`. Its `argv`, or optional `stdin` text, must contain `{file}`;
the runner replaces it with the path of a private copy of that file.
The command must be installed where the export runs and in the exported
computer where the task will be graded.
The command prints its answer in lines starting with `REA `; other stdout
is ignored. With `output: "json"`, those marked lines join into one JSON
value for `value_equals`, `value_absent` and `member_count`. With
`output: "lines"`, they supply the lines for `lines_present`, `lines_absent`
and `line_count`. A check selects a reader with `"reader": "READER-ID"`
and must give that reader's file as its `file`. Every reader must be used by
at least one check.
Readers execute commands, including any code in `argv` or `stdin`; review
them before exporting or running a task. The runner uses a private copy, a
fresh home, no session bus or computer control token, and no display unless
the reader requests `display: true`. This is execution isolation from the
running app's normal profile, not a filesystem sandbox. `timeout_s` defaults
to 60 seconds and accepts 1 to 300. A failed command, timeout, malformed
answer or missing marked output reports a reader error; it does not establish
that the document lacks the expected value. Correct the command or its output and retry; increase
`timeout_s` within its accepted range when the reader needs longer.
A source inspection failure, such as an inaccessible parent or a symlink loop,
also reports a reader error and leaves affected checks undecided. Only a missing
source or a source that is not a regular file is reported as absent.
Export runs readers on both retained ends, using the seed bytes as the bundle
actually ships them. The witness and exported verifier run them in the
computer after the app's files settle. Cleanup failures retain the reader's
reported resources: retry the check or close the build computer to retry
cleanup; when the command's outcome is unknown, confirm it stopped before
removing its folder.
## Screen judges
A task whose result exists only on screen can use `screen_judge` checks:
```json
{
"name": "show-list",
"prompt": "In Notes, show the shopping list.",
"checks": [
{
"id": "shown",
"kind": "screen_judge",
"question": "Does the window show the shopping list?"
}
]
}
```
Each question must be a nonempty yes-or-no question of at most 2,000
characters, false on the seed and true when the task is done. A screen task
contains only screen checks and takes no readers. Export refuses it when an
owned file's readable change could be checked instead, or when the build
computer's seed or end screenshot is missing or exceeds 2 MiB. Capture those
checkpoints in a build computer before exporting.
Export ships the two screenshots as labelled failing and passing examples;
it does not call the judge or establish that the questions distinguish them.
The [build witness](desktop-build-witness.md) runs the exported judge on a
fresh screenshot at each stage. The verifier takes a screenshot after the
solving agent stops. For each question, the judge sees both examples and
votes three times; a majority of yes answers passes the check.
The judge uses `[model.roles] judge` in the runner's
`.agentenv/config.toml`, then `[model] default`, then
`anthropic/claude-sonnet-5-5`. Configure its endpoint with
`LITELLM_BASE_URL` and `LITELLM_API_KEY`, or `[model] base_url` and `api_key`.
`agent-env run --judge-model` does not reach this verifier. Without an
endpoint, grading raises and the run fails rather than scoring the agent;
configure the endpoint and rerun the witness or task.
## Pointers and selectors
An app that rewrites a document moves things: FreeCAD 1.0 records more
@@ -168,9 +683,34 @@ rather than by its position:
`[name=Object]`.
- `[@KEY=VALUE]` names the member whose `attributes` hold `KEY="VALUE"`, as
the XML view keeps an element's attributes.
- `[>PATH=VALUE]` names the member that holds `VALUE` at a path of one to
three `/`-separated steps inside it. `>` reads XML and JSON differently:
For XML, `[>text()=VALUE]` compares the member's own whole text, with
whitespace collapsed, to `VALUE`. In JSON, `text()` remains a field name.
| `[>CHILD=VALUE]` | a child element `CHILD` whose text, every text node in it with each run of whitespace collapsed to one space and none at either end, is `VALUE` | the string field `CHILD`, compared whole |
| `[>A/B=VALUE]`, up to `[>A/B/C=VALUE]` | a child `A` with a child `B` of that text | the string at `member.A.B`, compared whole |
| `[>A@ATTR=VALUE]`, `[>A/B@ATTR=VALUE]` | a child `A` (or its child `B`) whose attribute `ATTR` is `VALUE` | the field named `A@ATTR`: `@` is part of a JSON key |
Records that keep their fields as elements are named by a child's text:
Osmo's `<contact><first_name>Delphine</first_name>…` is
`[name=contact][>first_name=Delphine]`. A GtkBuilder `<child>` is named by
the id its widget carries, `[name=child][>object@id=submit_button]`, and a
TreeLine node by a field of its `data`,
`[format=BOOK][>data/Title=The Silk Roads]`. Any element on the way will
do; a path with more than three steps, or an empty one, is no selector;
and a missing step, or a value at its end that is not a string, matches
nothing.
- Conditions can follow one another, `[name=Object][@name=Hole]`, and all must
hold. They compare strings only, the selector is escaped like any pointer
segment (`~1` for `/`), and a value cannot hold `][`.
segment, so a path's `/` is `~1` inside a pointer,
`/nodes/[format=BOOK][>data~1Title=The Silk Roads]`, and a `/` in a key
cannot be a step; a value cannot hold `][`. A JSON field whose key begins
with `@` or `>` is selected through a path, `[>@type=Book]`.
A selector must name exactly one member. When several match, or none does
before the last segment, the check fails and says which selector matched what.
@@ -184,7 +724,7 @@ FreeCAD 0.21 and 1.0 alike:
```json
{
"name": "cut-hole",
"prompt": "Cut a 10 mm hole through the middle of the plate and save it.",
"prompt": "In FreeCAD, cut a 10 mm hole through the middle of the plate and save it.",
"checks": [
{
"id": "cut",
@@ -291,11 +831,56 @@ read the element's whole text instead:
and trims both ends before comparing, as XPath's `normalize-space()` does,
for an app that reflows text or a value written across lines. A no-break
space is not whitespace there.
- `text_attribute` reads text an app keeps in an attribute: the element's
text is then that attribute's values on it and its descendants, joined in
document order, and its text nodes are left out. Scribus keeps a text
frame's text as `<StoryText><ITEXT CH="Spring "/><ITEXT CH="Fair"/></StoryText>`,
one `ITEXT` per run of formatting, so the check below reads `Spring Fair`
however the runs are split. The pointer must name an element; a text node
and [renderer usage notes](https://github.com/earendil-works/pi/blob/b45e0ebcc5ae2fc42a6f2f23104d10e656e6bedb/packages/codemode/README.md#declarations-for-the-model).
## Cline
The original contributor reported a run on 2026-11-11 on Linux (Fedora 45),
using Cline CLI 3.0.74 and the integration based on REA `main` at `9924bf344`.
That run checked client config listing and a direct registered-server MCP
handshake (254 tools on that build). It predates the verifier lifecycle changes
below and does not establish their real-client behavior.
`npm run verify:cline-client` creates its sandbox home before launching commands
and removes inherited `CLINE_MCP_SETTINGS_PATH`, `CLINE_DATA_DIR` and `CLINE_DIR`.
Setup therefore exercises the default
`~/.cline/data/settings/cline_mcp_settings.json` path under the sandboxed home.
Cline's CLI must list that registration using the sandbox's explicit
`--data-dir`; this checks the file parser, not independent default-home discovery.
The exact registered launcher then
completes SDK initialization and tool discovery through REA's owned stdio
transport. Stdin stays open until discovery finishes. Command and transport
cleanup completes before the verifier records lineage or reports success.
The updated orchestration has recording-port coverage for delayed discovery,
failed server identification and failed cleanup. These tests use the installed
SDK and verifier lifecycle owner, but do not execute Cline or a real REA server.
The updated real-client lane has not been rerun during this reconciliation.
Not covered by the lane: a model-driven tool call inside Cline. Cline's provider
configuration (`providers.json`) is versioned and auth-shaped, and the CLI ignores
`OPENAI_BASE_URL`, so the loopback model fixture cannot drive it without depending
on that private schema. The IDE extension is not covered either: its MCP settings are
UI-managed and live under editor-specific `globalStorage` roots, which `rea setup`
does not write in this release.
<a id="llamacpp"></a>
## llama.cpp
@@ -643,3 +674,65 @@ npm run evidence:check
The records preserve unsupported and unverified coverage as explicit unknowns.
Run the matching real-tool prerequisites described in this guide.
### Focused tool-description trials
For a wording or discoverability question, begin with one representative case
per affected workflow boundary and its nearest valid alternative. Repeat a
concrete failure or ambiguous result after repair. Add repetitions when the
requested confidence or comparison needs them; a large matrix is not the default.
Use the user's selected model and budget, and verify the actual client and Node
executables used by the trial rather than assuming the development shell's
versions apply.
Reuse the existing optional lane and source-owned scenarios where they fit:
```bash
REA_AGENT_EVAL_SCENARIOS=javascript-module-view npm run verify:agent
```
Select the account-supported model with `REA_AGENT_EVAL_MODEL`. Read the selected
prompt and rubric before interpreting the gate: closed factual scenarios request
complete producer Evidence, while `javascript-module-view` exercises retained
views with heuristic scoring. Neither gate alone proves correct selection or
fully supported prose for a different task. Extend the narrow scenario or grader
in the existing evaluation tooling when necessary; reusable runners and fixtures
belong there, not in skills or copied temporary harnesses.
Capture the actual `initialize` and `tools/list` surface. Compact presentation may
remove parameter descriptions or nested selector shapes, so a concise exact call
example can remain necessary. Verify the producer-to-consumer handoff before
removing its visible syntax. For a wording comparison, hold prompts, schemas,
tool order, fixtures, permissions and host settings fixed; change one surface at
a time. Use fresh agent context without the audit's answers. Record later source,
fixture or runtime changes separately rather than claiming a controlled delta.
Score these outcomes separately:
- Tool selection, accepting other valid routes.
- Canonical argument validity and the exact requested paths, IDs, selectors,
fields, units and delivery. Structurally valid arguments can select the wrong
input; fixtures should refuse that selection instead of returning the expected
answer anyway.
- Recovery and cleanup. A valid unresolved lookup is not a malformed request;
normal status reads and cleanup-only retries are not automatically mistakes.
Do not infer that work stopped from a host-only timeout.
- Final claims against returned facts and coverage. Correct text does not erase
malformed-call loops, and valid calls do not prove every claim. A failed name
lookup is not a negative reference search; an exhausted partial page does not
establish absence in unexamined source. Generic responses without state facts
cannot establish that a session is closed.
Retain prompts, condition/catalog hashes, actual calls/results, failures, final
answers, model/client/runtime versions and cleanup outcomes. Report whole-task
input, cached input, output and elapsed time when available, and label unmeasured
CPU or process-family memory. Fewer description bytes do not establish fewer
task tokens or faster execution. Remove copied authentication after owned
processes settle and review retained artifacts before sharing them.
Controlled errors can test agent recovery without target effects. Label them as
simulated and verify applicable workflows separately through the production
adapter and real provider boundaries. State tool-subset versus full/progressive
catalog scope and remaining provider/host coverage. Small one-shot trials are
smoke checks, not statistical success rates or evidence that newer models need
@@ -238,7 +238,9 @@ broad changes or diagnosing CI, rather than after every edit. Package/install
changes additionally need package verification; provider changes need actual
provider evidence.
Vitest projects use up to two workers, bounded by host parallelism.
Vitest projects use up to two workers, bounded by host parallelism. GitHub
Actions jobs own their runner and use up to four; see
[test deadlines](#developer-commands) for how deadlines scale there.
`process-boundary` runs later with serial files because process-tree sampling
shares host resources. Acceptance and process-global cases use isolated forks
without serial scheduling. Pure domain/service, composition, conformance,
@@ -283,7 +285,7 @@ coverage. Ordinary CI also exhausted the former 384 MiB budget after earlier
files loaded the shared source/contract graph, while later command registration
and schema/help rendering still needed transient allocation space. This is a
measured capacity control, not a suite-wide speedup claim. The worker count
remains bounded at two, and spawned REA processes retain their own heap settings.
remains bounded, and spawned REA processes retain their own heap settings.
Coverage enablement still controls instrumentation, test deadlines and coverage
thresholds; explicitly disabling it or selecting only a provider does not enable
instrumentation.
@@ -299,6 +301,21 @@ work from fixture generation, schema compilation, subprocess startup and cleanup
Choose elapsed, CPU and process-family memory limits before the run; a worker
count or test timeout alone does not enforce those limits.
A test deadline turns a hang into a named failure; it is not a performance
assertion. `vitest.config.ts` applies the deadlines in
`tests/support/testDeadlines.ts` per project: 30 seconds for source projects
and 120 seconds for built projects, with hooks sharing their project's
deadline. Coverage doubles every deadline, and so does hosted CI, where
CPU-bound cases share every core with other workers and their subprocesses.
Do not pass a per-test timeout to fit a slow host. A child run through a
test process registry (`processTest`, `cli.run`, or `createTestProcessRegistry`)
gets the running test's remaining deadline less a reporting reserve, so a hung
child fails with its captured output. Pass `timeoutMs` only when the child's deadline is itself under test. Tag a bounded workload that needs longer with
`{ tags: ["long-running"] }`, or a whole file with
`// @module-tag long-running`. Assert elapsed time explicitly when speed is the
behavior under test. Derive an explicit deadline only when a test must outlive
an operation deadline whose diagnostics it checks.
Keep capacity fixtures intact when diagnosing a test failure. For byte equality
of large buffers, use `Buffer.equals` or `Buffer.compare`; a generic structural
diff can allocate far more than the production operation.
@@ -407,6 +424,10 @@ relying on the new scope. While the variable is unset, the existing default
setup continues scanning. Adding the configuration file alone does not change
default setup in this user-owned repository.
Pull-request scans are cancelled when superseded. Active scans on `main` finish
so frequent pushes cannot prevent a complete baseline from being uploaded.
Cancelled runs skip the SARIF artifact upload when no report was produced.
Each analysis uses two threads and a 4096 MiB CodeQL memory budget; language
jobs run one at a time. Complete SARIF reports are retained for seven days.
For a scope comparison, use the same source SHA, CodeQL bundle, default query
@@ -602,6 +623,12 @@ For partial Evidence delivery, verify that same-session readback preserves
identity and provenance without rerunning the provider. Include a no-writer
control that keeps completed Evidence inline and preserves the original failure.
For a seekable file with a known extent, distinguish a provably impossible wire
extent from an independent representation limit. Pair the malformed declaration
with a structurally possible over-limit control. A count-only truncated fixture
does not establish capacity behavior. This does not require decoding an opaque
compressed payload merely to apply its declared expansion limit.
When producer order is unspecified, synchronize fixtures on the observed request
or completion event. A filename, generated key, or fixed delay does not prove
which item completed before cancellation. Preserve real producer ordering and
@@ -702,16 +729,16 @@ the affected owners and applicable scenarios below. Include temporary snapshots
and one-shot commands. Use public CLI/MCP workflows where practical and a
controlled production seam for interleavings that cannot be reproduced reliably.
| Target admitted; startup still pending | Status preserves the readiness distinction and observed startup activity; readiness requires the provider's documented proof. |
| First handshake is late, absent, invalid, or recovered during close | Initial and recovery paths retain the same authenticated identity facts. Foreign run/token controls cannot authorize cleanup; authenticated idle/native work survives the unauthenticated listener deadline. |
| Launch returns an error with a valid process handle | Cover already-exited and still-live owned processes. Verify observed exit separately from creation; failed termination or unavailable identity remains unresolved. |
| Cancellation arrives after workload exit, during result reads or temporary-root removal | Return typed cancellation with available partial observations and actual cleanup status. The non-aborted control succeeds; existing primary/cleanup failures keep their precedence. |
| Children have mixed health or cleanup outcomes | Missing peer health does not erase an observed failure or activity. Idle plus unknown stays unknown. Retry a failed child without closing a successful peer again, including after a close-option change. |
| Cleanup fails after success or primary failure | Public output retains the completed observation or primary diagnostic, remaining resources, and owner recovery action. Restore the denied resource and verify the owning cleanup retry. |
| Cleanup owners recover after an error was returned | Retain the original child and aggregate errors and project them after each owner releases. A successful peer stays released; the aggregate remains incomplete until all owners release. Preserve the first cause even when rejection is `undefined`. |
| Target exits without an acknowledgement, or transport disappears while it stays alive | Positive owned exit proof resolves target lifetime; other resources still need cleanup. Lost transport alone does not prove exit. Verify unrelated/attached targets survive and same-connection recovery works when supported. |
| Target admitted; startup still pending | Status preserves the readiness distinction and observed startup activity; readiness requires the provider's documented proof. |
| First handshake is late, absent, invalid, or recovered during close | Initial and recovery paths retain the same authenticated identity facts. Foreign run/token controls cannot authorize cleanup; authenticated idle/native work survives the unauthenticated listener deadline. |
| Launch returns an error with a valid process handle | Cover already-exited and still-live owned processes. Verify observed exit separately from creation; failed termination or unavailable identity remains unresolved. |
| Cancellation arrives after workload exit, during result reads or temporary-root removal | Before the documented commit cutoff, return typed cancellation with available partial observations and actual cleanup status. After commit, assert the owning publication outcome and receipt. The non-aborted control succeeds; existing primary/cleanup failures keep their precedence. |
| Children have mixed health or cleanup outcomes | Missing peer health does not erase an observed failure or activity. Idle plus unknown stays unknown. Retry a failed child without closing a successful peer again, including after a close-option change. |
| Cleanup fails after success or primary failure | Public output retains the completed observation or primary diagnostic, remaining resources, and owner recovery action. Restore the denied resource and verify the owning cleanup retry. |
| Cleanup owners recover after an error was returned | Retain the original child and aggregate errors and project them after each owner releases. A successful peer stays released; the aggregate remains incomplete until all owners release. Preserve the first cause even when rejection is `undefined`. |
| Target exits without an acknowledgement, or transport disappears while it stays alive | Positive owned exit proof resolves target lifetime; other resources still need cleanup. Lost transport alone does not prove exit. Verify unrelated/attached targets survive and same-connection recovery works when supported. |
Shared process-supervision controls separate leader exit from output closure.
Cover output during held or failed cleanup, bounded retention and drain timeout,
@@ -732,6 +759,16 @@ materialized source. Exercise descriptor close failure and retry before output
publication.
Use explicit abort reasons and assert their identity at admission and after
provider completion, alongside retained observations and cleanup state.
Include a physical I/O failure reused as the abort reason and genuine
cancellation whose reason is already a typed error; neither error names nor
value equality establishes origin. Assert that cleanup capabilities carried by
that opaque reason are neither adopted nor released. Verify an owning command's timeout or
refusal policy when preparation records cancellation but its caller is still
active. Parallel inspection failures must retain completed observations and
mixed physical/cancelled request facts after all peers settle, including
multiple failed closes and partial cleanup retry. For input copying, hold an
admitted read or write in flight before cancelling; descriptor close and
workspace retirement must await its settlement.
Electron fuse regressions cover actual descriptor close denial and retry through
the built CLI, same-connection MCP recovery, mixed descriptor outcomes, and
@@ -1057,32 +1094,12 @@ existing four-minute timing collection plus the bounded diagnostic stages.
Fast built shards target under ten minutes of uncontended shard-job execution.
That figure is the `test-shard` job. The job waits for classification and the
build, and the portable-test merge runs after the slowest shard. On 2026-10-20,
run `39451548945` measured those surrounding jobs with runner queue excluded:
classification 15 seconds, build 49 seconds, and the portable-test merge 29
seconds. Shard 1/5 of that run spent 22 seconds before built Vitest and 3
seconds after it, including `npm ci` at 9 seconds. `node_modules` is about 674
MiB, so publishing it as a shared artifact would transfer more data than that
install saves. The npm download cache stays the setup cache.
File timings from that run put the built suite at 7944 seconds of module time.
The sequencer keeps each file's full duration on one worker. Extra workers
overlap other files in the same sequence group, and `process-boundary` runs
after that group. Scheduling the unsplit inventory onto five shards peaks at
820 seconds of tests. That run's slowest built shard job lasted 925 seconds
(shard 4/5), and shard 1/5 spent 874 seconds in built Vitest, so hosted job
time sits above the file schedule. Six shards of the same inventory peak at
684 seconds of tests. Eight shards, after the 578-second loader file is split
by case count (54 cases stay in that file and 46 move into each of three
files), peak at 513 seconds of tests. With the measured 25-second shard setup,
that shard job is about 538 seconds. Adding the measured classification, build,
and merge makes an uncontended portable critical path of about 631 seconds,
still before the gap between this file schedule and hosted Vitest. Runner queue
is separate and grows when more shard jobs compete for runners.
build, and the portable-test merge runs after the slowest shard. Runner queue is
separate and grows when more shard jobs compete for runners. The npm download
cache stays the setup cache rather than a shared `node_modules` artifact.
`process-boundary` stays serial because process-tree sampling shares the host.
Raising the worker cap from two to four on this four-core host cut a six-file
acceptance sample from 96–101 seconds to 71 seconds and did not remove the
single-file floor, so the cap stays two. Full qualification keeps six covered
Hosted CI jobs use four workers; other hosts stay at two
because they are shared with other checks. Full qualification keeps six covered
shards; its longer instrumentation is outside this fast budget.
The separate `CI timings` workflow batches recent completed CI and Release runs
for independent observations, boundary controls and reporting unavailable
provider coverage. A scripted tool sequence does not establish agent selection;
that claim requires an actual agent evaluation.
## Audit existing tools
@@ -94,14 +82,10 @@ identity, session lifetime and available alternatives. Matching text between
two renderers does not establish that their shared advice is correct or that an
agent selected the workflow.
Exercise the selected public workflows, including the steps before and after a
tool call. Check task selection, input choices, returned facts and their source,
partial or unsupported outcomes, and applicable session and recovery behavior.
Compare native provider output and an independent artifact observation where
available. Inspect existing tests and real-provider lanes for the scenario they
actually cover; a passing test does not establish a different task or host.
Use [workflow verification](testing.md#provider-backed-workflow-verification)
for counterexamples and lifecycle controls relevant to the audit.
Exercise each selected workflow before and after the tool call. Use
[workflow verification](testing.md#provider-backed-workflow-verification) to
check returned facts, partial outcomes, recovery and the scope of existing
provider evidence.
Report working routes as well as findings. Separate a reproduced contract bug,
an intentional limitation, a design opportunity and an unverified hypothesis;
@@ -109,6 +93,38 @@ include the smallest useful reproduction, impact and coverage limits. Do not
claim autonomous-agent selection from a scripted trace. Check related reports
before proposing another issue, and avoid treating lack of API parity as a bug.
### Write descriptions for selection and a valid first call
Start with the analyst task, selected source and useful result. Put material
effects and the deciding boundary between neighboring tools early. Progressive
discovery previews the first 240 characters, so those characters should identify
the operation without requiring the rest of its reference documentation.
Keep exact call syntax where the advertised schema cannot convey it. The compact
input profile strips most annotation prose and can reduce nested controls to
`{type: "object"}`. Moving selectors into parameter descriptions alone does not
make them available to those clients. For example, retained pseudocode needs
both `source.kind: "retained-evidence"` and `view: {kind: "native", facet:
"pseudocode"}`; a short exact example can be more useful than many paragraphs.
Use the producer's handoff for other supported views rather than maintaining
another supposedly exhaustive list in the shared consumer description.
Keep configuration tutorials and detailed matching algorithms with their owning
parameter contracts, product guides and actionable failures. Retain applicability,
source identity, uncertainty, authority and cleanup facts needed to choose or
safely continue the operation. An unresolved symbol lookup is not a completed
reference search; an exhausted page does not establish absence in unexamined
source. Shorter copy must preserve these distinctions.
For wording changes, use a small set of representative agent tasks at the affected
boundaries. Hold schemas, tool order, fixtures and host configuration fixed when
comparing copy; add repetitions only for failures or ambiguous outcomes. Score
actual arguments, corrective calls and supported final claims separately. A
correct eventual answer can conceal malformed-call loops, while a valid call
can still produce an overstated answer. Report whole-task usage, including
retries and cached input, rather than treating fewer description bytes as proof
of lower token cost.
## Design recovery around the failure
Distinguish a request that violates its input contract from a valid selection
@@ -182,7 +198,7 @@ does not establish another instance.
whether applicable static relationships could have been missed by discovery;
missing discovery is unknown. Preserve paths supported by observed edges,
and qualify negative and shortest-path claims by discovery scope. Missing
relationship coverage remains `unknown`.
relationship coverage remains `unknown`. Computed candidates and unestablished incoming targets qualify directional discovery; they do not by themselves mean that recorded edges were omitted.
- Declare process, filesystem, network, UI, and mutation effects truthfully;
enforce the requested target and lifecycle.
- Add limits only for real format, protocol, authority, or measured resource
@@ -342,19 +358,23 @@ In multi-stage work, retain each completed, admitted observation before awaiting
the next stage. Later reads, validation, cancellation, or finalization can fail
without erasing those facts. Carry them through typed failure outcomes with
explicit coverage of unperformed work; do not construct a successful result for
stages that never ran.
stages that never ran. For concurrent reads, the batch owner settles every
admitted peer before reporting a failure. Retain completed observations together with each
failed request's physical, cancellation and cleanup facts.
- Keep target admission, provider readiness, active work, observed process exit,
and resource release separate. A valid process handle, successful launcher,
or selected target establishes only its own fact. Publish available identity
and exit observations even when launch fails; retain unknown ownership.
- Process exit does not establish output closure. The shared process supervisor
retains at most 64 KiB of additional diagnostics from owned stop entry,
including pending cleanup and retries, and drains for up to one second after
verified stop. It marks output incomplete when closure cannot be
observed and releases owned event-loop references without changing the proven
process-cleanup outcome. Failed process cleanup retains capture for retry;
borrowed processes and streams retain their own lifetime and reference state.
- Process exit does not establish output closure. The
| `0` | The operation completed. Its result may include partial evidence, warnings or unresolved questions. |
| `1` | The operation could not complete. Structured output identifies invalid input, permissions, cancellation, timeouts or another failure when available. |
| `3` | `audit-release-exposure` completed with `--fail_on` and an exposed check meets that severity. Unknown and unassessed checks do not change this status. |
| `128 + N` | Signal-derived status. An unhandled signal may have ended the process, with the shell or runtime preserving the conventional status; a command may instead have handled the signal, waited for its operation and cleanup to settle, and assigned the same status (`130` for SIGINT, `143` for SIGTERM). In the handled case, it may also have printed a structured `cancelled` result. |
`rea mcp --http` is long-running and prints no structured result. It exits
@@ -1330,62 +1453,39 @@ retains failed stages for retry; the CLI exposes no separate retry command.
or prints the refusing stage's structured error and exits `1`, writing nothing
when the refusal comes before the bundle is written. SIGINT cancels it with a
`cancelled` error; a cancellation before the bundle stage writes nothing.
`rea environment-export` is one-shot: it prints the written files and exits
`0`, or prints a structured `invalid_request` error and exits`1`for the same
pair refusals, a non-empty or file-occupied directory, a missing package
tarball or a name that is not a kebab-case label or is a Windows device name,
writing nothing in each case. A file occupying the destination is reported as
requiring a directory; identity-change diagnostics are reserved for admitted
paths that fail later identity checks. Host permission failures identify whether
input reading or bundle writing was denied and retain the reported path.
A write that fails after the first file (a permission, disk-space or
pathname-length failure on a later path) propagates as the host's failure,
naming its cause and, when it failed on one bundle file or the package
tarball, that file. A failure while syncing the finished folder names only its
cause. Either way the partial folder stays in place for inspection; remove it
before retrying, since the next attempt refuses the non-empty directory.
A `cancelled` result does not by itself determine the exit status. Commands
that handle SIGINT or SIGTERM assign `130` or`143`after their operation and
cleanup settle; their structured result can be printed before that status is
assigned. Read the process status and typed result separately.
A `cancelled` result does not by itself determine the exit status; it depends on
the command's signal handling. Commands routed through the shared
`withCommandCancellation` handler in the CLI set status `130` for SIGINT or
`143` for SIGTERM after their operation settles. Current source registrations
use that handler for process capture and comparison (`capture-process`,
`compare-process-captures`), desktop application capture and state
(or `cline-vscode`) configures the VS Code extension, while
`rea mcp add --agent cline-cli` configures the CLI. The setup lifecycle uses
`rea setup --client cline` and `rea doctor --client cline` for the CLI.
`rea uninstall` also removes its REA-owned Cline CLI entry.
For Cline's CLI, setup writes `mcpServers.rea` into
`~/.cline/data/settings/cline_mcp_settings.json`; set `CLINE_MCP_SETTINGS_PATH`
to select another file. Otherwise `CLINE_DATA_DIR` selects the data directory,
then `CLINE_DIR` selects the root containing `data`; blank overrides are ignored.
Setup and `mcp add --agent cline-cli` share this native path selection. Entries carry
`command`, `args`, and `env`; stdio is the default transport. Setup preserves
every other server and top-level key when it upserts `rea`, and refuses a
`rea` entry it did not write rather than adopting or overwriting it.
Existing nested `transport` registrations retain their representation during
setup. In that shape, launch `env` and `cwd` belong inside `transport`;
setup preserves those values and other nested metadata separately from outer
registration policy.
That refusal includes a hand-written `rea` entry: ownership is decided by
comparing the entry's launcher with the running installation's entry point, so a
registration that points at a different install path is reported as `foreign`
and left unchanged. Rename or remove that entry, then rerun setup; doctor's
remediation names the file and the entry to change.
Cline discovers skills in the shared `~/.agents/skills` directory. Setup
installs the bundled skill there,
and `--skill-client cline` resolves to that shared directory; setup does not
write a second copy under `~/.cline/skills`.
The IDE extension keeps its own MCP settings, reached from the Cline panel's
**Configure MCP Servers** action. Use `rea mcp add --agent cline` for that
registration; `rea setup --client cline` configures the CLI only.
Restart Cline after setup so the client reloads `mcpServers`. Cline's default
MCP tool-call timeout is 120 seconds; native provider work that spends longer
starting Hopper, Ghidra, or IDA can exceed it, so raise the timeout in Cline's
MCP settings for long native investigations.
Setup, doctor's alignment report, foreign-registration refusal, and uninstall
removal are covered by `tests/acceptance/setup/clineClientSetup.test.ts`.
## WorkBuddy
For [WorkBuddy](https://www.workbuddy.cn/docs/workbuddy/From-Beginner-to-Expert-Guide/Function-Description/MCP-Guide), setup writes `mcpServers.rea` into the
@@ -738,6 +812,13 @@ and top-level key when it upserts `rea`. WorkBuddy also reads a project-level
user-level file only. Loading this registration and real REA tool calls
through WorkBuddy are verified.
For CodeBuddy and WorkBuddy, REA's write checks cover their documented stdio
entries. If an existing `rea` row contains fields whose native interpretation is
unclassified, setup preserves it and reports unknown write applicability before
changing the file. Doctor retains any established launcher ownership while
reporting alignment as unknown. This does not prevent safe removal of a row
whose REA ownership is established.
## ZCode
For [ZCode](https://zcode.z.ai/en/docs/mcp-services), setup registers `rea` in the nested `mcp.servers` table of the
@@ -113,8 +113,8 @@ existing analysis families directly, for example
| `push_adb_file` | **mutates device** | One local file to a device path (e.g. pushing tools into `/data/local/tmp`); checks for existing targets and refuses them without explicit overwrite (the check is not atomic); compares digests when device `sha256sum` is available |
| `install_adb_package` | **mutates device** | Local APK via `adb install [-r]`; preserves its Success/Failure verdict, records `replace_requested`, and leaves actual replacement `null` because prior package state is not observed |
| `uninstall_adb_package` | **mutates device** | Requests package removal for user 0 via `adb shell pm uninstall --user 0`, without data retention |
| `start_adb_app` | **mutates device** | Resolves the package's launcher activity (`cmd package resolve-activity --brief`) and starts it (`am start -n`) |
| `start_adb_activity` | **mutates device** | Starts an intent (`am start -a` with optional URI, component, and up to 16 typed extras); preserves the activity manager's verdict |
| `start_adb_app` | **mutates device** | Resolves the package's launcher activity (`cmd package resolve-activity --brief`) and starts it (`am start -W -n`) |
| `start_adb_activity` | **mutates device** | Starts an intent (`am start -W -a` with optional URI, component, and up to 16 typed extras); preserves the activity manager's verdict |
| `stop_adb_app` | **mutates device** | Force-stops every process of a package, ending a dynamic-analysis run |
Screen capture validates the PNG signature, IHDR framing, dimensions, and IHDR
@@ -188,7 +188,7 @@ and during shutdown.
property value remains available in `properties`.
- **Device paths are not host paths.**`base_apk_device_path` values live on
the device filesystem. Only pulls and captures produce host-local paths,
each paired with byte size and SHA-256 digest. A pulled file's host name is
with byte size and SHA-256 digest on success. A pulled file's host name is
the device path's final POSIX segment. REA preserves the original device
path and filename; a missing final segment or `.`/`..` is refused before
output creation. Windows hosts follow [Windows filename rules](https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file),
@@ -249,6 +249,10 @@ and during shutdown.
- Pull and capture outputs are additive: the destination must not already
exist. Re-running a completed pull refuses the existing directory instead
of overwriting evidence.
- If a single-file pull fails and REA observes a regular file at the destination,
the error includes its `local_path` with `transfer_status: "unconfirmed"`
and `digest_status: "unknown"`. The file remains in place; the receipt does
not establish that the transfer completed.
- Cancellation during a package pull preserves caller-selected output. The
error includes partial observations identifying completed artifacts and the
interrupted local path; remaining APKs were not attempted.
- Dependencies load dependents-first in load-command order, and each image
loads once per process. A later @rpath request whose install name matches an
already loaded image reuses it.
already loaded image reuses it. Loading an image and expanding its dependencies
are separate: an already loaded sibling can first be expanded through another
dependency, inheriting that traversal's rpath stack. Cycles do not expand an
image twice, and lazy edges do not establish a launch-time traversal chain.
- Symlinks such as `Versions/Current` are followed inside the analyzed root.
An escape through `..` or an absolute link is `escapes-target`.
- Dyld path components are POSIX names. On Windows hosts, link text uses `\`
@@ -252,8 +255,12 @@ one of:
A `conditional` image is still traced, because it is an in-root candidate
that may load. Every edge below it has `loader_conditional: true`, and a
request that reuses it by install name is `conditional` too. An in-root dylib
with no platform load command remains unknown unless the root is an executable
request that reuses it by install name is `conditional` too. A definitely loaded
image can also first expand through a conditional chain: its dependency edges
then have `loader_conditional: true`, while later reuse of the image retains its
definite first-load result.
An in-root dylib with no platform load command remains unknown unless the root is an executable
with observed macOS platform and the candidate's CPU type is x86_64, i386, or arm64. In that macOS context,
[Apple's dyld](https://github.com/apple-oss-distributions/dyld/blob/fd8d0c4d52320ebf64db34f3cb280310d905c5ae/mach_o/UnsafeHeader.cpp#L1137-L1145) supplies an implicit macOS platform to legacy dylibs on those CPU
types. The candidate records `platform_basis: "macos-dyld-implicit"`; the
`file_bytes`, and `cpu_seconds`; positive safe integers apply on the next call
without restart. `input_bytes` overrides the launch default. File admission
follows selected `output_bytes` unless `file_bytes` is explicit. The separate
`pwntools_timeout_ms` controls each command wall deadline. On CLI pass
`--producer-limits '{"cpu_seconds":60,"output_bytes":134217728}'`. The
reply budget applies to the decoder record; the CLI/MCP Evidence envelope adds
copies and encoding overhead. The
pinned MCP SDK defaults to a 10 MiB receive buffer. Large complete results need
@@ -128,15 +139,16 @@ a caller-configured `StdioClientTransport({ maxBufferSize: ... })` and a matchin
server `REA_MCP_MAX_RESPONSE_BYTES`. See [MCP delivery budgets](../../reference/mcp-contracts.md#tool-results).
These settings affect delivery, not decoder coverage or limits. The large
extended-index verification uses 256 MiB and an explicit five-minute request
timeout in its separate opt-in MCP lane. REA does not silently truncate the result. Python lowers resource soft limits to at most 3 GiB virtual address space,
30 CPU seconds and 64 MiB file output. Inherited tighter soft/hard limits are
timeout in its separate opt-in MCP lane. REA does not silently truncate the result. Python lowers resource soft limits to the selected policy: defaults are 3 GiB
virtual address space, 30 CPU seconds and 64 MiB file output. Inherited tighter soft/hard limits are
retained, and the effective values are returned in the result limitations. Virtual address space is not RSS;
Unicorn needs a large virtual map. Limits fail with no partial success, and
owned process/root cleanup completes independently of cancellation.
Core files, GDB session/state/control, optional pwndbg enrichment and instruction
inspection are separate increments tracked by #969. This increment makes no
real support claim for those features or other operating systems/architectures.
For core files and optional pwndbg mapping context, use the
[recorded-crash workflow](recorded-crashes.md). The binary-layout operations
described here do not provide debugger session state or control, or establish
support for other operating systems/architectures.
Typed decoder failures retain bounded `captured_output` (stdout, stderr and an
explicit truncation flag) alongside the original failure category and reason.
@@ -272,6 +284,12 @@ regular files only and rejects symbolic links.
## Byte entropy triage
Entropy admission preserves the caller's spelling in `caller_input_path`. On
Windows, the selected regular file's case-only filesystem spelling is used
consistently in committed paths through both CLI and MCP. Symlink, junction and
short-name aliases are not replaced with a different path. General path
normalization remains lexical and does not probe source-discovered paths.
`inspect_byte_entropy` / `inspect-byte-entropy` profiles any local regular file
without an analysis engine, execution, extraction or network access.
@@ -465,6 +483,13 @@ section directory with neither a dynamic symbol table nor a dynamic segment
reports a complete empty inventory. Mach-O exports are reported by
`inspect_macho`.
ELF export tables marked `SHF_COMPRESSED` are not decompressed. A compressed
dynamic symbol table or its string table reports `unsupported` coverage rather
than treating stored bytes as symbols or names. Compressed version tables or
their string tables keep readable export rows with `partial` coverage and
unresolved versions. When version definition storage is compressed, requirement
names also remain unknown because unseen definitions may override them.
`max_file_bytes` defaults to 256 MiB (maximum 512 MiB; CLI `--max-file-bytes`).
Export directories in PE headers are read like those in sections. An export or
dynamic symbol table above 262144 entries, more than 262144 rows, or more than
These references establish the decoded layout, not runtime behavior.
Loaded 100 of 3797 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.