Commit Graph
2063 Commits
Author SHA1 Message Date
morluto 271e375924 refactor(application): read the host from HostPlatform in the remaining application modules (#11124)
Scenario planning and plan Evidence take a required platform. Host file operations (target resolution, output trees, Windows spelling lookup, executable checks, native snapshot admission), the Linux distribution read and the process CLI adapter read HOST_PLATFORM explicitly. ByteEntropyService takes an optional platform seam, so its Windows casing test injects win32 instead of stubbing process.platform. src/application no longer reads process.platform or process.arch.
2026-11-15 22:03:14 +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 3e4e178ccc refactor(setup): take the platform in client configuration readers and the setup write path (#11120)
* refactor(setup): take the platform in client configuration readers and the setup write path

ClientRegistrationEnvironment, ClientConfigurationFiles and SupportedClients take a required platform (before defaulted parameters). Setup's configure, inspect and alignment entry points take the setup host's platform and pass it through every layer read, ownership check and the Cordis serializer, which now aliases Windows environment names by the selected platform instead of the running host. Host file operations in ClientConfigPath, ClientConfigurationPublication, JsonFiles and rea mcp add read HOST_PLATFORM explicitly.

* fix(setup): keep private-profile modes on the host when setup selects another platform
2026-11-15 21:31:35 +08:00
morluto 4bd578cc08 refactor(views): give complete-within-view coverage only the returned count (#11132)
* refactor(views): give complete-within-view coverage only the returned count

* test(views): keep only the item-view coverage assertion whose fixture distinguishes it
2026-11-15 21:30:45 +08:00
morluto 682a57a987 fix(native): keep Objective-C class import coverage partial when candidate completeness is unstated (#11128) 2026-11-15 21:04:35 +08:00
morluto 28d28b0578 refactor(setup): take the platform in doctor, setup, uninstall and update maintenance (#11082)
* refactor(setup): take the platform in doctor, setup, uninstall and update maintenance

* test(setup): cover a caller-selected platform in update maintenance
2026-11-15 20:53:25 +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 39537b95af refactor(reverse): let objdump and Rizin own their Evidence limitations and name the provider a deadline bounded (#11113) 2026-11-15 20:49:24 +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 7134053d27 feat(artifacts): name integrity checks for DMG, directory and release-exposure readers (#11056)
* feat(artifacts): name integrity checks for DMG, directory and release-exposure readers

Directory members and release-exposure members whose file identity or
bytes changed since inventory report member-identity; a directory entry
without captured state reports entry-metadata and a changed directory root
container-identity; an hdiutil checksum failure reports image-checksum.
AnalysisArtifactChangedError carries the check from the reader failure and
projects it as details.integrity_check.

* fix(artifacts): name missing directory entry state and keep integrity checks through error copies

* feat(artifacts): name integrity checks for ZIP, PyInstaller and changed release files (#11081)

feat(artifacts): name integrity checks for ZIP, PyInstaller and changed release files, and keep them through reader projections

* feat(artifacts): name integrity checks for the remaining readers and their projections (#11098)
2026-11-15 20:35:28 +08:00
morluto 97ffa67f84 refactor(setup): require the platform in client-layer readers and private-profile helpers (#11074)
* refactor(setup): require the platform in client-layer readers and private-profile helpers

* refactor(setup): drop the client-layer readers' unused host import
2026-11-15 20:21:04 +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
morluto 7dd24dc08c feat(evidence): report comparison constraints, remaining issue counts and observed workflows as fields (#11078)
* feat(evidence): report comparison-source constraints, remaining issue counts and observed workflows as fields

* fix(evidence): locate snapshot bundle schema issues within the snapshot document

* test(evidence): check each comparison-input mismatch and terminal omission once (#11087)
2026-11-15 20:20:40 +08:00
morluto df1e647266 refactor(desktop): let build-computer and agentenv cleanup errors read their owner's release (#10855)
* refactor(errors): read cleanup completion from dependencies instead of pushing it to dependents

AnalysisError.cleanupIncomplete becomes a getter over the error's own unresolved state and its bound dependencies. The WeakRef dependents graph and resolveCleanup's push propagation are removed, and subclasses mark their initial state through markCleanupIncomplete instead of overriding a public mutable field.

* fix(errors): settle bound dependencies before binding more

A composite whose dependencies had released but whose status was never read
stayed incomplete when a later dependency was bound.

* refactor(errors): let errors read a lease group's release instead of resolving them (#10759)

AnalysisCleanupLeaseGroup no longer calls resolveCleanup() on its error.
The error holds the group as a release claim and reads cleanupReleased when
asked, so completion follows the lease owner without a push.

* chore(verify): count resolveCleanup and bindCleanupDependencies calls so they only shrink (#10763)

* refactor(errors): let errors read a lease group's release instead of resolving them

AnalysisCleanupLeaseGroup no longer calls resolveCleanup() on its error.
The error holds the group as a release claim and reads cleanupReleased when
asked, so completion follows the lease owner without a push.

* chore(verify): count resolveCleanup and bindCleanupDependencies calls so they only shrink

* refactor(process): let worker cleanup errors read their root epoch's release

OwnedWorkerRuntime's root epoch is now an AnalysisCleanupReleaseClaim. Errors bound to it read its release when asked instead of the epoch pushing resolveCleanup() through weak references, and a runtime without a retained root binds an already-released owner. Removes the OwnedWorkerCleanupFailure structural type and four resolveCleanup() pushes; the counted baseline shrinks accordingly.

* refactor(pcap): let pcap cleanup errors read command and snapshot release (#10792)

Retained pcap commands, retained snapshot failures and snapshot completion descriptors now own their release and errors read it: commands and snapshot failures implement AnalysisCleanupReleaseClaim, and a failed completion-descriptor close is retried through an AnalysisCleanupLease bound to its error. Removes four resolveCleanup() pushes; the counted baseline shrinks accordingly.

* refactor(process): let Darwin helper cleanup errors read their owner's release

Darwin helper cache and run-token reader root cleanup now record an AnalysisCleanupRelease that every failure reports; a successful retry confirms it. Replaces the first-failure anchor that later failures depended on and the resolveCleanup() push. Removes two resolveCleanup() and two bindCleanupDependencies() calls; both counted baselines shrink.

* refactor(ghidra): let Ghidra cleanup errors read the client's and checkpoint's release

GhidraClient and GhidraCheckpoint bind each cleanup failure to an AnalysisCleanupRelease and confirm it when a later close or retry releases everything, instead of keeping a set of failures and pushing resolveCleanup() to each. Removes two resolveCleanup() calls; the counted baseline shrinks.

* refactor(process): let debugger session cleanup errors read a confirmed stop

OwnedDebuggerSessionRegistry binds each retained gdb or rizin cleanup failure to the session's AnalysisCleanupRelease and confirms it when a stop is confirmed, keeping the latest failure for retry reporting, instead of pushing resolveCleanup() to a set of failures. Removes one resolveCleanup() call; the counted baseline shrinks.

* refactor(process): drop the debugger registry's unused closeOperation option

Returning the latest retained failure removed the last reader of closeOperation; remove the option and the values gdb and rizin supplied.

* refactor(server): let environment shutdown errors read a successful close

serveEnvironment binds each shutdown failure to an AnalysisCleanupRelease that a later successful close confirms, instead of pushing resolveCleanup() to a set of failures. The state-file retry test now also requires the earlier report to read the release. Removes one resolveCleanup() call; the counted baseline shrinks.

* refactor(desktop): release retained screen-judge cleanup through its lease

Each retained judge or screenshot cleanup owner is wrapped in an AnalysisCleanupLease bound to its failure; close() releases the lease, which records the release the failure reads, instead of closing the owner and pushing resolveCleanup(). Removes one resolveCleanup() call; the counted baseline shrinks.

* refactor(ida): let IDA close failures read the session's later close

IdaSessionClient's close failure binds an owner that is both its resource owner and its release claim, read from the client's closed state, instead of keeping weak references and pushing resolveCleanup() after a successful retry. Removes one resolveCleanup() call; the counted baseline shrinks.

* refactor(javascript): let source-map codec cleanup errors read a later close (#10814)

* refactor(javascript): let source-map codec cleanup errors read a later close

OwnedCodec binds each close failure to an AnalysisCleanupRelease that a later successful close confirms, replacing the first-failure anchor that later failures depended on and the resolveCleanup() push. Removes one resolveCleanup() and one bindCleanupDependencies() call; both counted baselines shrink.

* refactor(cutter): let pending Cutter command errors read the completion receipt (#10815)

* refactor(cutter): let pending Cutter command errors read the completion receipt

Each pending Cutter command keeps an AnalysisCleanupRelease bound to its timeout failure; the producer's terminal completion receipt confirms it instead of pushing resolveCleanup(). Removes one resolveCleanup() call; the counted baseline shrinks.

* refactor(application): let provider candidate cleanup errors read the candidate's close (#10817)

* refactor(application): let provider candidate cleanup errors read the candidate's close

AnalysisProviderRegistry keeps one AnalysisCleanupRelease per candidate whose resource close is unconfirmed; failures bind to it and a later confirmed close confirms it, instead of pushing resolveCleanup() to a set of failures. Removes one resolveCleanup() call; the counted baseline shrinks.

* fix(errors): bind a cleanup release claim to an error at most once

AnalysisError kept release claims in an array, so a provider whose retried close returned the same error bound the same claim again on every retry and each cleanupIncomplete read scanned the duplicates. Keep the claims in a set.

* refactor(application): let reverse-tool cleanup errors read a verified stop (#10819)

* refactor(application): let reverse-tool cleanup errors read a verified stop

Each retained provider process group in ReverseEngineeringService keeps an AnalysisCleanupRelease; its command and retry failures bind to it and a later verified stop confirms it, instead of collecting failures and pushing resolveCleanup(). Removes one resolveCleanup() call; the counted baseline shrinks.

* refactor(sqlite): let SQLite worker and root cleanup errors read their release (#10821)

Each retained SQLite snapshot cleanup records an AnalysisCleanupRelease for its worker and for its runtime root; the resource failures bind to them and a verified stop or root removal confirms them, instead of pushing resolveCleanup(). Composite failures keep reading those resource failures. Removes two resolveCleanup() calls; the counted baseline shrinks.

* refactor(android): let JADX cleanup errors read their retained cleanup's release

Each retained JADX cleanup records an AnalysisCleanupRelease for the admission failure's descriptor owners and one for the whole retained cleanup. The admission failure and every error the cleanup reports bind to them, and a confirmed close confirms them, instead of pushing resolveCleanup() through WeakRefs to reported errors. Removes two resolveCleanup() calls; the counted baseline shrinks.

* refactor(application): let session close failures read the client's later close

A retained session cleanup records an AnalysisCleanupRelease; unowned cleanup failures the client's close reported bind to it and the successful close confirms it, instead of the session collecting them and pushing resolveCleanup(). Failures that already follow their own owner keep reading it. Adds a regression for an unowned close failure. Removes one resolveCleanup() call; the counted baseline shrinks.

* refactor(desktop): let build-computer and agentenv cleanup errors read their owner's release

Build-computer teardown, retained task readers, remote reader folders and agentenv solve sandboxes each record an AnalysisCleanupRelease that their bound failures read; a verified teardown or stop confirms it instead of pushing resolveCleanup() through WeakRef sets. Long-run job failures read whether every job they name has confirmed its stop, so a failure naming two jobs no longer reads complete when only one stops (regression added). WeakRef sets kept only where late facts (completion receipts, reader views) are still retained as diagnostics. Removes six resolveCleanup() calls; the counted baseline shrinks.

* refactor(kuna): let source-map and Kuna cache cleanup errors read their owner's release (#10856)

* refactor(browser): let late source-map fetch errors read their response's release

A pending source-map fetch and a response cancellation each record an AnalysisCleanupRelease that their cancellation or deadline failure reads; releasing the late response or the reader lock confirms it instead of pushing resolveCleanup(). Removes two resolveCleanup() calls; the counted baseline shrinks.

* refactor(kuna): let project-cache cleanup errors read each owner's release

Each retained Kuna resource owner (process, staging, hash descriptor, retired file) records a release that the errors reporting it read; an error completes once every owner it reports confirms, and retirement that moves staging to a retained file forwards the staging owner's release to the file's. Replaces the WeakRef report list, its pruning and the resolveCleanup() push. Removes one resolveCleanup() call; the counted baseline shrinks.

* refactor(process): forbid resolveCleanup() pushes; native markers read their supervisor's release (#10857)

* refactor(providers): let an attributed client failure read its source's completed cleanup

Re-attributing a failure whose source cleanup already completed now binds a claim reading that source's release instead of pushing resolveCleanup() onto the new error; a pending source is still bound as a dependency. Resources stay as history, as before. Removes one resolveCleanup() call; the counted baseline shrinks.

* refactor(process): let native command markers read their supervisor's release

ProviderProcessSupervisor records an AnalysisCleanupRelease that a stop proving the process ended confirms. Native command and LLDB resolver cleanup markers bind to their supervisor's release, so a verified stop by either owner completes them, instead of each owner pushing resolveCleanup() onto the markers it tracks. Removes the last two resolveCleanup() calls outside the error base; the counted baseline is empty.

* chore(verify): forbid resolveCleanup() pushes outside the error base

Every production owner now confirms a release its errors read, so the counted baseline is empty. The guard becomes an outright ban with no baseline entry.

* test(native): complete native command and LLDB markers through a direct supervisor stop

* refactor(errors): complete an error's cleanup from its owner's record and every dependency (#10892)

* refactor(errors): complete an error's cleanup from its owner's record and every dependency

* refactor(errors): complete test errors through an owner release and delete resolveCleanup() (#10898)

* refactor(android): compose JADX and Apktool cleanup failures through one shared owner (#10900)

* refactor(android): compose JADX and Apktool cleanup failures through one shared owner

* refactor(application): compose service cleanup failures through the shared owner (#10902)
2026-11-15 20:06:47 +08:00
morluto 7826e9688d refactor(reverse): fake objdump and Rizin commands only through the owned supervisor (#11076)
* refactor(reverse): fake objdump and Rizin commands only through the owned supervisor

* test(reverse): fake launch refusals and failed exits as the owned supervisor reports them

* refactor(reverse): name objdump and Rizin command repairs through the command port (#11080)
2026-11-15 20:06:13 +08:00
morluto cabd56ea71 refactor(domain): give strict UTF-8 decoding one owner with an explicit BOM policy (#10728)
* refactor(domain): give strict UTF-8 decoding one owner with an explicit BOM policy

* fix(domain): keep the leading-BOM behaviour every migrated UTF-8 decoder had

* refactor(domain): read the domain layer's strict UTF-8 through the BOM-policy owner (#10803)

Twelve domain decoders built their own fatal TextDecoder with ignoreBOM: true. They now use strictUtf8Decoder("preserve"), which keeps that behaviour: each reads bytes where a leading U+FEFF is content (binary-format names and fields, JSON and XML read byte-for-byte, recorded bodies, registry seeds whose kept bytes are re-emitted). The counted baseline drops from 74 to 62.

* refactor(artifacts): read artifact readers' strict UTF-8 through the BOM-policy owner (#10871)

Fourteen artifact reader modules (17 sites) built their own fatal TextDecoder. They now take it from strictUtf8Decoder with the policy each had: every one kept a leading BOM (ignoreBOM: true), so they use "preserve"; JavaScriptArtifactFiles keeps it for JavaScript and HTML entries and strips it otherwise, as before. The strict-utf8-decoder baseline drops those 17 sites.

* refactor(domain): drop TextDecoder imports left by the strict UTF-8 migration

* refactor(application): read MCP registration metadata through the strict UTF-8 owner

* refactor(formats): read binary-format readers' strict UTF-8 through the BOM-policy owner (#10903)

* refactor(formats): read binary-format readers' strict UTF-8 through the BOM-policy owner

* refactor(formats): drop the model readers' unused TextDecoder imports

* refactor(browser): read browser capture and fetch strict UTF-8 through the BOM-policy owner (#10935)

* refactor(browser): read browser capture and fetch strict UTF-8 through the BOM-policy owner

* refactor(native): read LLDB and Mach-O name strict UTF-8 through the BOM-policy owner (#10936)

* refactor(native): read LLDB and Mach-O name strict UTF-8 through the BOM-policy owner

* refactor(process): read MCP and NDJSON transport frames' strict UTF-8 through the BOM-policy owner (#10937)

* refactor(process): read MCP and NDJSON transport frames' strict UTF-8 through the BOM-policy owner

* refactor(application): read JSON and file inputs' strict UTF-8 through the BOM-policy owner (#10938)

* refactor(application): read JSON and file inputs' strict UTF-8 through the BOM-policy owner

* refactor(providers): read IDA, Electron, Ghidra and reference strict UTF-8 through the BOM-policy owner (#10939)

* refactor(providers): read IDA, Electron, Ghidra and reference strict UTF-8 through the BOM-policy owner

* refactor(evm): read the hex bytecode carrier's strict UTF-8 through the owner and refuse new local decoders (#10940)

* refactor(evm): read the hex bytecode carrier's strict UTF-8 through the BOM-policy owner

* chore(architecture): match local strict UTF-8 decoders in any option order
2026-11-15 19:54:39 +08:00
morluto b98fbdc442 refactor(ida): let IDA close failures read the session's later close (#10811)
* refactor(errors): read cleanup completion from dependencies instead of pushing it to dependents

AnalysisError.cleanupIncomplete becomes a getter over the error's own unresolved state and its bound dependencies. The WeakRef dependents graph and resolveCleanup's push propagation are removed, and subclasses mark their initial state through markCleanupIncomplete instead of overriding a public mutable field.

* fix(errors): settle bound dependencies before binding more

A composite whose dependencies had released but whose status was never read
stayed incomplete when a later dependency was bound.

* refactor(errors): let errors read a lease group's release instead of resolving them (#10759)

AnalysisCleanupLeaseGroup no longer calls resolveCleanup() on its error.
The error holds the group as a release claim and reads cleanupReleased when
asked, so completion follows the lease owner without a push.

* chore(verify): count resolveCleanup and bindCleanupDependencies calls so they only shrink (#10763)

* refactor(errors): let errors read a lease group's release instead of resolving them

AnalysisCleanupLeaseGroup no longer calls resolveCleanup() on its error.
The error holds the group as a release claim and reads cleanupReleased when
asked, so completion follows the lease owner without a push.

* chore(verify): count resolveCleanup and bindCleanupDependencies calls so they only shrink

* refactor(process): let worker cleanup errors read their root epoch's release

OwnedWorkerRuntime's root epoch is now an AnalysisCleanupReleaseClaim. Errors bound to it read its release when asked instead of the epoch pushing resolveCleanup() through weak references, and a runtime without a retained root binds an already-released owner. Removes the OwnedWorkerCleanupFailure structural type and four resolveCleanup() pushes; the counted baseline shrinks accordingly.

* refactor(pcap): let pcap cleanup errors read command and snapshot release (#10792)

Retained pcap commands, retained snapshot failures and snapshot completion descriptors now own their release and errors read it: commands and snapshot failures implement AnalysisCleanupReleaseClaim, and a failed completion-descriptor close is retried through an AnalysisCleanupLease bound to its error. Removes four resolveCleanup() pushes; the counted baseline shrinks accordingly.

* refactor(process): let Darwin helper cleanup errors read their owner's release

Darwin helper cache and run-token reader root cleanup now record an AnalysisCleanupRelease that every failure reports; a successful retry confirms it. Replaces the first-failure anchor that later failures depended on and the resolveCleanup() push. Removes two resolveCleanup() and two bindCleanupDependencies() calls; both counted baselines shrink.

* refactor(ghidra): let Ghidra cleanup errors read the client's and checkpoint's release

GhidraClient and GhidraCheckpoint bind each cleanup failure to an AnalysisCleanupRelease and confirm it when a later close or retry releases everything, instead of keeping a set of failures and pushing resolveCleanup() to each. Removes two resolveCleanup() calls; the counted baseline shrinks.

* refactor(process): let debugger session cleanup errors read a confirmed stop

OwnedDebuggerSessionRegistry binds each retained gdb or rizin cleanup failure to the session's AnalysisCleanupRelease and confirms it when a stop is confirmed, keeping the latest failure for retry reporting, instead of pushing resolveCleanup() to a set of failures. Removes one resolveCleanup() call; the counted baseline shrinks.

* refactor(process): drop the debugger registry's unused closeOperation option

Returning the latest retained failure removed the last reader of closeOperation; remove the option and the values gdb and rizin supplied.

* refactor(server): let environment shutdown errors read a successful close

serveEnvironment binds each shutdown failure to an AnalysisCleanupRelease that a later successful close confirms, instead of pushing resolveCleanup() to a set of failures. The state-file retry test now also requires the earlier report to read the release. Removes one resolveCleanup() call; the counted baseline shrinks.

* refactor(desktop): release retained screen-judge cleanup through its lease

Each retained judge or screenshot cleanup owner is wrapped in an AnalysisCleanupLease bound to its failure; close() releases the lease, which records the release the failure reads, instead of closing the owner and pushing resolveCleanup(). Removes one resolveCleanup() call; the counted baseline shrinks.

* refactor(ida): let IDA close failures read the session's later close

IdaSessionClient's close failure binds an owner that is both its resource owner and its release claim, read from the client's closed state, instead of keeping weak references and pushing resolveCleanup() after a successful retry. Removes one resolveCleanup() call; the counted baseline shrinks.

* refactor(javascript): let source-map codec cleanup errors read a later close (#10814)

* refactor(javascript): let source-map codec cleanup errors read a later close

OwnedCodec binds each close failure to an AnalysisCleanupRelease that a later successful close confirms, replacing the first-failure anchor that later failures depended on and the resolveCleanup() push. Removes one resolveCleanup() and one bindCleanupDependencies() call; both counted baselines shrink.

* refactor(cutter): let pending Cutter command errors read the completion receipt (#10815)

* refactor(cutter): let pending Cutter command errors read the completion receipt

Each pending Cutter command keeps an AnalysisCleanupRelease bound to its timeout failure; the producer's terminal completion receipt confirms it instead of pushing resolveCleanup(). Removes one resolveCleanup() call; the counted baseline shrinks.

* refactor(application): let provider candidate cleanup errors read the candidate's close (#10817)

* refactor(application): let provider candidate cleanup errors read the candidate's close

AnalysisProviderRegistry keeps one AnalysisCleanupRelease per candidate whose resource close is unconfirmed; failures bind to it and a later confirmed close confirms it, instead of pushing resolveCleanup() to a set of failures. Removes one resolveCleanup() call; the counted baseline shrinks.

* fix(errors): bind a cleanup release claim to an error at most once

AnalysisError kept release claims in an array, so a provider whose retried close returned the same error bound the same claim again on every retry and each cleanupIncomplete read scanned the duplicates. Keep the claims in a set.

* refactor(application): let reverse-tool cleanup errors read a verified stop (#10819)

* refactor(application): let reverse-tool cleanup errors read a verified stop

Each retained provider process group in ReverseEngineeringService keeps an AnalysisCleanupRelease; its command and retry failures bind to it and a later verified stop confirms it, instead of collecting failures and pushing resolveCleanup(). Removes one resolveCleanup() call; the counted baseline shrinks.

* refactor(sqlite): let SQLite worker and root cleanup errors read their release (#10821)

Each retained SQLite snapshot cleanup records an AnalysisCleanupRelease for its worker and for its runtime root; the resource failures bind to them and a verified stop or root removal confirms them, instead of pushing resolveCleanup(). Composite failures keep reading those resource failures. Removes two resolveCleanup() calls; the counted baseline shrinks.

* refactor(application): let JADX and session close failures read their owner's release (#10854)

* refactor(android): let JADX cleanup errors read their retained cleanup's release

Each retained JADX cleanup records an AnalysisCleanupRelease for the admission failure's descriptor owners and one for the whole retained cleanup. The admission failure and every error the cleanup reports bind to them, and a confirmed close confirms them, instead of pushing resolveCleanup() through WeakRefs to reported errors. Removes two resolveCleanup() calls; the counted baseline shrinks.

* refactor(application): let session close failures read the client's later close

A retained session cleanup records an AnalysisCleanupRelease; unowned cleanup failures the client's close reported bind to it and the successful close confirms it, instead of the session collecting them and pushing resolveCleanup(). Failures that already follow their own owner keep reading it. Adds a regression for an unowned close failure. Removes one resolveCleanup() call; the counted baseline shrinks.
2026-11-15 19:53:53 +08:00
morluto 5d1a47768a feat(artifacts): surface the named integrity check in public artifact errors (#11052)
* feat(artifacts): surface the named integrity check in public artifact errors

ArtifactOperationError now carries integrityCheck from the reader failure
and projects it as details.integrity_check, including with artifact
integrity details. Rebuild sites copied a different subset of reader facts
each (ProcessCli.artifactFailureFacts, withCleanup, desktop, file-operation
cleanup, resource export, DMG inventory, safe output tree); they now spread
ArtifactReaderFailure.primaryFacts(), so a new reader fact has one owner.
ArtifactIntegrityCheck moves to the domain beside ArtifactAdmissionLimit.

* test(artifacts): check rebuilt cleanup failures keep the named integrity check
2026-11-15 19:53:05 +08:00
morluto 9be6a96fd6 refactor(cli): project reconstruction and source-to-bundle documents from their contracts (#10697)
* refactor(cli): project reconstruction ledger and coverage commands from their contracts

* refactor(cli): project compare-source-to-bundle from its contract and refuse omitted document fields

* fix(cli): advertise caller paths in JSON documents as the CLI admits them

* fix(cli): advertise that JSON document caller paths exclude URLs

* refactor(cli): narrow omitted document field refusal to object documents

* test(cli): give the coverage document fixture the current evidence_format
2026-11-15 19:40:22 +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 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
morluto a6cbe8b12a feat(providers): bound runOwned work and close by one deadline (#10996)
* feat(providers): bound runOwned work and close by one deadline

* fix(providers): read an expired budget at settle time and keep a caller abort during cleanup

* fix(providers): follow a late close rejection's owner and keep captured output through budget replacements

* fix(providers): track budget expiry apart from the caller, keep replaced work's cleanup, treat empty leases as none

* fix(providers): classify thrown abort reasons, keep recorded failures and follow a late close owner's resources

Work that throws its signal's abort reason (throwIfAborted) is now a
cancellation, so an elapsed budget becomes a timeout and a caller abort stays a
cancellation. A budget cancellation replaced by a timeout or a caller
cancellation keeps the secondary failures it recorded. A late close rejection
that names its own owner now also supplies the retained failure's resources.

* fix(providers): classify only the budget's own abort reason, and follow only a late rejection that reports its own cleanup

A caller's abort reason is opaque, so a thrown value equal to it stays the
work's failure with the caller's cancellation retained; only the budget's typed
OwnerCancellation.deadlineElapsed marks a thrown abort as the budget's. A late
close rejection without unreleased cleanup of its own (such as a cancellation)
no longer confirms release or replaces the fallback resource.

* fix(providers): follow a late close owner that already released, by cleanup ownership not completion

AnalysisError.carriesCleanup owns whether an error ever owned cleanup, apart
from whether it finished. runOwned's late close rejection uses it, so a
producer error released by a concurrent retry still confirms release, while a
cancellation without cleanup keeps the close's lease pending. Uninstall and
SetupCleanupOwner used the same four-clause predicate locally; they now read
the owner.
2026-11-15 19:36:38 +08:00
morluto 63b0426e40 fix(errors): rethrow internal invariant violations from plist consumers and the native output projection (#11029)
* fix(plist): rethrow internal invariant violations from plist consumers

KeyedArchiveReader reported any non-limit decode failure as a format
failure, InterfaceBuilderAnalysis listed it as an invalid archive,
ReleaseExposureReader fell back to scanning raw bytes, and the native
plist parser reported it as provider output. An InternalInvariantError
is a REA defect, not evidence about the input, so each consumer now
rethrows it. The native parser's binary path now shares its one cause
projection instead of a duplicate.

* fix(native): rethrow internal invariant violations from the native output projection

nativeOutputFailure wrapped any producer failure as AnalysisOutputError,
so a REA defect thrown while reading a plist or dispatching a command was
reported as native output parsing. It now rethrows InternalInvariantError,
which reaches the provider's adapter-failure path with the invariant as
its cause.

* fix(artifacts): rethrow internal invariant violations from the shared artifact failure mapper

translateArtifactFailure turned every unrecognised cause into an
ArtifactOperationError with reason io, so the keyed-archive and
Interface Builder rethrows still reached users as artifact read
failures. The shared mapper now rethrows InternalInvariantError, and the
deferred client reports it as a ProviderAdapterError. Provider-level
tests drive both operations through that client.

* refactor(dotnet): report managed reader defects through invariantViolation (#11030)

* refactor(dotnet): report managed reader defects through invariantViolation

The managed readers threw plain TypeError and RangeError for states their
callers rule out: an allowance or method page outside the input schema, a
missing snapshot after identity admission, and a missing CLI header after
the caller already returned for it. These are REA defects, not malformed
targets, so they throw invariantViolation. The two CLI re-checks are gone:
the callers pass the CLI header they already narrowed. The dotnet
parser-plain-throw baseline drops from 6 to 0.

* refactor(providers): refuse unusable tool paths and record marker mismatches without throwing to their own catch (#11031)

* refactor(firmware): refuse unusable tool paths without throwing to its own catch

resolveFirmwareCommand threw a plain TypeError for a configured engine or
limiter path that is not a regular file, only to catch it in the next line
and wrap it as an unavailable capability. A helper now returns why a path
cannot run, and each caller builds the one refusal directly. Refusal
reasons are unchanged; the directory tests now pin them exactly. The
firmware parser-plain-throw baseline drops from 2 to 0.

* refactor(pwndbg): refuse unusable debugger paths without throwing to its own catch

inspectPwndbgCore threw plain Errors for a relative or wrong-kind
configured path only to catch them and wrap them as an unavailable
capability. A helper now returns why a path cannot be used, with any host
error as its cause, and the loop builds the one refusal. Cancellation
between host checks still propagates. Refusal reasons are unchanged. The
PwndbgCoreContext parser-plain-throw baseline entry is removed.

* refactor(pwntools): record a mismatched failure marker without throwing to its own catch

readPwntoolsFailureEvidence threw a plain Error when the private bridge
marker disagreed with the exit status, only to catch it and record its
message as the marker failure. The mismatch is now recorded directly. The
recorded evidence is unchanged; a unit test pins both the matching and
the mismatched marker. The PwntoolsResourceLimits parser-plain-throw
baseline entry is removed.

* fix(android): report an unobserved Java probe as a REA defect, not a Java refusal (#11032)

* fix(android): report an unobserved Java probe as a REA defect, not a Java refusal

When runOwnedCommand resolved without observing the completed run, the
probe threw a plain Error that its catch then classified as a Java
runtime refusal, telling the caller to review their launcher. The state
is a broken internal assumption, so the probe throws invariantViolation
and projects it as a jadx adapter failure. The JadxJavaProbe
parser-plain-throw baseline entry is removed.

* refactor(native): report native provider defects through invariantViolation (#11033)

* refactor(native): report native provider defects through invariantViolation

Native providers threw plain Error and TypeError for states their own
earlier steps rule out: a client with no target, a measured slice with no
selection, a read longer than requested, a decoder command without its
runtime, an LLDB root released before its children, and an IPS integer
fact from another reader. These are REA defects, so they throw
invariantViolation. NativeUiInterruptionControl no longer needs a throw:
recording the deadline interruption returns the interruption its error is
built from. Six native parser-plain-throw baseline entries drop.

* refactor(native): report malformed lipo, swift-demangle and UI helper output through ParseFailure (#11034)

* refactor(native): report malformed lipo and swift-demangle output through ParseFailure

The lipo and swift-demangle output parsers threw plain TypeError for
output that breaks their framing, and the lipo result classifier caught
every TypeError as malformed output, so a host TypeError from a REA defect
was reported as lipo output. Both parsers now throw malformedInput with
their own format, and the lipo classifier accepts ParseFailureError
instead of TypeError. Messages and the consumers' output and format
failure categories are unchanged. Four native parser-plain-throw
baseline entries drop.

* refactor(native): report the UI helper's non-JSON output through ParseFailure

The native UI helper runtime threw a plain Error when the helper's
stdout was not JSON. It now throws malformedInput with its own format and
the JSON failure as cause. The observation boundary still reports it as a
native-macos adapter failure with the same reason. The
NativeUiHelperRuntime parser-plain-throw baseline entry is removed.

* refactor(verify): keep process-lifecycle owners out of the parser plain-throw guard (#11035)

* refactor(verify): keep process-lifecycle owners out of the parser plain-throw guard

JadxWorkspaceRoot and PwntoolsDecoder sit in parser folders but own a
private helper process; their remaining plain throws report the helper's
startup, handshake and cleanup outcomes, not parse faults. The guard now
names them as process-lifecycle owners and leaves them out, so their
five baseline entries drop without inventing parse errors for process
failures.

* refactor(parsers): report the remaining artifact, il2cpp, model and domain defects through invariantViolation (#11036)

* refactor(parsers): report the remaining artifact, il2cpp, model and domain defects through invariantViolation

Nine plain Error, TypeError and RangeError throws in parser folders
reported broken internal assumptions or caller contracts (an
uninitialized ZIP or DMG reader, a missing scan result, a non-byte
stream chunk, an invalid traversal or marshal limit, a header count or
linking fact the reader itself should have produced, a misordered
Safetensors budget, an append after fingerprinting). None is an input
fault, and no catch site depends on their class. They now throw
invariantViolation with the same messages, and their parser-plain-throw
baseline entries drop.

* refactor(evidence): refuse an earlier Evidence format through ParseFailure (#11037)

* refactor(evidence): refuse an earlier Evidence format through ParseFailure

UnsupportedEvidenceFormatError restated the ParseFailure algebra as its
own TypeError subclass. The format owner now throws unsupportedInput for
the evidence-bundle format at the evidence_format field and exports the
predicate its consumers use. The bundle reader's unsupported-format kind
and message are unchanged. The snapshot reader surfaced only TypeError
messages, so it now also surfaces the format refusal and keeps its
recreate guidance; a boundary test pins that.

* refactor(native): report Mach-O reader and inspection defects through invariantViolation (#11042)

The Mach-O inspection plan reads the lipo, nm and dyld_info captures it
just ran, the dispatch metadata reader is called only for Mach-O
executables, and the command-name reader takes limits its callers
derive. A missing capture, a non-Mach-O target or a malformed limit is a
REA defect, not an input fault, so these six sites throw
invariantViolation with their existing messages instead of TypeError.
No catch site depends on their class. Their parser-plain-throw baseline
entries drop.

* fix(artifacts): join the Interface Builder cleanup failure to a catalog defect

When catalog work failed while the concurrent Interface Builder reader
failed to close, analyzeAppleAssetCatalogs built the primary through
translateArtifactFailure before joining cleanup. That mapper now rethrows
an InternalInvariantError, so a defect skipped the join and the reader's
retained resources were lost. The defect now becomes the artifact graph
provider's adapter failure at that site and joins the cleanup failure as
before.
2026-11-15 19:35:22 +08:00
morluto 0d86953c32 feat(evidence): name a standalone Evidence record with detected_kind on every bundle refusal (#11039)
The file reader, inline bundle reader and investigation ledger refused a
single Evidence record with only a message saying so. The file reader's
wrong-kind failure now carries detected_kind "evidence-record" (as it
already did for analysis snapshots), and the inline and ledger refusals
carry it on their input issue through one domain helper. Tests assert the
field instead of the sentence. The inline CLI/MCP case now asserts full
parity: since compact input schemas, MCP reaches the same domain reader.
2026-11-15 19:34:40 +08:00
morluto 423489a761 feat(evidence): name the side and issue path of comparison source integrity failures (#11049)
* feat(process): name the side and issue path of process-capture integrity failures

compare_process_captures reported which side failed validation, and where,
only in the message. EvidenceIntegrityError now carries an optional side
and issue path, projected as details.side and details.issue_path, and the
CLI's malformed-capture issue carries observed.issue_path. The first issue
path comes from the same selection describeValidationFailure names.

* feat(evidence): report source authentication issue paths from the Evidence owner

readEvidence and readImmutableEvidence now set the record-relative issue
path on their integrity failure, and readEvidencePayload sets it when the
caller says where the payload sits. compare_functions passes its side, so a
tampered function source reports details.side and details.issue_path like
compare_process_captures. Process capture result issue paths are now
relative to the Evidence record (normalized_result.*) on both adapters.
2026-11-15 19:33:47 +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 2826592759 refactor(application): build application cancellations with a required reason (#10574)
* refactor(application): build application cancellations with a required reason

Migrate every AnalysisCancelledError construction under src/application
to cancelledBy and lower the guard baseline from 671 to 442.

Shutdown refusals in BinarySession report OwnerCancellation.ownerClosed
when the caller did not abort, instead of an undefined reason.
ReverseEngineeringService and DesktopBuildOwnedWork close their work
with the typed owner reason. SharedStartupAttempt returns the departing
caller's reason with a cancelled wait. One shared isAborted predicate
replaces eleven local copies and narrows a checked signal for
cancelledBy.

* refactor(desktop): drop an unused cancellation import from desktop capture state
2026-11-15 18:38:24 +08:00
morluto 54230c2e7b refactor(setup): require the platform for skill destinations instead of reading the host (#11006)
* refactor(setup): require the platform for skill destinations instead of reading the host

The setup skill owner defaulted seven entry points to process.platform. Every
production caller (setup host, doctor, uninstall, update maintenance) already
passes the platform it was composed with, so the defaults only let a caller
silently mix the host REA runs on with the one it asked about. The platform is
now a required parameter; tests name the platform they exercise, the setup
fixture passes its declared host, and the ambient-read baseline drops
SetupSkill.ts (7 to 0).

* test(setup): expect the fake host's POSIX skill paths in planning tests
2026-11-15 18:35:01 +08:00
morluto f46cd6770c test: advance the Rizin and build computer fixed waits instead of sleeping through them (#11026) 2026-11-15 18:33:52 +08:00
morluto 5cfaac0834 refactor(process): require the platform for environment snapshots instead of reading the host (#11028) 2026-11-15 18:33:23 +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 536c54ca1f feat(process): name whether a process input file failed to read or to parse (#11008) 2026-11-15 18:23:05 +08:00
morluto e0249db65c feat(evidence): mark a snapshot supplied to import_evidence_bundle with detected_kind (#11010) 2026-11-15 18:22:33 +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 5775c8124f test(desktop): inject the build computer's drawn-screenshot wait so its MCP test runs in 2 s (#10999) 2026-11-15 17:59:26 +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 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 b655645608 refactor(errors): give caller aborts a typed reason at the adapters (#10633)
* refactor(errors): give caller aborts a typed reason at the adapters

Squashes 908813551f, 87858de4dc (fix(mcp): preserve cancellation reasons in handlers) and fa151dc3f6 (test(server): race handler-entry gates against the triggering tool call) onto main without #10566's pre-squash commits.

* refactor(errors): give owner-initiated aborts a typed reason outside the browser adapters (#10692)

* refactor(errors): give owner-initiated aborts a typed reason outside the browser adapters

* refactor(verify): keep the abort-reason scan receiver-independent

Playwright's route.abort takes a network error code; the one call site now names its default code instead of the guard exempting a variable name.

* refactor(browser): give owner-initiated CDP aborts typed reasons (#10706)

* refactor(browser): give owner-initiated CDP aborts typed reasons

* fix(browser): give the page-side request fetch page-native abort reasons

The request callback runs in the page through page.evaluate and cannot
reach REA's modules. Define its deadline and cleanup reasons as
DOMExceptions inside the callback, and make the test fake evaluate the
callback from its source so module imports are unavailable, as in a page.

* test(process): expect the reader close's typed owner reason

Closing the Darwin run-token reader aborts its compilation with OwnerCancellation.ownerClosed since the owner-abort change in this PR; the boundary test still expected the default DOMException.

* fix(server): report HTTP session replacement and shutdown to pending tools as an owner close
2026-11-15 17:28:05 +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 85df21992f refactor(native): read retained inventory views through the operation fact contracts (#10883)
* refactor(native): read retained inventory views through the operation fact contracts

* refactor(native): read matching inventories through the operation fact contracts (#10887)

* refactor(native): read dispatch symbol fallbacks as lenient name facts at the port (#10889)

* refactor(native): read dispatch symbol fallbacks as lenient name facts at the port

* test(native): accept undeclared name-row fields in both dispatch consumers

* refactor(native): read literal searches and overview counts as operation facts (#10894)

* refactor(native): read literal searches and overview counts as operation facts

* refactor(native): read addressed inventory rows as operation facts in every reader (#10899)

* refactor(native): read addressed inventory rows as operation facts in every reader

* test(native): cover xrefs name resolution through the name fact contract

* test(server): pin procedure completion to the procedure inventory contract
2026-11-15 17:24:24 +08:00
morluto 5bf66f7500 refactor(domain): one owner for the non-array record predicate (#10588)
* refactor(domain): one owner for the non-array record predicate

Thirty-four source files each declared isRecord as a non-null object that is
not an array, and six more spelled the same predicate as isObject,
isSchemaRecord or isStorageRecord. They now import isRecord from
src/domain/isRecord.ts, whose overloads keep JsonValue narrowing for the
desktop and environment readers. DshCordisPatch's predicate also excludes
Cordis expressions, so it is renamed isPatchRecord.

architecture-guards refuses a new local copy under any name. The seven files
that still hold one are listed and must leave the list when their copy goes.

* fix(guards): count record predicate copies precisely

* test(hopper): retry after confirmed application exit

* fix: reject stale record predicate exceptions
2026-11-15 17:05:54 +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 66d1527a3d refactor(native): parse native inventory results once at the operation port (#10880) 2026-11-15 16:59:39 +08:00
morluto 8443930bd3 refactor(limits): declare the Node timer bound and duration schema once (#10640)
* refactor(limits): declare the Node timer bound and duration schema once

Fifteen modules re-declared 2_147_483_647 as their own timer maximum and
eleven built the same 1..2^31-1 millisecond schema. They now use
MAX_NODE_TIMER_DELAY_MS and timerDurationMsSchema from
src/domain/analysisResourceLimits.ts, and an architecture guard rejects the
literal outside that owner and five non-timer int32 bounds. The generated
MCP tool catalog is byte-identical.

* refactor(limits): keep the JVM processor-count bound separate from the timer bound

REA_JADX_ACTIVE_PROCESSOR_COUNT is a Java int count, not a timer delay, so it keeps its own named bound and a documented exception in the signed 32-bit guard. Remove the timer-bound and zod imports the duration-schema migration left unused.

* refactor(limits): keep the TShark packet-count bound separate from the timer bound

packet_limit is passed to TShark's -c as a C int packet count, not a timer delay, so it keeps its own named bound and a documented exception in the signed 32-bit guard.
2026-11-15 16:40:03 +08:00
morluto 17a5f14f98 fix(process): run ps from fixed system locations, not REA's PATH (#10755)
* fix(process): run macOS ps from /bin/ps, not REA's PATH

Owned process cleanup and process sampling ran "ps" through REA's PATH, so
an unusable PATH turned a confirmable cleanup into an incomplete one
(spawn ps ENOENT). macOS ships ps at /bin/ps on the sealed system volume.
Linux keeps the documented procps-on-REA's-PATH contract.

* test(process): stop the ps fixture only after its descendant spawned

* fix(process): run Linux ps from fixed system locations before REA's PATH

Linux process ownership now runs the first executable of /bin/ps, /usr/bin/ps and /run/current-system/sw/bin/ps, searching REA's PATH only when none exists. Admission refusals name the ps that ran in their reason and ps_command/ps_source diagnostics; cleanup failure reasons keep the executed command. Doctor and remediation text follow the same selection. The macOS cleanup test now releases its fixture group even when the descendant wait fails.

* fix(application): keep which ps ran in provider availability

ownedProcessPrerequisiteAvailability copied only the admission and system codes from a Linux ps refusal, so Ghidra, JADX and Apktool status lost ps_command and ps_source. Copy the refusal's prerequisite facts from one key list.
2026-11-15 16:28:46 +08:00
morluto c06a0b9ee8 feat(pcap): read tshark JSON packets through one envelope owner with an adjustable expansion bound (#10788)
* refactor(pcap): read tshark JSON packets through one envelope owner

PcapOutput and the packet-capture reader each decoded tshark -T json and checked the packet envelope. The capture reader let SyntaxError escape and accepted any shape until its field schema ran. src/pcap/TsharkJsonPackets.ts now owns the envelope (malformed JSON, the packet array, each packet's _source.layers) as typed failures, and the pcap byte and expansion budget. Both consumers map those failures to their existing error kinds. An architecture guard keeps _source.layers reads in the owner.

* fix(captures): keep producer output on a refused packet reply

parsePacketOutput threw AnalysisOutputError itself, so the provider's failure mapping returned it without the captured stdout and stderr, both for envelope refusals and for unordered or out-of-limit frames. It now returns a Result with the reason and cause, and the provider builds the AnalysisOutputError with capturedOutput.

* feat(captures): bound packet-capture JSON expansion by an adjustable setting

The capture path parsed TShark's selected-field JSON without the expansion
estimate the pcap reader charges. It now reads through readTsharkJsonPackets
under capture.packet_json_expansion_bytes (REA_CAPTURE_PACKET_JSON_EXPANSION_BYTES).
The estimate charges at most 130 bytes per code unit and the output is one
Node string, so no admissible output reaches 69,793,215,440 bytes; the default
and maximum are 128 GiB, so the budget refuses only when lowered. A refusal is
an adjustable_budget resource constraint naming the setting and keeps the
producer output; a malformed launch seed is refused before the capture is read.
2026-11-15 16:17:24 +08:00
morluto b0e32578ae feat(cli): warn after mcp add writes Claude Desktop's configuration (#10696)
* feat(cli): warn after mcp add writes Claude Desktop's configuration

rea mcp add resolves Claude Desktop from the client table, whose row
says a running app can rewrite its configuration from memory, but the
command never told the caller. Print that notice on stderr when it
writes the file. Backup notices now use the client's display name
instead of special-casing Amp.

* fix(cli): warn about Claude Desktop whenever mcp add wrote its file

Address review findings: a registration that failed after publication,
such as cancellation during verification, still left the file written,
so the running-app notice now follows the registration owner's written
fact instead of overall success. The CLI test compares the delivered
notice with its owner instead of freezing the sentence.
2026-11-15 15:43:55 +08:00