* ci: run required checks on merge-queue groups
Add the merge_group trigger to CI and PR title validation so every required
check reports on the queued group's exact head. Group runs use exact-head
concurrency groups that pushes cannot cancel, classify the group's combined
diff from its base (a non-ancestor base fails planning), and keep the CI
required gate running when a group is cancelled so it fails instead of
reporting a skipped check. Title validation checks every squash commit
subject between the group base and head.
* ci: read the merge-group comparison from a file
Every counted-baseline change edited scripts/verify/architecture-guard-baselines.json, so PRs touching unrelated guards conflicted there. Each guard now keeps its baseline in scripts/verify/baselines/<guard-key>.json with paths sorted by code unit; --shrink-baselines rewrites, and deletes a guard's file when its last count is removed, and the guard rejects unsorted files and files that name no counted guard.
* refactor(verify): give the native verifiers' roots to VerifierFixture
* fix(verify): report every nested JEB cleanup failure reason
* test(verify): give the isolated ADB verifier runs the fixture owner it now imports
* feat(javascript): make the member-mutation work allowance a runtime setting
The member-mutation alias-state allowance from #11144 was a fixed constant, so a refused source could not be admitted without a code change.
Register javascript.member_mutation_alias_states (default 524288, 1 to 8388608 alias states) with the launch seed REA_JAVASCRIPT_MEMBER_MUTATION_ALIAS_STATES. The application, web bundle and reference-source boundaries resolve it from the accepted operation snapshot and launch environment, refuse a malformed seed before analysis, and send it to the workers with each source. A refusal names the setting, its effective value, source and range, and the recovery; worker parents rebuild it from the allowance they sent.
Refs #8294
* fix(javascript): keep the setting and recovery of a refused web script
A web bundle script refused by the member-mutation allowance kept only its reason and reported limits, so the public unknown lost the setting, value, source and recovery. Carry the refusal's structured limit and remediation on the per-script result and its javascript_analysis unknown, grouping scripts only when that recovery agrees. Assert structured limit facts and the owning formatter's recovery instead of fixed wording.
* fix(browser): keep only registered setting limits on a refused web script
* feat(javascript): keep syntax facts for a source whose member-mutation phase is refused (#11187)
* feat(javascript): keep syntax facts for a source whose member-mutation phase is refused
A source refused by the member-mutation work allowance during static analysis lost every fact, so the application graph counted it as skipped. Reference-source import failed outright on such a file.
Checkpoint the parser, comment and text facts the static owner can still state, with parse status partial, examination not-examined and refused_phase member-mutations, before reporting the refusal; the semantic phase is not started because it indexes the same mutations. Reference-source import keeps the file's declaration-level imports and records that its CommonJS require calls were not examined.
Refs #8294
* test(javascript): compare the reference-source refusal with its owning formatter
* feat(javascript): keep a refused web script's parse and text facts
A captured script whose member-mutation phase was refused lost every fact in analyze_web_bundle, so it was neither counted as parsed nor kept its vendor markers.
The analyzer now returns the script as unavailable with partial facts: refused phase, parse status and the inferences read from script text alone. Findings gathered before the refusal are dropped, because they may have read an incomplete mutation index. The worker parent states the refusal and its setting recovery from the allowance it sent, through the same helper the isolated analyzer uses for every unavailable script. The bundle counts the script as parsed, keeps its text inferences, and its javascript_analysis unknown carries refused_phase.
Refs #8294
* chore(mcp): record the member-mutation setting in the advertised summary
* fix(javascript): rebuild a parked source worker when the member-mutation allowance changes
A worker parked on a resource scope was reused when heap, timeout and precision matched, but its requests carry the allowance it was built with. After update_analysis_settings accepted a new allowance, the next analysis on that scope kept the old one.
The reuse check now compares the allowance's value and source, so a changed setting gets a worker built with it. The web bundle worker already takes the allowance per script.
* refactor(verify): give the remaining verifiers' roots to VerifierFixture
* fix(verify): let the loader-relocation supervisor dispose itself so a retried stop can observe its exit
* test(verify): give the isolated bridge-sources and packaged-wasm runs the fixture owner they now import
* test(verify): observe the Bun and Go verifier roots where VerifierFixture loads them
* refactor: report NativeAOT, Interface Builder and owned-MCP output limits through the shared limit owners
* refactor(errors): carry a parser's required value on its limit reference and in adjustable-budget refusals (#11201)
* fix(android): keep the JADX output-limit refusal's retained process output
* refactor(native): name PE and native-export allowances in the BinaryResourceFailure limit (#11253)
PeImportLimitFailure, PeLoadConfigLimitFailure and NativeExportLimitFailure kept a parallel `limit` field beside the LimitRef their BinaryResourceFailure base already carries (named only "structure"). The allowance name now lives in the LimitRef, consumers read it with binaryResourceAllowance against the parser's exported allowance list, and the three subclasses are removed. The local-limit-error-class guard also counts limit subclasses of BinaryResourceFailure.
* fix(verify): recognize the ownership ps inventory from the process owner's arguments
* fix(verify): match the recorded-crash ps inventory against REA's fixed ps locations
* fix(javascript): report version-comparison coverage unknown when a source graph cannot tell
* refactor(javascript): name the unavailable-graph coverage mapping once
* fix(javascript): give the version change graph and its edges the same unknown coverage
* fix(javascript): report export-shape comparison coverage unknown when a source graph cannot tell (#11218)
* fix(javascript): report export-shape comparison coverage unknown when a source graph cannot tell
* test(javascript): keep unknown export-shape coverage beside a truncated projection
* fix(javascript): read runtime-reconciliation input coverage once and report unknown when a static graph cannot tell (#11230)
* fix(javascript): read runtime-reconciliation input coverage once and report unknown when a static graph cannot tell
* fix(javascript): keep a known input truncation beside unknown reconciliation coverage
* fix(javascript): report export-shape source omission counts unknown when a side has no projection (#11261)
compare_export_shapes reported left/right_omitted_fields and omitted_property_coverage as 0 for a side whose export selection recovered no return-shape projection (missing, ambiguous or unavailable), while the sibling source_omitted_variants already reported null. Add Known<T> (src/domain/known.ts) and read each side's source coverage once through it; the counts are now null (unknown) with a limitation naming why. The runtime-reconciliation graph's omission sum and its parallel 'omissions known' flag become one Known<number> via sumKnownCounts (no output change).
* fix(verify): run the native-calls hardened-runtime check in the artifact CLI environment
* fix(verify): give the native-calls get-task-allow copy its dSYM
* refactor(mcp): register service-backed tools through one contract binding loop
registerContractTools registers every bound contract from the registry through the shared Evidence or retained-summary handler its binding selects, and refuses a binding that names no registered contract. createServer declares the bindings by exact contract name; 41 per-tool register files are deleted.
* refactor(mcp): bind service-backed tools from one shared table and make each binding select one handler
serviceToolBindings owns the binding of every service-backed contract, keyed by its service; createServer and the MCP tests bind from it, so tests exercise the production handler kind, admission and dependencies. A binding can no longer carry both run and summary. The Go real-binary workflow follows the new registration owners.
* test(mcp): assert binding exclusivity with type-level checks that hold under every checker
inspect_release_exposure kept the first eight scan limitations and dropped the rest with no count, at the collector and at two merge points that prepend a reason. Add boundedList/BoundedListCollector (src/domain/boundedList.ts), which always carry the omitted count, and report it as a 'limitations' coverage.omitted entry. A counted constant-maximum-slice guard keeps new .slice(0, MAX) copies from appearing outside the owner.
* refactor(mcp): derive every advertised tool through one owner
The server's registration and tools/list rewrite, catalog identity and the
Copilot verifier each assembled the advertised description, input and
output schema themselves. src/contracts/advertisedTool.ts now owns that
composition; served environment catalogs (MCP listing and card) go through
the same owner's dialect projection, keeping argument descriptions.
* test(mcp): pin each remaining environment dialect violation to its query union
* test(support): copy isolated-repository scripts with their runtime import closure
* fix(test): resolve package imports to the file Node loads in the import closure
summary.json held every advertised pattern's site list and every tool's
schema digests in one file. Each new path-taking tool appended to the
shared path pattern's list, so concurrent tool PRs conflicted on it, and
updating it required a local build.
Each tool's digests and patterns now live in
scripts/verify/advertised-schemas/tools/<tool>.json. The check still prints
aggregated review lines such as "N tool schemas gained pattern /.../", and
on failure also prints the expected contents of each missing or stale file
so a PR can commit them from CI output without building. --update-summary
writes those files and deletes summaries of removed tools. The existing
summary was split into per-tool files from its recorded data.
Fixes#11189
Admit ELF machine 94 as the canonical Xtensa family through Ghidra's header-selected import. Share generic-header exclusions between static target types and live CLI/MCP summaries, preserving ELF-only format constraints and other providers' decline paths.
Fixes#10765.
Adapted from #10774.
Co-authored-by: Josh <joshuakrishna21@gmail.com>
Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
Scenario planning and plan Evidence take a required platform. Host file operations (target resolution, output trees, Windows spelling lookup, executable checks, native snapshot admission), the Linux distribution read and the process CLI adapter read HOST_PLATFORM explicitly. ByteEntropyService takes an optional platform seam, so its Windows casing test injects win32 instead of stubbing process.platform. src/application no longer reads process.platform or process.arch.
* refactor(native): report bounded string reads that stop at their allowance as decode limits
* fix(verify): count generic and multi-line plain-Error limit classes
* refactor(verify): give the package and release verifiers' roots to VerifierFixture
* fix(verify): import VerifierFixture at the published-package verifier's top level
* test(mcp): check advertised schemas against a portable dialect in every profile
* fix(mcp): re-emit advertised patterns in a portable regex grammar
* fix(mcp): present discover_tools input through the selected profile
The hot-cold catalog registered discover_tools directly with the SDK, so
the default compact input profile still advertised its canonical schema,
including page-size defaults clients apply on their own (#5322). Route
every listed input schema, transport discovery included, through one
server-owned presentation, and pass the selected presentation to the
deferred server, which serves recorded tool schemas but not discovery.
* fix(mcp): keep the Evidence union on described borrowed inputs
borrowEvidenceInput installed its advertised projection on one schema
instance. A call site's .describe() rebuilds the schema from its
definition, so match_native_functions advertised left and right as
unconstrained values in every input profile. Install the projection in
the borrowed parser's constructor so derived schemas keep it, for both
borrowed inputs and borrowed arrays.
* fix(mcp): validate advertised dialect by selected profile
* fix(mcp): preserve schema widening through references
* feat(mcp): project compact input schemas into the portable advertised dialect
One projection (advertisedDialectProjection.ts) owns the compact input
advertisement: one type per node, items on every array, string literals
as enums, oneOf widened to anyOf, allOf merged, tuples as element unions,
exclusive bounds made inclusive, and every keyword outside the dialect
dropped. Each rewrite is exact or widening; server validation is
unchanged. The root description and sentences for dropped cross-field
constraints move to the tool description, which Codex and Zed keep.
The compact baseline falls to nullable values only (29), which the
dialect cannot express without narrowing an input. The gateway shape
repairs (typeArrayItemUnions, hasGatewayIncompatibleShape), the second
dependentRequired-to-prose copy and the strict-gateway test denylist are
removed; the dialect checker gates the inline presentation instead.
* feat(mcp): omit output schemas from tools/list by default
No surveyed model API reads an output schema, MCP client validators
repeat REA's own result validation, and complete output schemas were
about 92% of the default tools/list. REA_MCP_OUTPUT_SCHEMA_PROFILE now
resolves to off through one owner (resolveMcpOutputSchemaProfile);
compact and full remain selectable. structuredContent is still
delivered, and the deferred server serves both off and full from the
recorded catalog with the matching recorded identity.
Tests and artifact verifiers that validate results against complete
advertised output contracts select full explicitly.
* docs: describe omitted output schemas as the default in README translations
* fix(mcp): select schemas in verification workflows
* fix(mcp): refuse zero-padded repeat bounds and record host-specific path patterns
* fix(mcp): keep projected schemas permissive
* fix(mcp): keep references valid when presentation widens their targets
* fix(mcp): keep a single union branch's closure scoped to its properties
* fix(mcp): select complete output schemas in the Electron verifier
* fix(mcp): select complete output schemas in standalone verifier probes
* fix(mcp): keep an allOf member's tuple tail with its own positions
* fix(mcp): relax references into negations and conditionals presentation may remove
* refactor(contracts): declare root discriminators as typed schema annotations
* test(mcp): select the borrowed Evidence contract from its family
* fix(mcp): keep astral literals and dynamic references widening in every mode
* fix(mcp): omit astral literals and relax untracked references when presentation widens
* fix(mcp): select complete output schemas in every verifier transport
* fix(mcp): read reference pointers by schema position, not segment spelling
* fix(mcp): protect references into collapsing boolean unions and keep referencing branches out of the collapse
* fix(mcp): keep reduced schemas open when their root admits names by pattern
* test(mcp): keep the reduced-schema pattern case outside the size-limited block
* fix(mcp): omit patterns whose classes name astral characters
* fix(mcp): decide union collapse and pointer protection from canonical branches
* docs(mcp): document non-u astral matching and pin BMP agreement across V8 modes
* fix(mcp): keep prototype-named properties as own properties in the dialect projection
* test(mcp): select full output schemas in packet and signal schema checks
* test(mcp): expect the advertised base64 schema to omit the unbounded pad-bit probe
* fix(mcp): protect references into unevaluated keywords lost evaluation removes
* fix(mcp): validate the doctor probe reply against the canonical output contract (#10655)
* fix(mcp): validate the doctor probe reply against the canonical output contract
* test(mcp): replay doctor probe validation under every output profile
* test(mcp): accept typed-branch nullable unions in the advertised dialect (#10649)
* test(mcp): select full output schemas in HTTP session workflows
* fix(mcp): emit stack-safe classes with non-ASCII members and protect draft-07 reference siblings
* fix(mcp): protect references into literal siblings of relaxed draft-07 references
* feat(mcp): omit output schemas from tools/list by default (#10646)
* feat(mcp): omit output schemas from tools/list by default
No surveyed model API reads an output schema, MCP client validators
repeat REA's own result validation, and complete output schemas were
about 92% of the default tools/list. REA_MCP_OUTPUT_SCHEMA_PROFILE now
resolves to off through one owner (resolveMcpOutputSchemaProfile);
compact and full remain selectable. structuredContent is still
delivered, and the deferred server serves both off and full from the
recorded catalog with the matching recorded identity.
Tests and artifact verifiers that validate results against complete
advertised output contracts select full explicitly.
* fix(mcp): select schemas in verification workflows
* fix(mcp): select complete output schemas in the Electron verifier
* fix(mcp): select complete output schemas in standalone verifier probes
* fix(mcp): select complete output schemas in every verifier transport
* test(mcp): select full output schemas in packet and signal schema checks
* fix(mcp): validate the doctor probe reply against the canonical output contract (#10655)
* fix(mcp): validate the doctor probe reply against the canonical output contract
* test(mcp): replay doctor probe validation under every output profile
* test(mcp): accept typed-branch nullable unions in the advertised dialect (#10649)
* test(mcp): select full output schemas in HTTP session workflows
* fix(mcp): keep the Evidence union on described borrowed inputs (#10597)
* fix(mcp): keep the Evidence union on described borrowed inputs
borrowEvidenceInput installed its advertised projection on one schema
instance. A call site's .describe() rebuilds the schema from its
definition, so match_native_functions advertised left and right as
unconstrained values in every input profile. Install the projection in
the borrowed parser's constructor so derived schemas keep it, for both
borrowed inputs and borrowed arrays.
* test(mcp): select the borrowed Evidence contract from its family
* docs(mcp): admit baseline growth only for restored contract declarations
* fix(mcp): state reduced multi-type properties as typed branches, not type lists
The reduced fallback derived a property's types and emitted them as a type
list, which Gemini, Moonshot and Ark reject. A property whose canonical
schema is anyOf string and number therefore traded a typed union for a
rejected construct when the inline projection fell back. Emit one type, or
anyOf typed branches, so [T, null] becomes the dialect's nullable union.
* feat(mcp)!: retire the full schema profiles and declare complete observations as a typed annotation (#10718)
* refactor(mcp): declare complete observations as a typed annotation applied only on the wire
* test(mcp): compare complete-observation declarations with their owning text
* refactor(schema): couple Zod checks with their JSON Schema constraint keywords (#10725)
* refactor(schema): couple Zod checks with their JSON Schema constraint keywords
* refactor(schema): declare pattern approximations and object output roots as typed annotations (#10754)
* refactor(schema): declare pattern approximations and object output roots as typed annotations
* fix(schema): refuse pattern declarations JSON Schema cannot state
* test(schema): select guided_workflows from its contract family
* feat(mcp)!: retire the full input and output schema profiles (#10841)
* feat(mcp)!: retire the full input and output schema profiles
The compact input presentation is now the only advertised input schema, and
output schemas are either omitted (`off`, the default) or bounded
(`compact`). Catalog identity, the build-recorded digests and the recorded
startup catalog collapse to those presentations: two digest profiles instead
of six, one recorded catalog instead of two, and no input dialect baseline.
Tests and verifiers that validated replies against listed full output
schemas now validate against the canonical output contract, which REA
already enforces server-side; tests that asserted canonical input
restrictions read the canonical input schema instead of a full listing.
BREAKING CHANGE: `REA_MCP_INPUT_SCHEMA_PROFILE=full` and
`REA_MCP_OUTPUT_SCHEMA_PROFILE=full` are refused at startup and by `rea setup`
with a configuration error naming the setting. Remove the setting from the
client registration (or rerun setup with `REA_MCP_INPUT_SCHEMA_PROFILE=compact`
/ `REA_MCP_OUTPUT_SCHEMA_PROFILE=compact`). `REA_MCP_INPUT_SCHEMA_PROFILE`
still accepts `compact`. Server-side validation is unchanged.
* test(mcp): validate replies against canonical output contracts at every listed-schema reader
tools/list omits output schemas by default, so fixtures, verifiers and the
Android analysis runners that read a listed outputSchema now bind the
canonical contract instead. connectLocalToolsMcp and advertisedMcpToolSchemas
hand the SDK a definition carrying the canonical output schema, so the SDK
client and caller-supplied validators check every successful reply.
* test(mcp): resolve Android runners' output contracts from the selected repository
The Android efficiency runners imported the canonical output-schema helper from the harness checkout while --repository selects the revision under test. Import it from the selected repository, as the runners already do for the SDK and Ajv.
* test(mcp): let Android runners compare revisions before the canonical output helper
A selected revision without scripts/lib/canonical-output-schema.mjs could not start the Android runners. Choose the output contracts in one harness helper: the selected revision's canonical contracts when it has them, otherwise its listed schemas under the full output profile it still supports.
* fix(scripts): keep caller launch settings when the real Hopper verifier builds its server environment
The verifier deleted REA_VERIFY_SERVER_COMMAND and REA_VERIFY_SERVER_ARGS_JSON from process.env itself before reading them, so a caller selecting an installed REA command silently ran the checkout's dist/main.js. Copy the environment, as main does, so only the child's environment changes.
* test(mcp): validate whole replies against canonical output contracts wherever a listed definition is held
tools/list omits output schemas by default and the full output profile is gone, so tests that handed the SDK a listed definition no longer checked complete replies. Attach the canonical output schema with withCanonicalOutputSchema at every held REA tool definition. Environment-served tools (not REA contracts), and the deferred-runtime and doctor tests that exercise per-profile listings, keep their listed definitions.
* test(mcp): record the advertised catalog summary without the retired full profiles
Retiring the full input and output profiles leaves only the compact input and compact output listings, so the summary drops the full-profile digests and every pattern only the full listings carried. Regenerated with npm run verify:advertised-schemas -- --update-summary.
* fix(verify): check the ESP lanes' RISC-V toolchain through the shared prerequisite before allocating
* test(verify): require the ESP compiler probe to generate RISC-V code
* fix(guards): refuse a counted-baseline file that is not in its written order
* test(guards): decide the baseline file's rewrite or refusal in its owner and cover it
* refactor(verify): give the Windows verifiers' workspaces to VerifierFixture
* fix(verify): import VerifierFixture at the Windows native verifiers' top level, not in their child scripts
* ci(guards): run the architecture guards in the pre-push check
* docs(limits): name lint as the numeric-limit check's owner
* ci(lint): cache the numeric-limit check by every file its restart-field scan reads
* test(mcp): compile advertised patterns with the regex engines clients run
List the built package's tools over stdio in every input, output and
catalog profile and compile each distinct advertised regular expression
with V8 (plain, u, v), Go regexp (RE2), C++ RE2, Python re, and Rust regex
and fancy-regex through the Rust jsonschema crate. A refusal names the
engine, its error, and each tool, schema path and profile.
The Python engines are pinned, hash-checked wheels; Go is the host
toolchain with GOTOOLCHAIN=local. A missing engine is reported as not
verified, and REA_REQUIRE_SCHEMA_ENGINES=1 makes it a failure.
The pattern owner now also reports patternProperties keys, which clients
compile as well.
Refs #10830
* test(mcp): run schema engine helpers and listings under the process owner
* test(mcp): load advertised schemas in the validators clients build (#10983)
* test(mcp): load advertised schemas in the validators clients build
Build a validator for every distinct advertised input and output schema
with the Rust jsonschema crate's defaults, which check the declared draft's
meta-schema and formats and compile every subschema. --complete adds Python
jsonschema's check_schema, which jsonschema.validate runs in Python clients;
it takes several seconds, so it is opt-in.
Refusals are grouped by reason, so one shared pattern that breaks many
schemas is reported once with its sites.
Ajv strict mode and OpenAI strict mode are documented as not covered, with
the reasons.
Refs #10830
* ci(mcp): require the client schema engines on every code change (#10984)
* ci(mcp): require the client schema engines on every code change
Run scripts/verify-advertised-schemas.mjs at the end of the build job, after
its artifacts upload, with REA_REQUIRE_SCHEMA_ENGINES=1 so a missing engine
fails rather than passes. Go is the runner's toolchain; the Python engines
install into a virtual environment cached by the requirements file's digest
and interpreter version. Full qualification adds --complete.
Refs #10830
* test(ci): require the client schema engine step in code builds
* test(mcp): record a reviewable summary of the advertised catalog (#10985)
* test(mcp): record a reviewable summary of the advertised catalog
Commit scripts/verify/advertised-schemas/summary.json: each advertised
pattern with the tool schemas that carry it, and a digest of every tool's
schema per input or output profile. The advertised schema check compares it
with the packaged listings it already makes and fails on any difference,
printing lines such as '119 tool schemas gained pattern /.../', so a change
that reaches many tools at once is visible in review.
--update-summary regenerates it.
Refs #10830
* docs(release): check the packed catalog with client engines before publishing (#10986)
* docs(release): check the packed catalog with client engines before publishing
Add a pre-publish step to the release guide: install the packed candidate
into an empty prefix and run verify-advertised-schemas.mjs against it with
--package-root, requiring the RE2 (Go regexp and C++ RE2) and Rust (regex,
fancy-regex and jsonschema) engines. CI checks each commit's build; this
covers the archive that will be published.
Refs #10830
* docs(release): check the exact qualified archive and clean up afterwards
* test(mcp): regenerate the advertised catalog summary from current main
* refactor(coverage): give the complete/partial/unknown status one owner and ratchet local completeness enums
* fix(verify): count chained local completeness enums and exclude detail levels
* fix(javascript): keep source-to-bundle coverage unknown when an input's coverage is unknown (#11162)
* refactor(setup): take the platform in client configuration readers and the setup write path
ClientRegistrationEnvironment, ClientConfigurationFiles and SupportedClients take a required platform (before defaulted parameters). Setup's configure, inspect and alignment entry points take the setup host's platform and pass it through every layer read, ownership check and the Cordis serializer, which now aliases Windows environment names by the selected platform instead of the running host. Host file operations in ClientConfigPath, ClientConfigurationPublication, JsonFiles and rea mcp add read HOST_PLATFORM explicitly.
* fix(setup): keep private-profile modes on the host when setup selects another platform
* 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(captures): classify TShark, sigrok and pcap failures through the shared provider classifier
TsharkCaptureProvider and SigrokRecordingProvider replace #failure with a dialect, and PcapProvider replaces analysisFailure and the tool-identity classifier. ArtifactAnalysisResources exposes its command context and a readCleanupFailure composer so the readCleanup hook keeps each provider's cleanup report shape.
* refactor(jeb): classify JEB failures through the shared provider classifier
JEB's raw MCP failures go through classifyProviderFailure with a JEB dialect;
a typed attribution step reports a shared phase's failure under the REA
operation. Each MCP phase deadline now reports its own interruption when it
aborts, deciding a cancellation's owner from the caller's own signal instead
of comparing abort reasons. A refusal carrying the JVM's OutOfMemoryError
report becomes a resource constraint naming JEB's Java heap.
* fix(jeb): advise the JVM limit that governs the reported memory area
* refactor(ghidra): classify Ghidra failures through the shared provider classifier
PSP extension inspection reports through classifyProviderFailure with a
Ghidra dialect. The startup deadline decides at abort time whether the shared
startup owner closed it, so Ghidra and Hopper no longer compare the abort
reason with the owner's sentinel, and Ghidra's startup no longer compares a
producer cancellation's reason with the deadline's. Three helpers that only
construct one error kind now name that kind instead of AnalysisError.
* refactor(hopper): classify original-source read failures through the shared provider classifier (#10838)
* test(ghidra): pin the aborted startup deadline over a producer cancellation
* refactor(kuna): classify Kuna failures through the shared provider classifier
Session operations, the workspace copy and the project export report raw
failures through classifyProviderFailure with Kuna dialects; the copy and
export keep their cleanup report shapes through the readCleanup hook.
* refactor(adb): report a pulled file's digest failure from its own type
* refactor(sqlite): classify worker-reply read failures through the shared provider classifier
* refactor(ida): classify IDA operation failures through the shared provider classifier
* refactor(process): let artifact-resource consumers classify through the shared provider classifier (#10867)
* test(providers): pin attribution of a cleanup-failed read's typed primary
* refactor(sqlite): classify database failures through the shared provider classifier
* refactor(ghidra): classify Ghidra failures through the shared provider classifier (#10836)
* refactor(ghidra): classify Ghidra failures through the shared provider classifier
PSP extension inspection reports through classifyProviderFailure with a
Ghidra dialect. The startup deadline decides at abort time whether the shared
startup owner closed it, so Ghidra and Hopper no longer compare the abort
reason with the owner's sentinel, and Ghidra's startup no longer compares a
producer cancellation's reason with the deadline's. Three helpers that only
construct one error kind now name that kind instead of AnalysisError.
* refactor(hopper): classify original-source read failures through the shared provider classifier (#10838)
* test(ghidra): pin the aborted startup deadline over a producer cancellation
* refactor(kuna): classify Kuna failures through the shared provider classifier (#10843)
Session operations, the workspace copy and the project export report raw
failures through classifyProviderFailure with Kuna dialects; the copy and
export keep their cleanup report shapes through the readCleanup hook.
* chore(guards): shrink the cancellation construction baseline after merging the base
* refactor(ida): classify IDA operation failures through the shared provider classifier (#10859)
* refactor(ida): classify IDA operation failures through the shared provider classifier
* refactor(process): let artifact-resource consumers classify through the shared provider classifier (#10867)
* test(providers): pin attribution of a cleanup-failed read's typed primary
* fix(verify): refuse the cross-architecture Ghidra lanes before allocating when their toolchain is missing
* fix(verify): probe the fixture toolchain through the owned command runner and check the tools' targets
* refactor(verify): release five verifier roots through VerifierFixture after their clients confirm
* fix(verify): keep the binary-layout and EVM roots until their auxiliary MCP clients confirm
* fix(verify): carry the retained root on each migrated verifier's final failure
* refactor(verify): give the switch, Swift and address-context Ghidra workspaces to VerifierFixture
* fix(verify): carry the retained Ghidra workspace on each verifier's final failure
* refactor(setup): take the platform in doctor, setup, uninstall and update maintenance
* test(setup): cover a caller-selected platform in update maintenance
* refactor(android): take the host in JADX configuration, doctor and workspace roots
* fix(android): pass the host in verifiers, follow it for JAVA_HOME, and test the native cache split
* test(platform): count tests that redefine process.platform or process.arch (#11102)
Tests select a host by injection. A redefined process.platform stops exercising its branch once the module under test reads HOST_PLATFORM, and still passes. A counted baseline (105 sites in 20 files) may only shrink; each module migration switches its tests to injection.
* refactor(process): read Ghidra and Hopper replies through one bounded JSON-line reader
Both clients now share JsonLineBridgeReader: UTF-8 framing with a mandatory
reply line ceiling (bridge.max_reply_bytes, REA_BRIDGE_MAX_REPLY_BYTES,
default 256 MiB), one fatal UTF-8 policy, request-id correlation including
rejections on the reserved id 0, and typed stream failures. An oversized
reply is refused as a transport resource constraint naming the setting and
its bytes are discarded. Hopper rejections on id 0 now reach the request on
the wire instead of being dropped.
Deletes GhidraResponseRouter and HopperResponseStream.
* fix(process): bound each bridge reply by the ceiling its own request admitted
* fix(process): admit the bridge reply ceiling before launching Hopper or Ghidra
* fix(ghidra): keep a refused setting's configuration error through the provider boundary
* fix(process): admit both bridge ceilings per request as a typed configuration refusal
A malformed REA_BRIDGE_MAX_REQUEST_BYTES or REA_BRIDGE_MAX_REPLY_BYTES seed covered by a runtime override at startup is refused by the runtime view once the override is reset. Hopper and Ghidra read those getters in the request path (and Hopper re-read the request ceiling when a queued request reached the wire, outside the caller's context), so the refusal was thrown instead of returned. Both clients now copy both ceilings once at admission through readRuntimeSetting, return the ConfigurationError (Ghidra as an admission failure), and write queued requests under the ceilings they were admitted with.
* refactor(config): read rizin, gdb, Kuna, Ghidra and regex settings through readRuntimeSetting (#10795)
Each site caught the ConfigurationError a setting view throws for a malformed launch seed by hand. They now read through readRuntimeSetting and return its Result. GDB reads both debugger limits once per console command and reuses those accepted values for its limitations, timeout and diagnostics instead of re-reading the views after the command ran. Kuna's single-function read returns the refusal after releasing admission instead of throwing it. A counted architecture guard keeps the remaining hand-caught sites from growing.
* refactor(hopper): return typed bridge replies from HopperClient.callTool (#10833)
* refactor(hopper): return typed bridge replies from HopperClient.callTool
The reply contract table existed, but the provider, caller reconciliation and
regex search each applied it after callTool returned JsonValue. callTool is now
typed by method: it validates the reply against that method's contract and
returns the typed reply, or a HopperReplyContractError that names the method
and keeps the raw reply. The caller scan keeps that raw reply as its unread
page or native result. The provider dispatches list_names, file offsets and
the three reconciled operations through their literal methods, and health has
its own contract. Transport tests inject a JSON-value table for the fake
bridge's own methods.
* fix(hopper): refuse value-changing reply contracts and uninstalled methods
parseHopperReply returns the raw reply, so a contract whose input and output types differ (a coercion, default or transform) would promise a type the reply does not hold. ValueKeepingContracts refuses such a schema at compile time, for the production table and any injected one. A client whose type argument names a table it was not given now returns AnalysisCapabilityUnavailableError for a method with no installed contract instead of throwing.
* refactor(hopper): read the startup handshake through the health reply contract (#10853)
parseHopperServerInfo hand-checked each health field that hopperReplySchemas.health already defines. It now parses through that contract, keeps the zod failure as the protocol error's cause, and binds run_id to the launch.
* refactor(kuna): read the project deadline through readRuntimeSetting at each request (#10837)
* refactor(kuna): read the project deadline through readRuntimeSetting at each request
The string, call-graph and xref scans read kuna.project_timeout_ms inside the background producer they start, and the project export read it inside the shared startup promise. A malformed launch seed without an override was thrown from there and reached the caller only through the generic failure mapper; a concurrent waiter that swallows the startup promise's rejection never saw it. Each site now reads the deadline with readRuntimeSetting before starting its work and returns the refusal as that request's value; the export start returns it to each caller that needs the project, and the next request reads it again.
* fix(kuna): return a call-graph deadline refusal from analyze_function
The dossier reported any relationship failure as unavailable call coverage. A call-graph deadline refusal is the request's own setting failure, so it now returns as that ConfigurationError, as cleanup and cancellation already did.
* refactor(kuna): require the accepted project deadline on every export request
Remove acceptedExportDeadline's fallback read of the throwing configuration view. KunaProjectRequest and KunaExportRequest now require the deadline the starting operation accepted; the session passes the one it admitted through readRuntimeSetting into #lookupOrBuild. Direct cache callers in tests take it from one shared helper, withKunaProjectDeadline.
* test(mcp): carry main's Evidence-format const count (#10995) beside the bridge reply setting's
* fix(process): clean Hopper and Ghidra sessions up under the ceilings their start admitted
Cleanup requests re-admitted bridge.max_request_bytes and bridge.max_reply_bytes from the current settings. Once an override covering a malformed launch seed was reset, Hopper's shutdown_document and Ghidra's closing checkpoint_program and shutdown were refused before writing, leaving Hopper's document and files or Ghidra's JVM behind. Each session now keeps the ceilings it admitted at start, and its cleanup requests use them.
* fix(hopper): reauthenticate for cleanup under the session's admitted ceilings
The cleanup reconnect's health handshake still re-admitted the ceilings from the current settings, so a seed refused after an override reset blocked reauthentication and the shutdown after it. It now uses the same session ceilings as the shutdown requests, through one #cleanupLimits accessor.
* refactor(process): report an NDJSON line above its ceiling as a limit parse failure
Utf8NdjsonOversizeError now extends ParseFailureError<"limit"> (format ndjson, limit ndjson-line-bytes) and keeps the observed bytes the transport error reports, so parseFailureFromCause recognizes it like every other byte-format budget.
* refactor(ci): plan change scopes through an injectable Git owner
ciChangeScope.test.ts spent about five processes per case (copy the seed
repository, git add, commit and rev-parse, then node plan.mjs with its own
merge-base and diff) to ask which lanes a path selects. Move the planner
into change-plan.mjs as planChanges(request, repository), keep plan.mjs as
the workflow entrypoint over gitRepository(), and answer the path and
package-script tables from an in-memory commit. History, renames,
deletions, push baselines, malformed metadata and GITHUB_OUTPUT keep the
Git fixture or the spawned entrypoint.
378 tests: 30.6 s elapsed and 32.6 s CPU before, 4.0 s and 4.0 s after.
* test(ci): cover workflow environment wiring through the plan entrypoint
* fix(electron): refuse captures handed off to an already-running app
When another instance holds the app's single-instance lock, Electron forwards the launch and its arguments to that instance. The active hook now stops the launched app at the refused requestSingleInstanceLock call and reports the refusal in its snapshot; the provider fails the capture with application_already_running before driving it.
* fix(electron): stop pending actions on a late lock refusal and keep the final snapshot
* docs(electron): describe late single-instance refusals during actions
* test(electron): join the late-refusal capture even when the test fails
The m68k, MIPS, MIPS16 shared, MIPS relocatable, PowerPC, RISC-V and DOL lanes kept their mkdtemp workspace for diagnosis whenever verification or cleanup failed, and removed it with rm after success. VerifierFixture now allocates the workspace (a plain root: Ghidra children use it as TMPDIR). Every failure branch that names the retained workspace records that reason with fixture.retain, and success releases the fixture. The verifier-fixture-root baseline shrinks by 7.
* refactor(setup): require the platform in client-layer readers and private-profile helpers
* refactor(setup): drop the client-layer readers' unused host import
The agent-client verifiers keep their lab directory as the run's artifact record (receipt, transcripts, profiles, Evidence) and name it as artifacts in the receipt, so it is not a fixture root that release should remove. VerifierFixture.keepArtifacts allocates such a directory canonically, never removes it, and names it on stderr at allocation so a run that fails before its receipt still reports where its artifacts are. The 11 client verifiers use it, and the verifier-fixture-root baseline shrinks by 11 sites.