* refactor(providers): finalize apktool and shared artifact resources through runOwned
runOwned moves to the provider layer so provider sites can use it without depending on application workflows. It gains a completed-value projection (partial) for values that are not themselves partial observations, an optional signal, and cleanupLeases that bind a close failure to the exact leases whose confirmed release resolves it.
ArtifactAnalysisResources.finish is removed; TShark and sigrok finalize through runOwned with the shared release as close. Apktool's hand-written release, cancellation and lease-group composition is replaced by runOwned. A failed primary now keeps a cancellation observed during final cleanup as a secondary failure, as the documented precedence requires.
A counted architecture guard refuses new hand-written completed-result abort reclassification outside runOwned.
* refactor(adb): finalize runtime-root work through runOwned (#10660)
* refactor(adb): finalize runtime-root work through runOwned
The ADB runtime-root finalizer re-derived the precedence by hand and kept its own cancellation-with-cleanup combiner. It now runs through runOwned: the work body maps snapshot failures, close releases the root lease and binds its own cleanup error, and partial projects the completed execution. A cancellation whose cleanup fails is now the shared cleanup composite with the cancellation as its primary, as runOwned produces for every other owner.
* refactor(providers): keep completed values as diagnostics in runOwned and migrate four finalizers (#10691)
* refactor(providers): keep completed values as diagnostics in runOwned and migrate signature, Ghidra, bundle and LLDB finalization
* test(providers): run the lifecycle scenarios over every runOwned adopter shape (#10707)
One table of testing.md lifecycle rows runs over partial, diagnostics, publishing and lease adopters, replacing the hand-written precedence cases it covers.
* chore(lint): move runOwned's classifier baseline entry with the file
* fix(pcap): report a refused tool launch as an unavailable tool
When a capture read's tool launch was refused with EPERM, pcap reported it
as unreadable tool output (AnalysisProtocolError) instead of an unavailable
tool. Pcap also never told "not executable" apart from "missing".
Add classifyLaunchFailure in src/process as the one owner of which spawn
errnos mean missing (ENOENT, ENOTDIR) and which mean present but not
executable (EACCES, EPERM, ENOEXEC, EISDIR). Pcap, apktool and gdb used three
different errno lists; all three now use it.
* refactor(process): classify owned command outcomes through one owner
Add ProviderOutcome, read once from the supervisor's facts, and classifyProviderOutcome, the one mapping from an outcome to a typed error. Migrate pcap and the shared artifact producer command failure (TShark, sigrok). Add the rea/provider-failure-classifier lint rule with a shrink-only baseline of per-site (cause: unknown) => AnalysisError functions.
* refactor(process): classify Java and adb launch refusals with the shared owner (#10598)
The Ghidra and JADX Java probes and adb's version probe now take
missing-versus-not-executable from classifyLaunchFailure instead of their
own errno checks. ELOOP and ENAMETOOLONG join the owner's missing set, so
adb keeps its existing classification and both Java probes now treat a
non-directory path component as a missing runtime.
* fix(pcap): preserve command launch attribution
* test(pcap): expect an unknown failing stage when a limited capture tool exits unsuccessfully
* refactor(process): classify owned command outcomes through one owner
Add ProviderOutcome, read once from the supervisor's facts, and classifyProviderOutcome, the one mapping from an outcome to a typed error. Migrate pcap and the shared artifact producer command failure (TShark, sigrok). Add the rea/provider-failure-classifier lint rule with a shrink-only baseline of per-site (cause: unknown) => AnalysisError functions.
* test(lint): cover baselined files in the provider-failure classifier rule
* fix(apktool): report the decoder JVM's heap exhaustion as memory, not a target refusal
The supervisor now watches for a managed runtime's out-of-memory report while output streams (JVM java.lang.OutOfMemoryError, V8 'JavaScript heap out of memory'), so a retention budget cannot hide it. ProviderOutcome gains a runtime-exhausted kind and classifyProviderOutcome maps it once. Apktool decode opts in and moves to the shared classifier; Ghidra's single-purpose output token becomes the JVM runtime option and its area matching reuses the shared table.
* fix(process): keep the runtime's out-of-memory report on the memory error it supports
* fix(pcap): attribute a limited capture's exit through the prlimit wrapper
* fix(process): keep a shortened out-of-memory line marked as shortened
* fix(process): identify the refused executable for every launcher shape
Name the executable on errnos Node throws synchronously from spawn
(ENOEXEC, ENOTDIR, ...) and read the Windows native launcher's
executable-launch constraint, so pcap attributes those refusals to the
selected tool or limiter instead of an unknown stage.
* test(pcap): prove synchronous launch naming with ENOTDIR on every POSIX host
* fix(apktool): advise the JVM limit that governs the reported memory area
* fix(ghidra): read the supervisor's parsed OOM area instead of reparsing lines
* fix(sqlite,capture): read V8 heap exhaustion from streamed output, not retained stderr (#10724)
The SQLite worker and the Node capture decoders now declare runtime: "v8", so the supervisor observes V8's heap report as output streams. Both classifiers read the runtime-exhausted outcome instead of matching retained stderr, so a report beyond the retention budget, or one whose process then overflowed its output budget, is still reported as memory exhaustion.
* refactor(evm): classify EVMole worker outcomes through the shared provider classifier (#10716)
* refactor(evm): classify EVMole worker outcomes through the shared provider classifier
The EVMole worker's cancellation, deadline, output budget and CPU/file-size signals now go through classifyProviderOutcome; only the prlimit launch refusal and the reserved EFBIG exit remain in its dialect. The worker is opted into V8 exhaustion scanning so a heap report names its fixed 256 MiB heap. providerOutcomeOf now reads a launch refusal only from a spawn errno, so a private-storage or carrier read errno is no longer reported as a limiter launch failure.
* fix(evm): keep a stream failure's exit status and update Linux worker expectations
* refactor(process): read launch refusals through the launcher identity owner
* refactor(wasm): classify WABT command outcomes through the shared provider classifier (#10727)
* refactor(wasm): classify WABT command outcomes through the shared provider classifier
WABT's cancellation, command deadline, output budget, and inherited-limit signals now go through classifyProviderOutcome; its dialect keeps only the configuration-probe and launch-refusal unavailability and the unclassified-exit output error. A tool ended by SIGXCPU/SIGXFSZ or the memory limit is now a resource constraint instead of an output error, and a refused launch in the producer phase is reported as unavailable. The shared timeout context gains the analysis setting that selected the deadline, and the output-limit context may name its exceeded budget.
* fix(wasm): keep setup guidance when a WABT version probe's output cannot be collected
* refactor(providers): classify raw provider failures through one owner
* refactor(process): build classified cancellations with cancelledBy and the carried caller reason
* chore(lint): count runOwned's close-failure mapper from main in the classifier baseline
* fix(process): carry the runtime's exhaustion report on every memory error that claims it
SQLite and historical-capture memory errors recognised a streamed V8 heap report but dropped it, so a report outside retained output reached callers without its source line or omission flags. One projection, runtimeExhaustionReport, now owns that shape for the shared classifier and both consumers. Also lowers the EVMole and WABT cancellation-constructor baselines.
* fix(process): keep the provider's recovery for a native-thread exhaustion report
The shared classifier replaced the provider-supplied recovery with generic capacity advice whenever the JVM reported it could not create a native thread, dropping Apktool's launcher guidance and flipping retryable to true. The provider's recovery now wins; the capacity advice remains the fallback for providers that supply none.
* refactor(pwntools): classify pwntools failures through the shared provider classifier (#10782)
* refactor(pwntools): classify pwntools failures through the shared provider classifier
pwntools' per-site classifier is replaced by pwntoolsFailureDialect for classifyProviderFailure. Command outcomes go through classifyProviderOutcome, launch refusals through launcherRefusalOf instead of a 'spawn ' syscall prefix check, the input limit through the file dialect's fileSize, and the cleanup report through commandCleanup. The shared read cancellation now carries the reader's partial observation, and resource-signal reports accept provider detail.
* fix(pwntools): report a failed command's output once and expect resource_constraint for overflow
The shared outcome owner already attaches the command's output as capturedOutput, so the pwntools adapter fallback no longer copies the same text into diagnostics.stdout, diagnostics.stderr and diagnostics.captured_output; diagnostics keep the exit facts. Update the overflow expectations the dialect migration missed (layout provider cleanup and over-budget rows, pwntools cleanup) to resource_constraint.
* refactor(wasm): leave WABT cancellation to the shared owners
WabtFailures matched any Error named AbortError as a cancellation. No WABT path produces one: reads, path checks and owned commands report cancellation as ArtifactReaderFailure or OwnedCommandFailure, which classifyProviderFailure and classifyProviderOutcome already classify with the caller's reason. The provider's two signal checks now build the cancellation with cancelledBy. Lowers the AnalysisCancelledError constructor baseline for both WABT files to zero.
* fix(wasm): report a cancelled snapshot write as the caller's cancellation
The private snapshot write passed the caller's signal to Node's writeFile, whose abort envelope reached the dialect's unclassified output failure once the name-based AbortError guess was removed. The write now turns the envelope carrying its own signal's reason into cancelledBy at the producer.
* refactor(firmware): classify firmware failures through the shared provider classifier (#10810)
Firmware producers now report their own deadline expiry: the start check, the output inventory and launcher admission throw the phase timeout where they observe the abort, and a read the phase signal cancelled reports it through the dialect's interruption. The classifier no longer rereads a cancellation's reason as a timeout. A new readCleanup hook keeps firmware's cleanup report shape.
* refactor(platform): give the host platform one owner and ratchet ambient process.platform reads
* refactor(process): pass the host platform to process sampling instead of reading it ambiently
#10755 added two process.platform reads (ps selection in process sampling and the
desktop build doctor's ps fact) after the host-platform ratchet was measured.
The desktop fact already receives the host platform from doctor; process sampling
now receives it from the capture harness, removing all five ambient reads there.
* refactor(errors): give caller aborts a typed reason at the adapters
Squashes 908813551f, 87858de4dc (fix(mcp): preserve cancellation reasons in handlers) and fa151dc3f6 (test(server): race handler-entry gates against the triggering tool call) onto main without #10566's pre-squash commits.
* refactor(errors): give owner-initiated aborts a typed reason outside the browser adapters (#10692)
* refactor(errors): give owner-initiated aborts a typed reason outside the browser adapters
* refactor(verify): keep the abort-reason scan receiver-independent
Playwright's route.abort takes a network error code; the one call site now names its default code instead of the guard exempting a variable name.
* refactor(browser): give owner-initiated CDP aborts typed reasons (#10706)
* refactor(browser): give owner-initiated CDP aborts typed reasons
* fix(browser): give the page-side request fetch page-native abort reasons
The request callback runs in the page through page.evaluate and cannot
reach REA's modules. Define its deadline and cleanup reasons as
DOMExceptions inside the callback, and make the test fake evaluate the
callback from its source so module imports are unavailable, as in a page.
* test(process): expect the reader close's typed owner reason
Closing the Darwin run-token reader aborts its compilation with OwnerCancellation.ownerClosed since the owner-abort change in this PR; the boundary test still expected the default DOMException.
* fix(server): report HTTP session replacement and shutdown to pending tools as an owner close
* refactor(errors): read cleanup completion from dependencies instead of pushing it to dependents
AnalysisError.cleanupIncomplete becomes a getter over the error's own unresolved state and its bound dependencies. The WeakRef dependents graph and resolveCleanup's push propagation are removed, and subclasses mark their initial state through markCleanupIncomplete instead of overriding a public mutable field.
* fix(errors): settle bound dependencies before binding more
A composite whose dependencies had released but whose status was never read
stayed incomplete when a later dependency was bound.
* refactor(errors): let errors read a lease group's release instead of resolving them (#10759)
AnalysisCleanupLeaseGroup no longer calls resolveCleanup() on its error.
The error holds the group as a release claim and reads cleanupReleased when
asked, so completion follows the lease owner without a push.
* chore(verify): count resolveCleanup and bindCleanupDependencies calls so they only shrink (#10763)
* refactor(errors): let errors read a lease group's release instead of resolving them
AnalysisCleanupLeaseGroup no longer calls resolveCleanup() on its error.
The error holds the group as a release claim and reads cleanupReleased when
asked, so completion follows the lease owner without a push.
* chore(verify): count resolveCleanup and bindCleanupDependencies calls so they only shrink
* refactor(process): let worker cleanup errors read their root epoch's release
OwnedWorkerRuntime's root epoch is now an AnalysisCleanupReleaseClaim. Errors bound to it read its release when asked instead of the epoch pushing resolveCleanup() through weak references, and a runtime without a retained root binds an already-released owner. Removes the OwnedWorkerCleanupFailure structural type and four resolveCleanup() pushes; the counted baseline shrinks accordingly.
* refactor(pcap): let pcap cleanup errors read command and snapshot release (#10792)
Retained pcap commands, retained snapshot failures and snapshot completion descriptors now own their release and errors read it: commands and snapshot failures implement AnalysisCleanupReleaseClaim, and a failed completion-descriptor close is retried through an AnalysisCleanupLease bound to its error. Removes four resolveCleanup() pushes; the counted baseline shrinks accordingly.
* refactor(process): let Darwin helper cleanup errors read their owner's release
Darwin helper cache and run-token reader root cleanup now record an AnalysisCleanupRelease that every failure reports; a successful retry confirms it. Replaces the first-failure anchor that later failures depended on and the resolveCleanup() push. Removes two resolveCleanup() and two bindCleanupDependencies() calls; both counted baselines shrink.
* refactor(ghidra): let Ghidra cleanup errors read the client's and checkpoint's release (#10802)
GhidraClient and GhidraCheckpoint bind each cleanup failure to an AnalysisCleanupRelease and confirm it when a later close or retry releases everything, instead of keeping a set of failures and pushing resolveCleanup() to each. Removes two resolveCleanup() calls; the counted baseline shrinks.
* refactor(native): read retained inventory views through the operation fact contracts
* refactor(native): read matching inventories through the operation fact contracts (#10887)
* refactor(native): read dispatch symbol fallbacks as lenient name facts at the port (#10889)
* refactor(native): read dispatch symbol fallbacks as lenient name facts at the port
* test(native): accept undeclared name-row fields in both dispatch consumers
* refactor(native): read literal searches and overview counts as operation facts (#10894)
* refactor(native): read literal searches and overview counts as operation facts
* refactor(native): read addressed inventory rows as operation facts in every reader (#10899)
* refactor(native): read addressed inventory rows as operation facts in every reader
* test(native): cover xrefs name resolution through the name fact contract
* test(server): pin procedure completion to the procedure inventory contract
* refactor(domain): one owner for the non-array record predicate
Thirty-four source files each declared isRecord as a non-null object that is
not an array, and six more spelled the same predicate as isObject,
isSchemaRecord or isStorageRecord. They now import isRecord from
src/domain/isRecord.ts, whose overloads keep JsonValue narrowing for the
desktop and environment readers. DshCordisPatch's predicate also excludes
Cordis expressions, so it is renamed isPatchRecord.
architecture-guards refuses a new local copy under any name. The seven files
that still hold one are listed and must leave the list when their copy goes.
* fix(guards): count record predicate copies precisely
* test(hopper): retry after confirmed application exit
* fix: reject stale record predicate exceptions
* refactor(errors): require a reason for every analysis cancellation
Add cancelledBy, the one constructor for AnalysisCancelledError. Its
reason argument is required: an aborted signal, an earlier cancellation,
a reason carried from an inner failure, or an OwnerCancellation when REA
closed the work's owner itself.
Closed adb, apktool, firmware, sqlite, gdb, web-bundle analyzer and
binary-session record owners now report OwnerCancellation.ownerClosed
instead of an absent reason. Firmware's predecessor wait returns its
cancellation as a Result, removing an unreachable reason-less fallback.
An architecture guard rejects new direct constructions, with a counted
per-file baseline that can only shrink.
* refactor(errors): share one aborted-signal predicate with cancelledBy
* refactor(process): return a departing caller's reason from a shared startup wait
* refactor(providers): build decompiler-provider cancellations with a required reason (#10587)
Migrate every AnalysisCancelledError construction under src/ghidra,
src/ida, src/cutter, src/jeb and src/kuna to cancelledBy and lower the
guard baseline from 671 to 572. Closed Kuna target commands report
OwnerCancellation.ownerClosed instead of an absent reason, and Kuna and
JEB close abort their lifetimes with that typed reason.
* refactor(process): build infrastructure cancellations with a required reason
Migrate every AnalysisCancelledError construction in shared process,
provider, artifact, domain, server and CLI code and in the smaller
providers to cancelledBy, lowering the guard baseline from 671 to 566.
Option bags that carried a cause are split so the cancellation branch
cannot receive one.
* fix(errors): read the reason of any signal-shaped cancellation source
* test(ci): cover the counted-baseline comparison behind the architecture guard
* refactor(native): select ELF sections through the keyed owner
* test(native): pin returned exports and versions for repeated version sections
* refactor(nativeaot): read neither of repeated frozen object regions (#10790)
* refactor(nativeaot): read neither of repeated frozen object regions through the keyed owner
* refactor(parsers): retire every first-match section lookup (#10793)
* fix(nativeaot): report an ambiguous frozen object region as partial (#10812)
* refactor(native): select program headers and load commands through the keyed owner (#10827)
* refactor(native): select the GOT base and dependency segments through the keyed owner (#10847)
* refactor(native): select the GOT base and dependency segments through the keyed owner
resolveGotBase selects the SHT_DYNAMIC section and the allocated .got.plt/.got sections with uniqueKeyed, so a repeat leaves the GOT base unknown instead of taking the first match; elfDependencies reads PT_LOAD segments through the same keyed program table as PT_DYNAMIC. A new keyed-first-match guard recognizes a find/findLast or filter-then-[0] whose predicate compares a key field, whatever the table is called; existing matches are baselined. The unwind fixture can declare PT_DYNAMIC headers and repeated .dynamic/.got.plt sections.
* test(native): pin the PT_DYNAMIC path and destructured keyed lookups
The keyed-first-match guard moves to scripts/verify/keyed-first-match.mjs with focused tests, and now recognizes a destructured key in any position and a destructured filter predicate. The single PT_DYNAMIC unwind test declares .dynamic as SHT_PROGBITS so only the program header supplies DT_PLTGOT, and asserts the resolved ranges.
* fix(macho): refuse repeated chained-fixup and dyld-info tables (#10850)
parseMachoLayout kept the last LC_DYLD_CHAINED_FIXUPS range and merged the rebase/bind streams of every LC_DYLD_INFO(_ONLY) command. A repeat now refuses the slice as malformed. NativeMachoInspection's chained import-name reader already declined repeated LC_DYLD_CHAINED_FIXUPS; that rule is now covered by a test.
* fix(verify): count a filter index only on the filter's own result
* refactor(errors): read cleanup completion from dependencies instead of pushing it to dependents
AnalysisError.cleanupIncomplete becomes a getter over the error's own unresolved state and its bound dependencies. The WeakRef dependents graph and resolveCleanup's push propagation are removed, and subclasses mark their initial state through markCleanupIncomplete instead of overriding a public mutable field.
* fix(errors): settle bound dependencies before binding more
A composite whose dependencies had released but whose status was never read
stayed incomplete when a later dependency was bound.
* refactor(errors): let errors read a lease group's release instead of resolving them (#10759)
AnalysisCleanupLeaseGroup no longer calls resolveCleanup() on its error.
The error holds the group as a release claim and reads cleanupReleased when
asked, so completion follows the lease owner without a push.
* chore(verify): count resolveCleanup and bindCleanupDependencies calls so they only shrink (#10763)
* refactor(errors): let errors read a lease group's release instead of resolving them
AnalysisCleanupLeaseGroup no longer calls resolveCleanup() on its error.
The error holds the group as a release claim and reads cleanupReleased when
asked, so completion follows the lease owner without a push.
* chore(verify): count resolveCleanup and bindCleanupDependencies calls so they only shrink
* refactor(process): let worker cleanup errors read their root epoch's release
OwnedWorkerRuntime's root epoch is now an AnalysisCleanupReleaseClaim. Errors bound to it read its release when asked instead of the epoch pushing resolveCleanup() through weak references, and a runtime without a retained root binds an already-released owner. Removes the OwnedWorkerCleanupFailure structural type and four resolveCleanup() pushes; the counted baseline shrinks accordingly.
* refactor(pcap): let pcap cleanup errors read command and snapshot release (#10792)
Retained pcap commands, retained snapshot failures and snapshot completion descriptors now own their release and errors read it: commands and snapshot failures implement AnalysisCleanupReleaseClaim, and a failed completion-descriptor close is retried through an AnalysisCleanupLease bound to its error. Removes four resolveCleanup() pushes; the counted baseline shrinks accordingly.
* refactor(gdb): parse MI result records with one typed record parser
GdbSessionManager recognized ^done with a prefix regex at seven sites, kept a hand-written span scanner and a second character scanner for -list-thread-groups, and decoded C-string fields with a dynamic RegExp. src/gdb/GdbMiRecord.ts now parses a result record into its token, class and typed results (C-string bytes, tuples, value lists and result lists), and returns a typed malformed-mi-record failure with a reason and offset. The done checks, the thread-group PID inventory and the C-string field reader use it; the stream dispatcher reads only the record header through the same module. An architecture guard keeps MI result-record regexes in the owner.
* perf(gdb): encode each unescaped C-string run once
The MI C-string decoder allocated a Buffer per unescaped character, pushed every byte into a number array, and copied the rest of the line for each hex or octal escape. It now validates unescaped runs in place, encodes each run once, reads escapes with sticky patterns, and joins the chunks once. On a 12.1 MiB groups reply: 887/832 ms elapsed and 818/775 ms CPU before, 76/76 ms elapsed and 131/128 ms CPU after; peak RSS 152-170 MiB both.
* test(gdb): refuse a malformed ^done for a startup setting through a real session
A fake GDB answers -gdb-set auto-load off with ^done,msg="unterminated. The session must not start: the typed MI record parser refuses the record, where main's ^done prefix match accepted it (the case fails against main's GdbSessionManager).
* feat(gdb): record the GDB version from -gdb-version in session Evidence (#10865)
* feat(gdb): record the GDB version from -gdb-version in session Evidence
Startup now asks -gdb-version before the auto-load setting and reads its first console stream record through a new MI stream-record parser (parseGdbMiStreamRecord). Each session's Evidence names that version; an unrecognised banner, malformed record or refused query leaves it null (unknown), as before.
Fake GDB fixtures answer -gdb-version from one owner in gdbTestHelpers: a stdin preamble for scripted fakes and answerFakeGdbStartup for in-process fakes, so scenarios model only their own commands.
* fix(gdb): leave a lossily decoded version unknown and frame fake startup replies by line
* test(gdb): reject unmodeled commands in the lossy version banner fixture
* test(gdb): refuse commands the scripted fake GDBs do not model (#10947)
* test(gdb): refuse commands the scripted fake GDBs do not model
* test(gdb): match the migrated fakes' special commands exactly
* refactor(python): read pyc and PYZ marshal values through one shared reader
* refactor(python): read pyc 64-bit values and header fields through shared readers (#10834)
* refactor(limits): declare the Node timer bound and duration schema once
Fifteen modules re-declared 2_147_483_647 as their own timer maximum and
eleven built the same 1..2^31-1 millisecond schema. They now use
MAX_NODE_TIMER_DELAY_MS and timerDurationMsSchema from
src/domain/analysisResourceLimits.ts, and an architecture guard rejects the
literal outside that owner and five non-timer int32 bounds. The generated
MCP tool catalog is byte-identical.
* refactor(limits): keep the JVM processor-count bound separate from the timer bound
REA_JADX_ACTIVE_PROCESSOR_COUNT is a Java int count, not a timer delay, so it keeps its own named bound and a documented exception in the signed 32-bit guard. Remove the timer-bound and zod imports the duration-schema migration left unused.
* refactor(limits): keep the TShark packet-count bound separate from the timer bound
packet_limit is passed to TShark's -c as a C int packet count, not a timer delay, so it keeps its own named bound and a documented exception in the signed 32-bit guard.
* fix(pcap): report a refused tool launch as an unavailable tool
When a capture read's tool launch was refused with EPERM, pcap reported it
as unreadable tool output (AnalysisProtocolError) instead of an unavailable
tool. Pcap also never told "not executable" apart from "missing".
Add classifyLaunchFailure in src/process as the one owner of which spawn
errnos mean missing (ENOENT, ENOTDIR) and which mean present but not
executable (EACCES, EPERM, ENOEXEC, EISDIR). Pcap, apktool and gdb used three
different errno lists; all three now use it.
* refactor(process): classify owned command outcomes through one owner
Add ProviderOutcome, read once from the supervisor's facts, and classifyProviderOutcome, the one mapping from an outcome to a typed error. Migrate pcap and the shared artifact producer command failure (TShark, sigrok). Add the rea/provider-failure-classifier lint rule with a shrink-only baseline of per-site (cause: unknown) => AnalysisError functions.
* refactor(process): classify Java and adb launch refusals with the shared owner (#10598)
The Ghidra and JADX Java probes and adb's version probe now take
missing-versus-not-executable from classifyLaunchFailure instead of their
own errno checks. ELOOP and ENAMETOOLONG join the owner's missing set, so
adb keeps its existing classification and both Java probes now treat a
non-directory path component as a missing runtime.
* fix(pcap): preserve command launch attribution
* test(pcap): expect an unknown failing stage when a limited capture tool exits unsuccessfully
* refactor(process): classify owned command outcomes through one owner
Add ProviderOutcome, read once from the supervisor's facts, and classifyProviderOutcome, the one mapping from an outcome to a typed error. Migrate pcap and the shared artifact producer command failure (TShark, sigrok). Add the rea/provider-failure-classifier lint rule with a shrink-only baseline of per-site (cause: unknown) => AnalysisError functions.
* test(lint): cover baselined files in the provider-failure classifier rule
* fix(apktool): report the decoder JVM's heap exhaustion as memory, not a target refusal
The supervisor now watches for a managed runtime's out-of-memory report while output streams (JVM java.lang.OutOfMemoryError, V8 'JavaScript heap out of memory'), so a retention budget cannot hide it. ProviderOutcome gains a runtime-exhausted kind and classifyProviderOutcome maps it once. Apktool decode opts in and moves to the shared classifier; Ghidra's single-purpose output token becomes the JVM runtime option and its area matching reuses the shared table.
* fix(process): keep the runtime's out-of-memory report on the memory error it supports
* fix(pcap): attribute a limited capture's exit through the prlimit wrapper
* fix(process): keep a shortened out-of-memory line marked as shortened
* fix(process): identify the refused executable for every launcher shape
Name the executable on errnos Node throws synchronously from spawn
(ENOEXEC, ENOTDIR, ...) and read the Windows native launcher's
executable-launch constraint, so pcap attributes those refusals to the
selected tool or limiter instead of an unknown stage.
* test(pcap): prove synchronous launch naming with ENOTDIR on every POSIX host
* fix(apktool): advise the JVM limit that governs the reported memory area
* fix(ghidra): read the supervisor's parsed OOM area instead of reparsing lines
* fix(sqlite,capture): read V8 heap exhaustion from streamed output, not retained stderr (#10724)
The SQLite worker and the Node capture decoders now declare runtime: "v8", so the supervisor observes V8's heap report as output streams. Both classifiers read the runtime-exhausted outcome instead of matching retained stderr, so a report beyond the retention budget, or one whose process then overflowed its output budget, is still reported as memory exhaustion.
* refactor(evm): classify EVMole worker outcomes through the shared provider classifier (#10716)
* refactor(evm): classify EVMole worker outcomes through the shared provider classifier
The EVMole worker's cancellation, deadline, output budget and CPU/file-size signals now go through classifyProviderOutcome; only the prlimit launch refusal and the reserved EFBIG exit remain in its dialect. The worker is opted into V8 exhaustion scanning so a heap report names its fixed 256 MiB heap. providerOutcomeOf now reads a launch refusal only from a spawn errno, so a private-storage or carrier read errno is no longer reported as a limiter launch failure.
* fix(evm): keep a stream failure's exit status and update Linux worker expectations
* refactor(process): read launch refusals through the launcher identity owner
* refactor(wasm): classify WABT command outcomes through the shared provider classifier (#10727)
* refactor(wasm): classify WABT command outcomes through the shared provider classifier
WABT's cancellation, command deadline, output budget, and inherited-limit signals now go through classifyProviderOutcome; its dialect keeps only the configuration-probe and launch-refusal unavailability and the unclassified-exit output error. A tool ended by SIGXCPU/SIGXFSZ or the memory limit is now a resource constraint instead of an output error, and a refused launch in the producer phase is reported as unavailable. The shared timeout context gains the analysis setting that selected the deadline, and the output-limit context may name its exceeded budget.
* fix(wasm): keep setup guidance when a WABT version probe's output cannot be collected
* refactor(providers): classify raw provider failures through one owner
* refactor(process): build classified cancellations with cancelledBy and the carried caller reason
* chore(lint): count runOwned's close-failure mapper from main in the classifier baseline
* fix(process): carry the runtime's exhaustion report on every memory error that claims it
SQLite and historical-capture memory errors recognised a streamed V8 heap report but dropped it, so a report outside retained output reached callers without its source line or omission flags. One projection, runtimeExhaustionReport, now owns that shape for the shared classifier and both consumers. Also lowers the EVMole and WABT cancellation-constructor baselines.
* fix(process): keep the provider's recovery for a native-thread exhaustion report
The shared classifier replaced the provider-supplied recovery with generic capacity advice whenever the JVM reported it could not create a native thread, dropping Apktool's launcher guidance and flipping retryable to true. The provider's recovery now wins; the capacity advice remains the fallback for providers that supply none.
* refactor(pwntools): classify pwntools failures through the shared provider classifier (#10782)
* refactor(pwntools): classify pwntools failures through the shared provider classifier
pwntools' per-site classifier is replaced by pwntoolsFailureDialect for classifyProviderFailure. Command outcomes go through classifyProviderOutcome, launch refusals through launcherRefusalOf instead of a 'spawn ' syscall prefix check, the input limit through the file dialect's fileSize, and the cleanup report through commandCleanup. The shared read cancellation now carries the reader's partial observation, and resource-signal reports accept provider detail.
* fix(pwntools): report a failed command's output once and expect resource_constraint for overflow
The shared outcome owner already attaches the command's output as capturedOutput, so the pwntools adapter fallback no longer copies the same text into diagnostics.stdout, diagnostics.stderr and diagnostics.captured_output; diagnostics keep the exit facts. Update the overflow expectations the dialect migration missed (layout provider cleanup and over-budget rows, pwntools cleanup) to resource_constraint.
* refactor(pcap): read tshark JSON packets through one envelope owner
PcapOutput and the packet-capture reader each decoded tshark -T json and checked the packet envelope. The capture reader let SyntaxError escape and accepted any shape until its field schema ran. src/pcap/TsharkJsonPackets.ts now owns the envelope (malformed JSON, the packet array, each packet's _source.layers) as typed failures, and the pcap byte and expansion budget. Both consumers map those failures to their existing error kinds. An architecture guard keeps _source.layers reads in the owner.
* fix(captures): keep producer output on a refused packet reply
parsePacketOutput threw AnalysisOutputError itself, so the provider's failure mapping returned it without the captured stdout and stderr, both for envelope refusals and for unordered or out-of-limit frames. It now returns a Result with the reason and cause, and the provider builds the AnalysisOutputError with capturedOutput.
* feat(captures): bound packet-capture JSON expansion by an adjustable setting
The capture path parsed TShark's selected-field JSON without the expansion
estimate the pcap reader charges. It now reads through readTsharkJsonPackets
under capture.packet_json_expansion_bytes (REA_CAPTURE_PACKET_JSON_EXPANSION_BYTES).
The estimate charges at most 130 bytes per code unit and the output is one
Node string, so no admissible output reaches 69,793,215,440 bytes; the default
and maximum are 128 GiB, so the budget refuses only when lowered. A refusal is
an adjustable_budget resource constraint naming the setting and keeps the
producer output; a malformed launch seed is refused before the capture is read.
* refactor(errors): read cleanup completion from dependencies instead of pushing it to dependents
AnalysisError.cleanupIncomplete becomes a getter over the error's own unresolved state and its bound dependencies. The WeakRef dependents graph and resolveCleanup's push propagation are removed, and subclasses mark their initial state through markCleanupIncomplete instead of overriding a public mutable field.
* fix(errors): settle bound dependencies before binding more
A composite whose dependencies had released but whose status was never read
stayed incomplete when a later dependency was bound.
* refactor(errors): let errors read a lease group's release instead of resolving them (#10759)
AnalysisCleanupLeaseGroup no longer calls resolveCleanup() on its error.
The error holds the group as a release claim and reads cleanupReleased when
asked, so completion follows the lease owner without a push.
* chore(verify): count resolveCleanup and bindCleanupDependencies calls so they only shrink (#10763)
* refactor(errors): let errors read a lease group's release instead of resolving them
AnalysisCleanupLeaseGroup no longer calls resolveCleanup() on its error.
The error holds the group as a release claim and reads cleanupReleased when
asked, so completion follows the lease owner without a push.
* chore(verify): count resolveCleanup and bindCleanupDependencies calls so they only shrink
* fix(pcap): report a refused tool launch as an unavailable tool
When a capture read's tool launch was refused with EPERM, pcap reported it
as unreadable tool output (AnalysisProtocolError) instead of an unavailable
tool. Pcap also never told "not executable" apart from "missing".
Add classifyLaunchFailure in src/process as the one owner of which spawn
errnos mean missing (ENOENT, ENOTDIR) and which mean present but not
executable (EACCES, EPERM, ENOEXEC, EISDIR). Pcap, apktool and gdb used three
different errno lists; all three now use it.
* refactor(process): classify owned command outcomes through one owner
Add ProviderOutcome, read once from the supervisor's facts, and classifyProviderOutcome, the one mapping from an outcome to a typed error. Migrate pcap and the shared artifact producer command failure (TShark, sigrok). Add the rea/provider-failure-classifier lint rule with a shrink-only baseline of per-site (cause: unknown) => AnalysisError functions.
* refactor(process): classify Java and adb launch refusals with the shared owner (#10598)
The Ghidra and JADX Java probes and adb's version probe now take
missing-versus-not-executable from classifyLaunchFailure instead of their
own errno checks. ELOOP and ENAMETOOLONG join the owner's missing set, so
adb keeps its existing classification and both Java probes now treat a
non-directory path component as a missing runtime.
* fix(pcap): preserve command launch attribution
* test(pcap): expect an unknown failing stage when a limited capture tool exits unsuccessfully
* refactor(process): classify owned command outcomes through one owner
Add ProviderOutcome, read once from the supervisor's facts, and classifyProviderOutcome, the one mapping from an outcome to a typed error. Migrate pcap and the shared artifact producer command failure (TShark, sigrok). Add the rea/provider-failure-classifier lint rule with a shrink-only baseline of per-site (cause: unknown) => AnalysisError functions.
* test(lint): cover baselined files in the provider-failure classifier rule
* fix(apktool): report the decoder JVM's heap exhaustion as memory, not a target refusal
The supervisor now watches for a managed runtime's out-of-memory report while output streams (JVM java.lang.OutOfMemoryError, V8 'JavaScript heap out of memory'), so a retention budget cannot hide it. ProviderOutcome gains a runtime-exhausted kind and classifyProviderOutcome maps it once. Apktool decode opts in and moves to the shared classifier; Ghidra's single-purpose output token becomes the JVM runtime option and its area matching reuses the shared table.
* fix(process): keep the runtime's out-of-memory report on the memory error it supports
* fix(pcap): attribute a limited capture's exit through the prlimit wrapper
* fix(process): keep a shortened out-of-memory line marked as shortened
* fix(process): identify the refused executable for every launcher shape
Name the executable on errnos Node throws synchronously from spawn
(ENOEXEC, ENOTDIR, ...) and read the Windows native launcher's
executable-launch constraint, so pcap attributes those refusals to the
selected tool or limiter instead of an unknown stage.
* test(pcap): prove synchronous launch naming with ENOTDIR on every POSIX host
* fix(apktool): advise the JVM limit that governs the reported memory area
* fix(ghidra): read the supervisor's parsed OOM area instead of reparsing lines
* fix(sqlite,capture): read V8 heap exhaustion from streamed output, not retained stderr (#10724)
The SQLite worker and the Node capture decoders now declare runtime: "v8", so the supervisor observes V8's heap report as output streams. Both classifiers read the runtime-exhausted outcome instead of matching retained stderr, so a report beyond the retention budget, or one whose process then overflowed its output budget, is still reported as memory exhaustion.
* refactor(evm): classify EVMole worker outcomes through the shared provider classifier (#10716)
* refactor(evm): classify EVMole worker outcomes through the shared provider classifier
The EVMole worker's cancellation, deadline, output budget and CPU/file-size signals now go through classifyProviderOutcome; only the prlimit launch refusal and the reserved EFBIG exit remain in its dialect. The worker is opted into V8 exhaustion scanning so a heap report names its fixed 256 MiB heap. providerOutcomeOf now reads a launch refusal only from a spawn errno, so a private-storage or carrier read errno is no longer reported as a limiter launch failure.
* fix(evm): keep a stream failure's exit status and update Linux worker expectations
* refactor(process): read launch refusals through the launcher identity owner
* refactor(wasm): classify WABT command outcomes through the shared provider classifier (#10727)
* refactor(wasm): classify WABT command outcomes through the shared provider classifier
WABT's cancellation, command deadline, output budget, and inherited-limit signals now go through classifyProviderOutcome; its dialect keeps only the configuration-probe and launch-refusal unavailability and the unclassified-exit output error. A tool ended by SIGXCPU/SIGXFSZ or the memory limit is now a resource constraint instead of an output error, and a refused launch in the producer phase is reported as unavailable. The shared timeout context gains the analysis setting that selected the deadline, and the output-limit context may name its exceeded budget.
* fix(wasm): keep setup guidance when a WABT version probe's output cannot be collected
* refactor(process): build classified cancellations with cancelledBy and the carried caller reason
* chore(lint): count runOwned's close-failure mapper from main in the classifier baseline
* fix(process): carry the runtime's exhaustion report on every memory error that claims it
SQLite and historical-capture memory errors recognised a streamed V8 heap report but dropped it, so a report outside retained output reached callers without its source line or omission flags. One projection, runtimeExhaustionReport, now owns that shape for the shared classifier and both consumers. Also lowers the EVMole and WABT cancellation-constructor baselines.
* fix(process): keep the provider's recovery for a native-thread exhaustion report
The shared classifier replaced the provider-supplied recovery with generic capacity advice whenever the JVM reported it could not create a native thread, dropping Apktool's launcher guidance and flipping retryable to true. The provider's recovery now wins; the capacity advice remains the fallback for providers that supply none.
* fix(pcap): report a refused tool launch as an unavailable tool
When a capture read's tool launch was refused with EPERM, pcap reported it
as unreadable tool output (AnalysisProtocolError) instead of an unavailable
tool. Pcap also never told "not executable" apart from "missing".
Add classifyLaunchFailure in src/process as the one owner of which spawn
errnos mean missing (ENOENT, ENOTDIR) and which mean present but not
executable (EACCES, EPERM, ENOEXEC, EISDIR). Pcap, apktool and gdb used three
different errno lists; all three now use it.
* refactor(process): classify owned command outcomes through one owner
Add ProviderOutcome, read once from the supervisor's facts, and classifyProviderOutcome, the one mapping from an outcome to a typed error. Migrate pcap and the shared artifact producer command failure (TShark, sigrok). Add the rea/provider-failure-classifier lint rule with a shrink-only baseline of per-site (cause: unknown) => AnalysisError functions.
* refactor(process): classify Java and adb launch refusals with the shared owner (#10598)
The Ghidra and JADX Java probes and adb's version probe now take
missing-versus-not-executable from classifyLaunchFailure instead of their
own errno checks. ELOOP and ENAMETOOLONG join the owner's missing set, so
adb keeps its existing classification and both Java probes now treat a
non-directory path component as a missing runtime.
* fix(pcap): preserve command launch attribution
* test(pcap): expect an unknown failing stage when a limited capture tool exits unsuccessfully
* refactor(process): classify owned command outcomes through one owner
Add ProviderOutcome, read once from the supervisor's facts, and classifyProviderOutcome, the one mapping from an outcome to a typed error. Migrate pcap and the shared artifact producer command failure (TShark, sigrok). Add the rea/provider-failure-classifier lint rule with a shrink-only baseline of per-site (cause: unknown) => AnalysisError functions.
* test(lint): cover baselined files in the provider-failure classifier rule
* fix(pcap): attribute a limited capture's exit through the prlimit wrapper
* fix(process): identify the refused executable for every launcher shape
Name the executable on errnos Node throws synchronously from spawn
(ENOEXEC, ENOTDIR, ...) and read the Windows native launcher's
executable-launch constraint, so pcap attributes those refusals to the
selected tool or limiter instead of an unknown stage.
* test(pcap): prove synchronous launch naming with ENOTDIR on every POSIX host
* refactor(process): build classified cancellations with cancelledBy and the carried caller reason
* chore(lint): count runOwned's close-failure mapper from main in the classifier baseline
* refactor(process): own deadlines as one monotonic type with a finalize budget
Deadline (src/domain/deadline) holds a monotonic work budget and a separate finalize budget that starts when finalization begins. ProviderStartupDeadline is built on it and exposes finalize(), so a failed Ghidra startup drains launcher output under its own budget and the caller's signal instead of the expired startup signal. The six wall-clock deadline sums in src move onto Deadline; a counted guard keeps the one page-clock conversion and rejects new copies.
* refactor(process): share one finalize window across repeated beginFinalize calls
* refactor(application): own the finalization precedence in runOwned
runOwned runs owned work, closes its owner and applies the documented
precedence once: a recorded failure stays primary, cancellation before
commit keeps the completed value as a partial observation, cancellation
after commit keeps the value, and unconfirmed cleanup reports
cleanup_incomplete while keeping the completed value. finishAnalysisCommand
now projects its Finalized outcome; its 31 callers are unchanged.
* fix(application): preserve finalization outcomes
* fix(application): classify finalization by the primary error and keep cancellation through recovered publication
A producer composite whose primary is a cancellation finalized as failed because the kind read only the top-level error class; it now follows primaryAnalysisError. A cancellation that arrived while a recovered value's publication failed was dropped because the signal was read before that last await; it is now attached to the publication failure as a secondary.
* fix(cli): keep the initial failure snapshot whenever close itself succeeded
* refactor(errors): require a reason for every analysis cancellation
Add cancelledBy, the one constructor for AnalysisCancelledError. Its
reason argument is required: an aborted signal, an earlier cancellation,
a reason carried from an inner failure, or an OwnerCancellation when REA
closed the work's owner itself.
Closed adb, apktool, firmware, sqlite, gdb, web-bundle analyzer and
binary-session record owners now report OwnerCancellation.ownerClosed
instead of an absent reason. Firmware's predecessor wait returns its
cancellation as a Result, removing an unreachable reason-less fallback.
An architecture guard rejects new direct constructions, with a counted
per-file baseline that can only shrink.
* refactor(errors): share one aborted-signal predicate with cancelledBy
* refactor(process): return a departing caller's reason from a shared startup wait
* refactor(providers): build decompiler-provider cancellations with a required reason (#10587)
Migrate every AnalysisCancelledError construction under src/ghidra,
src/ida, src/cutter, src/jeb and src/kuna to cancelledBy and lower the
guard baseline from 671 to 572. Closed Kuna target commands report
OwnerCancellation.ownerClosed instead of an absent reason, and Kuna and
JEB close abort their lifetimes with that typed reason.
* fix(errors): read the reason of any signal-shaped cancellation source
* test(ci): cover the counted-baseline comparison behind the architecture guard
* refactor(hopper): build Hopper cancellations with a required reason
Migrate the remaining AnalysisCancelledError constructions under
src/hopper to cancelledBy. Regex-search close aborts its workers with
OwnerCancellation.ownerClosed. HopperCleanup and HopperClient are not
touched.
* test(hopper): cover cancellation during search shutdown
* refactor(parsers): report format failures through one ParseFailure algebra
Add ParseFailure (malformed, truncated, unsupported, limit, with format,
offset and field) and its one carrier, ParseFailureError, in the domain.
BinaryReader now names truncation and the field it read, and its native
format and resource failures are ParseFailureError subclasses.
Providers projected a format failure with their own instanceof ladder,
and some dropped the selected path from the invalid_format message.
parseFailureAnalysisError now owns that projection for the native,
Go, devicetree, V8, Windows Shortcut and Electron fuse providers.
The V8 code cache, Windows Shortcut and Electron fuse format classes
are removed in favour of the algebra.
The architecture guard counts raw Buffer and DataView reads, plain
Error throws in parser folders and plain JSON.parse of parser input
against baselines that can only shrink.
* fix(parsers): stop reading every RangeError as a format failure
Mach-O, Swift and Objective-C metadata parsers threw RangeError for a
truncated or malformed record, so their catch sites treated any
RangeError as a partial decode: a call stack overflow or a host string
limit became an "unresolved" metadata reason, and the IL2CPP Mach-O
path reported it as a malformed container.
Those parsers and the CIL method body reader now throw ParseFailureError.
hostRangeErrorKind is the one interpreter of a host RangeError (stack
overflow, host capacity, raw out-of-bounds read), and
parseFailureFromCause admits only a parse failure or a raw
out-of-bounds read as evidence about the input. Fixup stream catch-alls
rethrow host limits. The architecture guard counts the remaining direct
instanceof RangeError checks.
* fix(parsers): preserve Apple metadata failure ownership
* test(architecture): shrink parser failure baseline
* fix: preserve uncertainty for host range errors
SourceMapFormatFailure and its format/unsupported/limit/section-overlap reasons are replaced by ParseFailureError with format source-map. Limits carry the selected allowance name and maximum; a section-boundary overlap is a malformed failure on the section-boundary field. The codec reply protocol and every public error are unchanged.
* refactor(model): report GGUF and Safetensors format failures through ParseFailure
* fix(model): report malformed model headers as invalid_format through the shared mapping
Malformed and truncated GGUF and Safetensors headers now take the shared ParseFailure mapping (invalid_request, invalid_format on path) instead of the model provider's own BinaryTargetError file_format_rejected. Unsupported forms stay unsupported_target and model allowances keep their resource-constraint recovery.
* refactor(errors): require a reason for every analysis cancellation
Add cancelledBy, the one constructor for AnalysisCancelledError. Its
reason argument is required: an aborted signal, an earlier cancellation,
a reason carried from an inner failure, or an OwnerCancellation when REA
closed the work's owner itself.
Closed adb, apktool, firmware, sqlite, gdb, web-bundle analyzer and
binary-session record owners now report OwnerCancellation.ownerClosed
instead of an absent reason. Firmware's predecessor wait returns its
cancellation as a Result, removing an unreachable reason-less fallback.
An architecture guard rejects new direct constructions, with a counted
per-file baseline that can only shrink.
* refactor(errors): share one aborted-signal predicate with cancelledBy
* refactor(process): return a departing caller's reason from a shared startup wait
* refactor(android): build mobile-provider cancellations with a required reason
Migrate every remaining AnalysisCancelledError construction under
src/adb, src/android, src/apktool, src/apkSignature, src/flutter,
src/il2cpp, src/dotnet and src/frida to cancelledBy and lower the guard
baseline from 671 to 608. A closed adb provider reports
OwnerCancellation.ownerClosed for later probes, and JADX and apktool
shutdown abort with that typed reason.
* refactor(providers): build decompiler-provider cancellations with a required reason (#10587)
Migrate every AnalysisCancelledError construction under src/ghidra,
src/ida, src/cutter, src/jeb and src/kuna to cancelledBy and lower the
guard baseline from 671 to 572. Closed Kuna target commands report
OwnerCancellation.ownerClosed instead of an absent reason, and Kuna and
JEB close abort their lifetimes with that typed reason.
* fix(errors): read the reason of any signal-shaped cancellation source
* test(ci): cover the counted-baseline comparison behind the architecture guard
Hopper and native dispatch metadata re-ranked FAT slices from the target's
architecture name instead of reading the admitted universalSlice. Both now
consume the admitted range and refuse when it is missing or undeclared.
Mach-O load-command codes move to one table with an architecture guard.
* refactor(rust): report cargo-auditable section failures through ParseFailure
RustAuditableFormatFailure restated the malformed/unsupported algebra and
the provider translated it by hand; container BinaryFormatFailures were
re-wrapped and lost their kind. The reader now throws ParseFailureError
(format rust-auditable), container failures propagate unchanged, and the
provider maps both through parseFailureAnalysisError. A counted guard
keeps new per-format failure classes from returning.
* test(rust): pin the container's truncated failure facts through the Rust reader
* refactor(errors): require a reason for every analysis cancellation
Add cancelledBy, the one constructor for AnalysisCancelledError. Its
reason argument is required: an aborted signal, an earlier cancellation,
a reason carried from an inner failure, or an OwnerCancellation when REA
closed the work's owner itself.
Closed adb, apktool, firmware, sqlite, gdb, web-bundle analyzer and
binary-session record owners now report OwnerCancellation.ownerClosed
instead of an absent reason. Firmware's predecessor wait returns its
cancellation as a Result, removing an unreachable reason-less fallback.
An architecture guard rejects new direct constructions, with a counted
per-file baseline that can only shrink.
* refactor(errors): share one aborted-signal predicate with cancelledBy
* refactor(process): return a departing caller's reason from a shared startup wait
* refactor(providers): build decompiler-provider cancellations with a required reason (#10587)
Migrate every AnalysisCancelledError construction under src/ghidra,
src/ida, src/cutter, src/jeb and src/kuna to cancelledBy and lower the
guard baseline from 671 to 572. Closed Kuna target commands report
OwnerCancellation.ownerClosed instead of an absent reason, and Kuna and
JEB close abort their lifetimes with that typed reason.
* fix(errors): read the reason of any signal-shaped cancellation source
* test(ci): cover the counted-baseline comparison behind the architecture guard
* refactor(parsers): report format failures through one ParseFailure algebra
Add ParseFailure (malformed, truncated, unsupported, limit, with format,
offset and field) and its one carrier, ParseFailureError, in the domain.
BinaryReader now names truncation and the field it read, and its native
format and resource failures are ParseFailureError subclasses.
Providers projected a format failure with their own instanceof ladder,
and some dropped the selected path from the invalid_format message.
parseFailureAnalysisError now owns that projection for the native,
Go, devicetree, V8, Windows Shortcut and Electron fuse providers.
The V8 code cache, Windows Shortcut and Electron fuse format classes
are removed in favour of the algebra.
The architecture guard counts raw Buffer and DataView reads, plain
Error throws in parser folders and plain JSON.parse of parser input
against baselines that can only shrink.
* fix(parsers): retain malformed range offsets
* refactor: prune internal seams and preserve configuration ownership
* fix(setup): keep YAML aliases and inherited registration settings on refresh
- Reuse an unchanged YAML collection only while every alias in it still
resolves after the edit; otherwise rebuild it so the alias becomes its
value. A sibling anchor replaced or moved in the same refresh no longer
leaves an unresolved alias in Hermes or Goose output.
- Keep a connection selector whose existing value already chooses stdio
(for example an inherited Hermes transport: stdio) instead of superseding
it; remote selectors are still replaced. Explicit stdio selectors are
therefore no longer rewritten (Kimi expectation updated).
- When the whole Hermes registration is inherited through a merge,
override it with that merge and write only the changed fields.
* fix(setup): edit registration fields whose YAML key is an alias
A key written as an alias to a string scalar (for example *field with
field: &field env) was treated as a source-only key, so any change to
its value was refused in Hermes and Goose. Resolve such keys to their
string, keep the aliased spelling while its anchor survives the edit,
and write the plain key when the same edit rebuilds the anchor.
* test(process): bound cleanup-boundary waits and always release gates
Both cancellation tests awaited a bare started promise and released
their gates only on success, so a missed boundary waited for the outer
deadline and could leave cleanup parked. Use awaitAcknowledgement and
release and join the cleanup in finally.
* fix(setup): repair only aliases an edit leaves unresolved
Replace the pre-edit alias survival guess, which expanded aliases to
sibling anchors that survive unchanged, with an exact repair: record
what each alias in the replaced value resolves to, write the edit, then
replace only aliases left without a preceding anchor by that value.
- Keep an aliased key's source position, so its anchor stays ahead of
later aliases.
- Treat an aliased key as its string only when no other key in the map
resolves to the same string; an ambiguous one stays guarded.
- Override an inherited Hermes registration only when the requested
entry keeps every inherited field, so an explicit replacement can drop
remote selectors.
* fix(setup): repair aliases whose binding an edit changes, keeping comments
An alias whose own anchor was removed could resolve to an earlier anchor
with the same name and silently change value. Record the node each alias
resolved to and repair any alias that no longer resolves to that node.
Carry the alias's comments and spacing onto the repaired value.
* test(setup): split the Hermes anchor and merge refresh cases
* fix(setup): refuse removing an overridden key its merge would restore
The merge-retention check skipped keys the mapping also overrides, so
dropping such a key (for example a remote url during an explicit stdio
replacement) let the retained merge restore it. Check removal before
skipping overridden keys, so the edit is refused with the existing
message instead of producing a registration that cannot be applied.
* fix(verifiers): release fixture roots only after their owners finish
Several real-tool verifiers released a fixture root even when a cleanup
that still used it had failed, so a surviving MCP server, session or
descendant kept running against a deleted directory and the failure
looked like ordinary cleanup.
- scripts/lib/verifier-cleanup.mjs: new releaseAfter(dependents,
release, retained) task. It attempts every dependent cleanup and
releases the resource only if all succeed; otherwise it keeps the
resource and reports it with the dependent failures.
- verify-signal-recording: the root is released after the MCP client
and transport close, and the host-limits read moves before the root
is acquired so its failure cannot leak the root.
- verify-recorded-crash: sessions, the sentinel and the process-lineage
check must all succeed before the root is released. A transport close
failure after a failed connect keeps the connect failure.
- verify-wasm-artifact: both roots are released after the MCP client
and transport close; the residue check and the two directories stay
independent after that.
- verify-native-ui: `open -W` exits only after the app exits, so the
root is removed only once that exit is observed; otherwise it is
retained and reported.
Fixes#8561Fixes#8562Fixes#8564
Refs #8559
Co-Authored-By: gensokyo-dev <gensokyo-dev@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(verifiers): treat a signaled open as unproven fixture exit
A terminated `open` launcher does not prove the LaunchServices app it
started has exited, so only an `open -W` that exits on its own counts.
Also clear the grace timer so a prompt exit does not hold the process
for the rest of the grace period.
Refs #8559
Co-Authored-By: gensokyo-dev <gensokyo-dev@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* test(verifiers): model open -W exiting after the fixture is signaled
The native UI verifier now removes its root only after `open -W` exits
on its own. The authored recording's `open` never exited when the
fixture was signaled and had no signalCode, so the complete case waited
out the grace period and kept the root. The double now behaves like
`open -W`: it exits once the fixture process is signaled.
Refs #8559
Co-Authored-By: gensokyo-dev <gensokyo-dev@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: gensokyo-dev <gensokyo-dev@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
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 <342580529+gensokyo-dev@users.noreply.github.com>
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 <342580529+gensokyo-dev@users.noreply.github.com>
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.