Commit Graph
2553 Commits
Author SHA1 Message Date
morluto 76c71f0dbd refactor(verify): keep each counted guard's baseline in its own file (#11316)
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.
2026-11-15 11:14:59 -05:00
morluto 0f53849160 feat(javascript): make the member-mutation work allowance a runtime setting (#11181)
* 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.
2026-11-16 00:05:14 +08:00
morluto c56fffcd75 docs(javascript): record pushed effect invalidation for semantic reads (#11188)
* docs(javascript): record pushed effect invalidation for semantic reads

Record the decision that recorded member effects and eval rebinding invalidate reads through one effect log owner instead of 32 reader-side checks, with the slice order, per-slice corpus measurement and stop conditions.

* docs(javascript): require invalidation to reach dependent cached values

* docs(javascript): record the rejected global-reference fold and its measurements

* docs(adr): record per-target cache invalidation as measured and declined
2026-11-16 00:03:09 +08:00
morluto e09bbcaf5f refactor: report NativeAOT, Interface Builder and owned-MCP output limits through the shared limit owners (#11194)
* 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.
2026-11-16 00:02:47 +08:00
morluto 6ec19b4b01 fix(wasm): report interface comparison coverage unknown when either side has no interface facts (#11243) 2026-11-15 23:57:29 +08:00
morluto dac48a1783 test(lifecycle): share late-handshake, final-cleanup cancellation and cleanup-retry controls (#11245)
* test(lifecycle): share late-handshake, final-cleanup cancellation and cleanup-retry controls

* test(lifecycle): move seven hand-rolled phase gates onto the lifecycle harness (#11289)
2026-11-15 23:56:46 +08:00
morluto 067f4ff98b fix(managed): report comparison and native-verification coverage unknown when an input cannot tell (#11257)
compare_managed_members reported partial when a side's member inspection was unavailable or when selected-scope agreement could not be established, and verify_managed_native_boundaries reported partial when the boundary inspection was unavailable. Both now fold their inputs with joinCoverageStatus, so unknown wins beside a known gap. The status enums gain unknown (additive).
2026-11-15 23:56:18 +08:00
morluto 7aea9843bc refactor(mcp): register service-backed tools through one contract binding loop (#11264)
* 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
2026-11-15 23:55:31 +08:00
morluto b41fe5a238 fix(release-exposure): count scan limitations past the cap instead of dropping them silently (#11267)
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.
2026-11-15 23:54:42 +08:00
morluto 8f8683544e fix(native): record breakpoint hits LLDB reports as exceptions and keep coverage partial for unattributed stops (#11276) 2026-11-15 23:50:48 +08:00
morluto 936cef0a5b docs(native): scope function-matching complete-within-inputs to supplied inventories (#11279)
* docs(native): scope function-matching complete-within-inputs to supplied inventories

* docs(native): say partial covers unresolved as well as unpaired rows
2026-11-15 23:49:33 +08:00
morluto c931ba43ab fix(managed): report a managed graph over an unavailable source as unknown (#11280) 2026-11-15 10:34:17 -05:00
morluto c272d050b1 docs(adr): split src/domain by subsystem behind one entry point (#11231)
* docs(adr): split src/domain by subsystem behind one entry point

* docs(adr): say project references do not enforce the entry rule
2026-11-15 09:57:16 -05:00
morluto b4cc1c998b test(mcp): record the advertised-schema summary per tool (#11192)
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
2026-11-15 22:11:06 +08:00
1995ab7fce fix(elf): admit Xtensa through canonical target contracts (#11178)
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>
2026-11-15 22:09:41 +08:00
morluto 5c53bcaed8 feat(mcp)!: retire the full schema profiles and declare schema facts as typed annotations (#10693)
* 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.
2026-11-15 21:51:18 +08:00
morluto 863d82e370 feat(errors): name a deadline's parameter or environment control in details.limit (#11172) 2026-11-15 21:49:35 +08:00
2d4a97b405 fix(managed): compare verified same-artifact TypeSpec targets (#11163)
Resolve target rows at the metadata producer and compare proven same-artifact TypeSpec targets without inventing nominal identity. Keep malformed, missing, wrong-kind, historical and cross-artifact references unknown.

Fixes #10396.
Builds on #10400.

Co-authored-by: 2SinceJuly <202121119+2SinceJuly@users.noreply.github.com>
Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
2026-11-15 21:41:16 +08:00
morluto ecf5372627 ci(guards): run the architecture guards in the pre-push check (#10948)
* 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
2026-11-15 21:38:59 +08:00
morluto bd46f3c1ee test(mcp): compile advertised patterns with the regex engines clients run (#10982)
* 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
2026-11-15 21:38:23 +08:00
morluto d07ab79f9b fix(javascript): refuse member-mutation work beyond a fixed allowance (#11144)
* fix(javascript): refuse member-mutation work beyond a fixed allowance

Member-mutation indexing retained every scheduled alias state until its query finished, with no bound on the phase. Conditional self-selections before one member write (#8294) multiplied those states until the worker heap was exhausted.

Charge each scheduled alias state to a per-phase work meter. Past 524,288 states the phase is refused with a resource_constraint that names the fixed work allowance. The application and web bundle workers carry the reported limit to the parent, which rebuilds the refusal with that allowance instead of heap advice.

Refs #8294 #6127 #6344 #7170 #7510 #8298

* test(javascript): prove the member-mutation meter spans the phase
2026-11-15 21:34:13 +08:00
morluto 8110c98c72 fix(process): give process-command failures their own recovery instead of doctor (#11101)
* fix(process): give process-command failures their own recovery instead of doctor

* test(process): check the delivered process-command recovery instead of banning a word
2026-11-15 20:50:38 +08:00
morluto a8e472967b refactor(process): read Ghidra and Hopper replies through one bounded JSON-line reader (#10699)
* 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.
2026-11-15 20:37:21 +08:00
morluto 483f53fdef fix(electron): refuse captures handed off to an already-running app (#10989)
* 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
2026-11-15 20:35:48 +08:00
ce9bdc526a fix(agentenv): settle cancelled long runs and preserve teardown receipts (#11095)
Bound AgentEnv long-run cancellation, preserve partial command facts and
complete computer teardown before resolving retained jobs. Includes the
supervisor and cleanup implementation from #10675 plus polled-receipt and
explicit-unknown exit-status fixes.

Validation: 14 Python controller tests, 31 host tests and 30 compiled
MCP/lifecycle tests passed. Static, documentation and discovery checks
passed. Real AgentEnv deployment was not exercised: runner unavailable.

Fixes #10676
Related: #10675

Co-authored-by: Adolanium <94890352+Adolanium@users.noreply.github.com>
Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
2026-11-15 20:20:47 +08:00
Ysopwnableandgensokyo-dev 29dd58c96d fix(il2cpp): resolve ELF ABS64 and GLOB_DAT image definitions (#11075)
Derive bounded ELF ABS64 and GLOB_DAT link-image values for IL2CPP module
scanning while preserving the default conservative relocation policy.
Retain ambiguous hash declarations, validate complete hash-table mappings,
and disclose runtime-binding limitations in CLI and MCP evidence.

Validation: 408 focused tests, check:fast and docs:check passed. Public
CLI/MCP and independently linked ARM64/x86-64 ELF arithmetic were checked;
the original Unity binaries remain unavailable.

Refs #10478
Related: #10495

Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
2026-11-15 19:55:43 +08:00
morluto fd2eabb12a feat(setup): manage fx, Junie, Goose and Kilo registrations through the client table (#10709)
* feat(setup): manage fx registrations with its private profile modes

fx becomes a client-table row, so doctor, update and uninstall see the
registration rea mcp add wrote, and setup offers fx when ~/.fx or
FX_HOME exists. Setup now publishes a private profile through the same
owner as mcp add (folder 0700, file 0600) and repairs those modes when
the row is otherwise current. Doctor parses fx's array launcher instead
of reporting it invalid.

* fix(setup): judge fx's private profile by its targets and keep its cleanup owner

Address review: privateProfileModesCurrent reads the modes of the canonical file and folder, so a linked profile becomes current; clientConfigurationAligned applies the same modes rule as plan and apply; a private-folder close failure reaches the caller with its cleanup owner instead of becoming a write failure; and the setup plan names the folder and file modes it sets.

* fix(setup): keep fx's recovery backups owner-only beside a linked profile

Setup and uninstall copied a private profile's original bytes with copyFile, so a backup beside a linked ~/.fx/mcp.json kept the target's readable mode outside the folder setup secures. Both now use the owner-only backup owner that mcp add already used.

* feat(setup): manage Junie registrations and apply its location policy (#10712)

* feat(setup): manage Junie registrations and apply its location policy

Junie becomes a client-table row, so doctor, update and uninstall see
the registration rea mcp add wrote, and setup offers Junie when its home
exists. Whether Junie loads its default user MCP file now has one owner,
read through the client policy block that setup, doctor and mcp add
already consult: setup and mcp add refuse, and doctor reports, a
registration Junie would not load.

* fix(setup): read Junie's user configuration as strict JSON

Address review: Junie loads config.json as strict JSON, so comments, trailing commas or an empty file leave its MCP location choice unknown, as before the move into the row. Filesystem tests also clear JUNIE_*, FX_HOME and MCPORTER_CONFIG so a developer's own client locations cannot be inspected or removed.

* test(setup): keep the detected client order out of the lifecycle suite body

The expected detection order grew with each folded client until the suite callback exceeded the max-lines-per-function lint limit.

* fix(update): report a client whose policy setup refuses instead of failing maintenance

Junie's disabled default locations (and Gemini's settings policy) make
doctor report the registration stale, but setup refuses that client, so
selecting it failed every other client's maintenance. The registration
inspection now carries the setup blocker, and maintenance lists that
client with its reason instead of selecting it.

* refactor(setup): read fx's private-profile host through HOST_PLATFORM

The fx private-profile mode checks and permissions describe the host REA runs
on, so they take it from the host-platform owner instead of reading
process.platform (refused by the ratchet from #10911).

* feat(setup): manage Goose registrations across its configuration layers (#10715)

* feat(setup): manage Goose registrations across its configuration layers

Goose becomes a client-table row, so doctor, update and uninstall see
the registration rea mcp add wrote, and setup offers Goose when its user
file or system file exists. Goose loads its system file and
GOOSE_ADDITIONAL_CONFIG_FILES beneath the user file and overlays each
extension one field deep; doctor now reports that effective
registration, while setup, update and uninstall change only the user
file. Doctor also parses Goose's cmd launcher instead of reporting it
invalid.

* fix(setup): keep Goose's layered environment and load order through setup and uninstall

Address review: read Goose's env alias and apply requested settings over envs; keep an inherited lower envs map instead of masking it with an empty default; keep each layer's last load so a repeated user file keeps its precedence; split GOOSE_ADDITIONAL_CONFIG_FILES with Windows quoting; validate only the effective environment across read-only layers; refuse uninstall while a read-only layer keeps REA enabled; and clear Goose's location variables in tests.

* fix(setup): apply requested Goose settings over the inherited environment

* fix(setup): leave lower-layer Goose settings in force and read admin layers read-only

Setup no longer writes its default timeout, enabled, name or description into Goose's user entry when only a lower layer declares that setting, so the inherited value keeps applying and a repeat setup stays current. Uninstall reads Goose's read-only lower layers where Goose reads them, without the write target's ownership check, so an administrator-owned system file no longer blocks removal. The client table accepts an injected Goose system-config path so tests no longer read or skip on the host's /etc/goose/config.yaml; adds a shadowed-invalid-lower-layer regression.

* fix(setup): keep an inherited Goose env alias's spelling when writing the user entry

Goose overlays each extension field separately, so writing envs into the user file beside a lower layer's env alias left both in force, which readers reject as ambiguous. Setup now writes the user map under the alias a lower layer declares, and mcp add neither adds a default envs map beside an inherited alias nor renames a user alias the lower layer shares.

* test(setup): inject Goose's system layer into mcp add selection

resolveMcpRegistrationClients accepts the caller's system configuration paths, so the Goose layers test no longer reads the host's /etc/goose/config.yaml.

* fix(setup): report REA's Goose registration that only a read-only layer defines

* test(setup): pass an isolated Goose system path through update maintenance and the mcp add table

* fix(setup): judge Goose uninstall by the merged entry and stop on an unreadable lower layer

* fix(setup): select Goose's env alias by presence so a null alias stays malformed

* fix(setup): use the shared record predicate for Goose's layered merge after #10588

* feat(setup): give Kilo a client-table row with an async layered location (#10823)

* feat(setup): give Kilo a client-table row with an async layered location

The client table gains a layeredLocation hook and resolveSupportedClients, which awaits it. Kilo's row resolves its user-scope layers through the same owner rea mcp add uses (KiloClientLocation), so both pick the same write target; the synchronous row keeps kilo.json in Kilo's config root with every candidate layer. The table refuses a relative KILO_CONFIG, KILO_CONFIG_DIR or XDG_CONFIG_HOME because Kilo's working directory is unknown.

Refs #7181

* fix(setup): treat every folder Kilo reads as evidence that it is installed

* fix(setup): normalize Kilo's absolute table paths and parse its command array

* feat(setup): resolve the client table's layered locations in setup, doctor, update and uninstall (#10832)

SetupHost (detection and the selectable table), ClientRegistrationStatus, uninstall, doctor's selected-registration environment and the mcp add catalog now read resolveSupportedClients, so each sees the Kilo layer Kilo writes rather than the synchronous default.

Refs #7181

* docs(adr): record the one-client-table decision for setup and mcp add

* fix(setup): refuse to read a client row whose location is unresolved

Doctor's selected-client settings read a Kilo row whose relative
KILO_CONFIG left only path evidence, so it inspected a file relative to
REA's folder. The shared configuration reader now refuses such a row,
so every reader reports the registration as unreadable.

* docs(adr): name the row's layeredLocation hook

* feat(setup): read KILO_CONFIG_CONTENT as Kilo's top read-only layer (#10851)

* feat(setup): read KILO_CONFIG_CONTENT as Kilo's top read-only layer

* fix(setup): refuse a Kilo uninstall that would expose or keep another layer's rea entry (#10852)

* fix(setup): recheck KILO_CONFIG_CONTENT at write time and name it in doctor's recovery

* refactor(mcp): register Kilo through its table row and remove the registration-only catalog kind (#10862)

* refactor(mcp): register Kilo through its table row and remove the registration-only catalog kind

* refactor(mcp): resolve user-scope targets in the catalog so location modules never import the client table

* test(setup): cover Kilo's KILO_CONFIG, home-folder and inline-only layers

* fix(setup): report REA's Kilo entry that only KILO_CONFIG_CONTENT sets during uninstall

Doctor reports a registration that KILO_CONFIG_CONTENT alone defines, but
uninstall found no file entry and reported Kilo as skipped. Uninstall now
reports it once as failed and names the variable to edit.

* docs(adr): record Kilo's inline layer in the client-table decision

* fix(setup): compare Windows environment names without case when a higher layer overrides REA's entry

Kilo's inline-layer check and Gemini's system-override check compared
environment maps by exact key, so an equal Path beside PATH on Windows was
reported as an override and setup refused the client. Both compare the
entry as the client launches it.

* fix(update): leave a stale Kilo registration KILO_CONFIG_CONTENT sets out of maintenance

Setup refuses a refresh the inline layer would override, and one refused
client failed every other client's maintenance. The registration inspection
now names the inline layer as the setup blocker of a stale entry, and
maintenance reports Kilo with that recovery instead of selecting it.

* fix(setup): refuse relative JUNIE_HOME and Cline CLI overrides like the other client roots (#10909)

* fix(setup): refuse relative JUNIE_HOME and Cline CLI overrides like the other client roots

* fix(setup): refuse Windows client paths without a drive that follow the client's current drive

* fix(update): select maintenance clients through setup's own refresh refusal check

Maintenance predicted setup's refusal with its own rule: any inline rea entry
blocked a stale registration, while an aligned one was never checked. A
harmless KILO_CONFIG_CONTENT entry (enabled: true) kept a stale Kilo launcher
pinned, and an inline value setup would not write (an extra environment
variable) let an aligned Kilo registration block every client's maintenance.

Setup's refusal checks (client policy, then the inline override comparison of
the entry it would write) now live in one reader that setup's alignment check
and maintenance share. Maintenance asks it for every owned enabled candidate
and reports a refused client with setup's remediation and its real state.

* fix(update): keep failed refresh-check reads owned by the supplied maintenance scope

clientRefreshRefusal read the client's layers, target file and policy without
the caller's resource scope, so a close that failed during those reads threw a
cleanup failure no scope retained; maintenance then reported it as text and
the scope's close could not retry the descriptor. The refresh reader now takes
the scope and passes it to every read.

* fix(setup): take the Kilo layer readers' default host from HOST_PLATFORM and the shared record predicate

After rebasing on main (#10911, #10588): the new inlineRegistrationOverride and
resolveSupportedClients defaults read the running host through the
host-platform owner, and the layered launcher merge uses isRecord.

* fix(update): keep an unreadable layer's reason in the refresh refusal check

readClientRefresh returned undefined for an unreadable or malformed layer, so
clientRefreshRefusal reported no refusal and maintenance selected a client
whose setup preflight then failed. The reader returns the typed file failure,
and the refusal check reports its detail like setup's preflight does.

* fix(setup): count REA's entry in KILO_CONFIG_CONTENT as Kilo lifecycle evidence

Doctor, update and uninstall inspected a client only when one of its folders
existed, so a registration set only by KILO_CONFIG_CONTENT was skipped. A
read-only inline layer that sets REA's entry is now evidence on its own.

* fix(update): report a client whose only conflict is a discovered provider path as needing setup

* fix(update): match discovered settings by the platform's environment identity

* fix(update): preflight maintenance with setup's write-target checks and treat discovered inline paths as unknown
2026-11-15 19:39:33 +08:00
morluto 607a7872bb refactor(jeb): classify JEB failures through the shared provider classifier (#10831)
* 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 (#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
2026-11-15 19:37:46 +08:00
morluto 15de1d4dce feat(evidence)!: require JavaScript, browser, managed and reference-source facts their producers always write (#10990)
* feat(javascript)!: require the runtime parse-failure count and active Electron execution paths

The V8 Inspector projection always writes scripts.parse_failed_total and the
active Electron service always commits parameters.resolved_paths, so both are
now required. Runtime reconciliation reads the admitted execution paths only
and no longer falls back to request-spelled paths in parameters, and checks
the observed executable path against them on every capture.

JavaScriptStaticAnalysis.routes and module_runtime keep their omission, which
current producers still make (a checkpoint that cannot retain route facts; no
recovered factory runtime); their comments now say so instead of naming
historical analyses.

BREAKING CHANGE: JavaScript runtime observations without
scripts.parse_failed_total, and active Electron Evidence without
parameters.resolved_paths, are refused as malformed; recreate the observation.

* test(javascript): give the shared Node reconciliation fixture the required parse-failure count

* feat(browser)!: require browser observation facts their producers always write (#10991)

* feat(browser)!: require browser observation facts their producers always write

The source-map fetcher always writes effective_url (string or null), the web
bundle analyzer always writes schema_property_names_coverage, and the CDP
storage fingerprinter always marks IndexedDB schema fingerprints with
projection_version 2. Each is now required. The IndexedDB marker is tied to
its scope by a discriminated union and derived from the scope in the
producer, so compare_web_captures no longer carries the store-only
projection branch.

response_identity and the WebMCP consequential hint keep their omission,
which current producers still make (a body not fully read; an invalid
annotation); the guide no longer attributes it to historical captures.

BREAKING CHANGE: source-map items without effective_url, WebMCP declarations
without schema_property_names_coverage, and IndexedDB schema fingerprints
without projection_version are refused as malformed; recreate the capture.

* feat(managed)!: refuse members beside a null signature resolution (#10992)

* feat(managed)!: refuse members beside a null signature resolution

signature_resolution is null only when member decoding exceeded capacity,
and the capacity-recovery producer then examines no members. The managed
member inspection schema now enforces that: a null resolution with any
field, method, member reference, call edge or field access is malformed.

With that rule, every admitted inspection with members attests type-name
resolution, so the raw-digest fallback is unreachable and is deleted:
comparableSides, hasSignatureResolution, the comparison's
signatureResolutionExamined flag and the "does not attest" limitation. The
shared workflow example that carried resolved digests beside a null
attestation now attests resolution.

BREAKING CHANGE: managed member Evidence with a null signature_resolution
and any examined member is refused as malformed; recreate the observation.

* docs: describe historical Evidence by what the format revision now refuses (#10993)

SQLite worker_timeout_ms is required, so the SQLite guide no longer reads a
missing value as unknown settings. The IL2CPP, ESP image and process capture
guides now say that bundles exported without evidence_format are refused,
while a standalone record from an earlier release is still read with the
stated omission semantics (records carry no format marker yet). The route
inventory guide describes a parent without route_inventory by its meaning
rather than by its age.

* refactor(javascript): require static analysis coverage from the JavaScript worker (#11001)

* refactor(javascript): require static analysis coverage from the JavaScript worker

Both JavaScript static producers always write analysis_coverage: the
analyzer measures it on every parsed source and the failed-parse result
carries an unexamined coverage. The worker schema and the static analysis
type therefore require it, the preload-discovery checkpoint copies it
unconditionally, and ElectronBoundaryAnalysis reads Electron applicability
and examination limits from it alone. The fallback that re-derived Electron
use from semantic module links and retained references for analyses
without coverage is deleted, as are the graph and builder branches for a
missing coverage.

Retained Evidence graph observations are unchanged: the analysis view still
counts a stored JavaScript observation without analysis_coverage as
unmeasured, since standalone records carry no format marker yet.

The worker runs from the same build as its parent, so no external caller
sees the stricter reply schema.

* feat(browser)!: require storage fingerprint, Link context, mapping index and invalid-map identity facts (#11002)

* feat(browser)!: require storage fingerprint, Link context, mapping index and invalid-map identity facts

Each field is written by its only producer on every capture:

- The CDP storage capture always writes content_fingerprints,
  fingerprint_algorithm and fingerprints_complete, so their schema defaults
  are removed.
- The CDP Link header projection always writes context_url and
  context_scope, so they are required.
- The source-map fetcher writes source_index and declaration_index on every
  mapping row, so they are required.
- The fetcher judges a map invalid only after reading its complete body. A
  separate invalidSourceMapItem builder now takes the response identity as a
  required argument, and the schema's invalid member requires
  response_identity; fetch_failed keeps it optional because those rows can
  come from a body that was not fully read.

The browser and source-location guides no longer describe these fields as
optional for older saved captures.

BREAKING CHANGE: page inspections without the storage fingerprint fields or
Link context fields, source-map mapping rows without their indexes, and
invalid source-map items without response_identity are refused as malformed;
recreate the capture.

* test(browser): keep source-omission coverage on a still-defaulted field and refuse a missing fingerprint algorithm

* feat(browser)!: require web-bundle target origin, inspection producer facts, scenario redirect state and exclusion kind (#11014)

feat(browser)!: require web-bundle target origin, page inspection producer facts, scenario redirect state and exclusion kind

Each field is written by its only producer on every record:

- buildCaptureObservation always writes capture.target_origin (the validated
  origin or null), so analyze_web_bundle Evidence and both web-bundle view
  results require it; the view's conditional spread is gone.
- The CDP page capture always writes accessibility node states, script
  frame_id (null when unattributed) and worker opener_target_id and
  parent_frame_id, so their schema defaults are removed.
- PlaywrightScenarioNetwork writes redirected_from_transaction_state exactly
  when the predecessor ID is null, so request events now require it there and
  still refuse it on linked events.
- The reference-source importer writes entry_kind on every exclusion
  (unknown when the kind was not observed), so it is required.

The browser scenario contract and CLI guide no longer describe older
captures or exclusions without these fields.

BREAKING CHANGE: web-bundle analyses without capture.target_origin, page
inspections without these script, accessibility or worker fields, scenario
network events with a null predecessor and no redirect state, and reference
source graphs whose exclusions lack entry_kind are refused as malformed;
recreate the capture or re-import the source.
2026-11-15 19:37:10 +08:00
Ysopwnableandgensokyo-dev 08420be3bd fix(ghidra): explain Program reuse after startup timeouts
Explain Program reuse in cold-start timeout recovery and distinguish query
snapshots from saved Ghidra analysis. Preserve explicit startup-mode selection,
first-analysis requirements, and custom-analyzer choices.

Refs #10365

Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
2026-11-15 19:14:41 +08:00
morluto 7181e7f515 perf(mcp): advertise large output union alternatives as shared definitions (#11005)
* perf(mcp): advertise large output union alternatives as shared definitions

inspect_analysis_view's advertised output schema held every view result inline in one union definition, which the MCP SDK's default validator compiled into one function: about 1.7 s CPU and 500 MiB peak. Large union alternatives now become definitions of their own and identical definitions are merged, at the owning output projection. Tests that check results against the advertised output contract now compile that schema instead of a separate inline projection, which overflowed the main-thread stack under forked workers.

* perf(tests): run source test projects in forked workers (#11016)

Thread workers of the source lane share one process, which grew to 3.8-5.0 GiB; forked workers keep each heap separate so the largest process stays near 1.2-1.8 GiB.

* test(mcp): select the analysis view contract from its family
2026-11-15 06:13:21 -05:00
morluto 0ddc732c69 docs: describe omitted output schemas as the default in README translations (#10658)
* 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

* 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.
2026-11-15 18:37:55 +08:00
morluto 4126698407 feat(mcp): project compact input schemas into the portable advertised dialect (#10632)
* 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.

* 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): keep an allOf member's tuple tail with its own positions

* fix(mcp): relax references into negations and conditionals presentation may remove

* 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): 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): expect the advertised base64 schema to omit the unbounded pad-bit probe

* fix(mcp): protect references into unevaluated keywords lost evaluation removes

* 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.
2026-11-15 18:26:13 +08:00
morluto da8f366596 fix(mcp): register fx and MCPorter where they read their configuration (#10698)
* fix(mcp): register fx and MCPorter where they read their configuration

fx reads $FX_HOME/mcp.json when FX_HOME is set, and MCPorter reads
MCPORTER_CONFIG exclusively or prefers an absolute XDG_CONFIG_HOME file.
rea mcp add always wrote ~/.fx/mcp.json and ~/.mcporter/mcporter.json,
which those clients then ignored. Resolve both from the clients' own
rules, and refuse an FX_HOME fx itself refuses.

* fix(mcp): detect MCPorter by its selected file and refuse FX_HOME aliases

Address review findings: MCPORTER_CONFIG now also names the detection
marker, so default registration includes MCPorter when only that file
exists, and is resolved with path.resolve as MCPorter does. FX_HOME with
. or .. segments is refused, so the private profile mode cannot reach
the home folder through an alias. Override tests use the host's path
rules for real temporary paths.

* fix(mcp): never apply fx's private folder mode to the home folder

A linked parent in FX_HOME can name the caller's home folder after the textual root and home checks pass. The private profile writer now compares the opened folder's identity with the caller's home folder and refuses before changing modes or publishing.

* feat(setup): let doctor, update and uninstall see MCPorter registrations (#10702)

* feat(setup): let doctor, update and uninstall see MCPorter registrations

MCPorter becomes a client-table row, so a registration rea mcp add or
rea setup --client mcporter wrote is inspected, refreshed and removed like
other clients. MCPorter already imports setup-managed clients'
registrations, so the row is applicable only through REA's own
registration: setup never selects it from detection and doctor reports
it only when its file holds a rea row or the caller selects it. A
relative MCPORTER_CONFIG is refused because MCPorter resolves it per
command directory.

* fix(setup): detect MCPorter by the folder of the file it loads

With an absolute XDG_CONFIG_HOME and only a legacy ~/.mcporter file, the row returned the legacy file but the absent XDG folder as its detection evidence, so default registration and setup skipped MCPorter.

* fix(setup): keep an unresolved client unknown without blocking others' maintenance

A client whose configuration location cannot be resolved (for example a relative MCPORTER_CONFIG) is reported as unknown, never missing or absent. Update maintenance leaves each unresolved client out of its scope and names it with its own recovery, while still maintaining the other clients; uninstall still stops, because it cannot tell whether that client's file holds REA's row.

* docs(setup): restore MCPorter's release note and client-table row after rebase

* fix(update): keep unknown clients in every failed maintenance plan
2026-11-15 18:25:02 +08:00
morluto 422cff00ab refactor(process): give POSIX PATH search order one owner (#10891)
* refactor(process): give POSIX PATH search order one owner

Program lookup, the packet-tool locator and Jadx's Java lookup each split
PATH and resolved empty and relative entries themselves. They now take the
candidates from posixPathCandidates in src/process/executableSearch and keep
only their own probe and failure reporting. A counted architecture guard
keeps new PATH splits from returning; the remaining Windows and Ghidra
searches are baselined.

* refactor(process): move the colon-split PATH searches onto the search owner

REA's launcher-alias lookup and Ghidra's macOS Java lookup split PATH on
":" and skipped relative entries themselves; they now use
posixPathCandidates with relativeEntries: "skip", and the guard counts
colon splits of PATH as well.

* fix(process): produce PATH candidates lazily so an unused entry cannot fail an earlier match

* refactor(process): give Windows PATH/PATHEXT search order the executable search owner (#10916)

* refactor(process): give Windows PATH/PATHEXT search order the executable search owner

* fix(process): keep later PATH entries searchable after a stray quote or a removed working directory

* fix(reverse): refuse a Windows objdump or Rizin command that PATH does not find (#10953)

* fix(reverse): refuse an objdump or Rizin command Windows PATH does not find instead of launching the bare name

* fix(reverse): report a Windows lookup refusal after cancellation and to capability discovery

* fix(desktop): search a Windows host's PATH as Windows does when doctor looks up a program

programOnPath sent every host through the POSIX PATH search, so on Windows a
relative PATH entry reached absoluteCallerPath with platform linux and the
Windows working directory, which resolved against itself until the stack
overflowed. programOnPath now searches PATH and PATHEXT through
windowsPathCandidates on a Windows host, and absoluteCallerPath refuses a
non-POSIX working directory with a TypeError instead of recursing.
2026-11-15 18:24:04 +08:00
morluto e793fd6808 feat(mcp): name the next operation when a completed result overflows the response budget (#11015)
* feat(mcp): name the next operation when a completed result overflows the response budget

* test(mcp): assert next_operation for every overflow recovery route
2026-11-15 18:22:08 +08:00
morluto b572dbaf25 feat(setup): mark a shared enable:false registration as a manual conflict in doctor status (#11019) 2026-11-15 18:21:40 +08:00
morluto 4aefe2daec fix(errors): name rea doctor only for providers it can check (#10565)
* fix(errors): name rea doctor only for providers it can check

An unclassified ProviderAdapterError, an unreadable result and the final
presentation fallback all told the caller to run rea doctor, although doctor
checks only hopper, ghidra, ida, jadx, kuna and desktop_build. The doctor
scope list moves into the domain so presentation can consult it: covered
providers get `rea doctor --provider ID`, others are told plainly that the
failure is unclassified and where its reason is reported.

Producers whose actionable reason lived only in details.diagnostics now
author it: Ghidra's committed-profile conflicts share one owner that says to
reopen the target, closed analysis clients share one owner, and a jadx-reported
request failure no longer points at provider readiness. The deep-analysis
profile rejection names doctor only for doctor-checkable providers. Dead
presentation branches for tags handled earlier are removed.

* fix(errors): make provider recovery actionable

* test(android): remove brittle recovery wording check

* test: wait for observed child exit before cancellation

* test(errors): check that doctor is not offered instead of matching recovery prose
2026-11-15 18:09:40 +08:00
morluto babfc87ec0 fix(mcp): present discover_tools input through the selected profile (#10592)
* 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): validate advertised dialect by selected profile

* fix(mcp): preserve schema widening through references

* fix(mcp): refuse zero-padded repeat bounds and record host-specific path patterns

* fix(mcp): keep references valid when presentation widens their targets

* fix(mcp): relax references into negations and conditionals presentation may remove

* 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): 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): 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

* 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): 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

* 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
2026-11-15 18:08:12 +08:00
morluto 80755a9d16 fix(doctor): refuse Ghidra on Windows when REA_GHIDRA_PROJECT_PATH selects an existing project (#10981)
* fix(doctor): refuse Ghidra on Windows when REA_GHIDRA_PROJECT_PATH selects an existing project

Runtime refused an existing project on Windows before admission, but doctor
did not read REA_GHIDRA_PROJECT_PATH, so it reported Ghidra ready for a
registration the provider then refused. The existing-project host check is
now part of Ghidra's admission owner; doctor reads the setting from the
client's registration and reports a failed existing_project check, and the
provider projects the same refusal. An unresolved project path makes doctor's
Ghidra answer unknown only on Windows, the one host it affects.

* test(ghidra): read the existing-project setting through doctor's system inspection
2026-11-15 18:03:20 +08:00
morluto 27b732e957 refactor(kuna): decide Kuna readiness once for doctor and runtime (#10997)
Runtime availability admitted Kuna from its settings and a runnable command,
while doctor also required a passing version probe and cache inspection, so
doctor could report Kuna unavailable while runtime admitted it.

admitKunaCommand is now the one readiness decision: provider availability,
the installation inspection and doctor's installation check all project it.
Doctor reports the version probe and the cache inspection as separate health
checks (version, cache) beside admission; they no longer decide availability,
and runtime still does not pay for them. Setup continues to list Kuna as a
ready provider only when every check passes.
2026-11-15 18:01:05 +08:00
morluto 26520bd290 refactor(providers): finalize apktool and shared artifact resources through runOwned (#10653)
* 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
2026-11-15 17:44:13 +08:00
morluto b87b318ae7 refactor(wasm): leave WABT cancellation to the shared owners (#10796)
* 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.
2026-11-15 17:42:49 +08:00
morluto c36649a674 feat(managed)!: require managed Evidence fields that were defaulted only for historical Evidence (#10952)
* feat(managed)!: require managed Evidence fields that were defaulted only for historical Evidence

Member inspections, comparisons, verification results and application graphs
require every field their producers write. nesting_read is boolean and the
bare-name comparison mode for historical observations is deleted;
signature_resolution loses type-names-v1. The unavailable-metadata member
result now writes assembly: null instead of relying on a default.

BREAKING CHANGE: managed records missing these fields are refused as
malformed; recreate the observation.

* docs(managed): drop the remaining historical-omission claims for member Evidence
2026-11-15 17:41:59 +08:00
morluto 3afd8a16ee feat(evidence)!: give Evidence bundles a format revision owned by one module (#10861)
* feat(evidence)!: give Evidence bundles a format revision owned by one module

`src/domain/evidenceFormat.ts` owns Evidence format evolution. Bundles now
carry `evidence_format: 1`; every bundle reader admits only the current
revision and refuses an unmarked or other-revision bundle once, with the
single recreate-observations guidance, instead of admitting earlier shapes
one optional field at a time.

The 6.7-era artifact inventory summary identities are no longer rebuilt:
compare_artifacts checks the current projection only and reports a mismatch
with the owner's guidance. The pre-6.6 identity diagnosis uses the same
guidance. SQLite `worker_timeout_ms` and Dart AOT `string_pool.policy`,
optional only for historical Evidence, are now required.

BREAKING CHANGE: Evidence bundles written by REA 6.x (no `evidence_format`)
are refused by import, comparison, reconstruction and analysis-snapshot
readers as an integrity mismatch telling the caller to recreate the
observations; re-exporting an earlier bundle does not update its format or
identities. Bundles constructed by hand must include `"evidence_format": 1`.
6.7-era artifact summaries are refused by compare_artifacts, and SQLite and
Dart AOT Evidence without the newly required fields fails validation.

* fix(evidence): let the ledger application admit its bundle's format

build_reconstruction_obligation_ledger registered the full bundle schema with the SDK, so an unmarked or other-revision bundle was refused as invalid_request before the format owner ran. Register it like coverage: the application parses the request envelope and admits the bundle.

* feat(pcap)!: read capture Evidence through the current result schema only (#10879)

Delete the pcap read-compatibility schema that defaulted executable identity for historical capture Evidence, and require query_labels_hex (null when unknown) now that every producer writes it. Evidence from an earlier format is refused at the bundle format owner; a record that omits these fields is reported as malformed instead of defaulted.

BREAKING CHANGE: inspect_pcap_capture results always carry query_labels_hex, and analysis views no longer default missing tshark/capinfos identity fields.

* feat(desktop)!: require the effective view policy in desktop state Evidence (#10886)

inspect_desktop_application_state always records the complete policy it used, including sqlite_view_bytes. Drop the optional historical shape and the task draft's absent-policy branch.

BREAKING CHANGE: desktop application state results without effective_view_policy, or with a policy missing sqlite_view_bytes, are refused as malformed.

* feat(android)!: require component declaration facets and reference aggregates in Android Evidence (#10904)

* feat(android)!: require component declaration facets and reference aggregates in Android Evidence

The JADX producers always write the manifest component projection with every declaration facet, target_definition and direct_invokes_coverage. Require them, delete the duplicate observed component schema, drop the unknown declaration coverage that only described historical omission, and give manifest view results their explicit facet.

BREAKING CHANGE: Android package, component and reference results missing these fields are refused as malformed; component declaration_coverage and its summary counts no longer include unknown; manifest view results always carry facet manifest.

* refactor(android): parse package results with the domain schema directly

* feat(browser)!: require the DOM length unit and redirect-scoped prior-hop facts (#10897)

* feat(browser)!: require the DOM length unit and redirect-scoped prior-hop facts

Both CDP producers always declare node_value_length_unit, and the observation session writes redirect_response on every redirect event and no other. Require the unit, drop the diff's absent-unit branch, and admit redirect_response exactly on redirect rows.

BREAKING CHANGE: DOM snapshots without node_value_length_unit and redirect timeline rows without redirect_response are refused as malformed, including inline inspections passed to compare_web_captures.

* docs(browser): drop the historical redirect_response omission note

* feat(native)!: require native Evidence fields that were optional only for historical Evidence (#10946)

Mach-O encryption, slice malformed cause, symbol runtime names and import
facts; ELF export encoding facts and visibility; value-flow limits; load-image
permissions and relocation symbols; Swift type-reference kind and GPU AMD
target metadata are required. Legacy enum values (unrecorded, unknown
visibility, undecodable, modinfo_field_exceeds_limit) and the retained
schemas, view defaults and reinterpretations that served historical Evidence
are deleted.

BREAKING CHANGE: native records missing these fields are refused as
malformed; recreate the observation.
2026-11-15 17:25:31 +08:00
morluto f8e8dc3e7a feat(cutter): name the plugin re-copy recovery for sessions whose plugin source is unknown or modified (#10950)
* feat(cutter): name the plugin re-copy recovery for sessions whose plugin source is unknown or modified

* test(cutter): assert recovery names the owner's platform plugin path
2026-11-15 17:22:30 +08:00
morluto d0fee4e58e fix(mcp): re-emit advertised patterns in a portable regex grammar (#10582)
* 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): validate advertised dialect by selected profile

* fix(mcp): preserve schema widening through references

* fix(mcp): refuse zero-padded repeat bounds and record host-specific path patterns

* fix(mcp): keep references valid when presentation widens their targets

* fix(mcp): relax references into negations and conditionals presentation may remove

* 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): 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): 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

* 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): 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
2026-11-15 17:06:43 +08:00
morluto d044f44d91 feat(setup): inspect, refresh and remove five mcp-add-only clients (#10694)
* feat(setup): give five mcp-add clients one client-table row

Cline VS Code extension, Kiro, Mastra Code, Zed and Amp move from
registration-only catalog entries to rows in the client table. rea mcp add
projects those rows, so doctor, update and uninstall now inspect, refresh
and remove what it wrote, and setup offers each client when detected.

Refs #7181

* refactor(setup): keep FreeBuff's comment on its declaration

The client helpers added for the folded mcp-add clients were inserted
between FreeBuff's profile-directory comment and its declaration. Move
them above it, and drop an import the folded locations no longer use.

* test(setup): refresh an older mcp-add registration through setup

Address review: seed each folded client with an older REA-owned package
runner launcher, then check that doctor reports it stale, update selects
it, setup plans and applies the update, and doctor then reads the
current launcher.

* fix(setup): register Amp in settings.jsonc when that is its settings file (#10717)

Amp reads ~/.config/amp/settings.json or settings.jsonc. REA always
used settings.json, creating a second file beside an existing
settings.jsonc. Use settings.jsonc when it is the only file, and refuse
when both exist because Amp does not document which one it reads.

* feat(setup): manage Cline in VS Code Insiders, VSCodium, Cursor and Windsurf (#10729)

* feat(setup): manage Cline in VS Code Insiders, VSCodium, Cursor and Windsurf

The Cline extension keeps its MCP settings in the global storage of
whichever editor runs it. REA resolved only VS Code's, so a Cline in
another editor was never configured, inspected or cleaned. Add one
client-table row per editor, resolved through the shared Code-OSS user
data rule, and detected where that editor's Cline settings exist.

* test(setup): cover VSCODE_PORTABLE for Cline's editor hosts

Address review: VSCODE_PORTABLE moves only VS Code's own storage, so the other editors keep their settings path; and the older-release note now says which hosts mcp add could register before.

* fix(setup): find a Linux Flatpak Zed's configuration (#10721)

* fix(setup): find a Linux Flatpak Zed's configuration

Zed's config_dir honors FLATPAK_XDG_CONFIG_HOME first, and a Flathub Zed
reads ~/.var/app/dev.zed.Zed/config/zed inside its sandbox. REA always
wrote ~/.config/zed, which that Zed never reads. Honor the override, use
the sandbox folder when it is the only one, and refuse when both a
native and a Flatpak configuration exist.

* fix(setup): treat an XDG_CONFIG_HOME naming the Flatpak folder as one Zed folder

* fix(setup): treat linked native and Flatpak Zed folders as one configuration

* test(setup): expect doctor to report the Amp registration mcp add wrote
2026-11-15 17:04:11 +08:00
morluto 8428de13d3 feat(ida)!: require an explicit protocol for attached IDA registrations (#10846)
* feat(ida)!: require an explicit protocol for attached IDA registrations

An attached registration (the default mode) silently selected the legacy
ida-pro-mcp 1.4 GUI proxy when `protocol` was omitted. The registration
schema now resolves the upstream protocol once: attached registrations
must name `"legacy"` or `"native"`, headless registrations resolve to the
database supervisor, and consumers read the resolved protocol instead of
inferring it from `mode`.

BREAKING CHANGE: an attached IDA registration without `protocol` is refused
with a configuration error naming the choice. Add `"protocol": "legacy"` to
an ida-pro-mcp 1.4 proxy registration (or `"protocol": "native"` for the
ida-free-mcp plugin). Headless registrations are unchanged.

* test(ida): name the protocol in every attached registration fixture

The setup budget fixture still wrote an attached URL registration without a
protocol, and the IDA registration rejection fixtures relied on the missing
protocol as one of several failures. Each now names its protocol, so setup
reaches its timeout checks and every rejection case fails only for its own
constraint.
2026-11-15 17:00:24 +08:00
morluto 66d1527a3d refactor(native): parse native inventory results once at the operation port (#10880) 2026-11-15 16:59:39 +08:00