Files
rea/docs/tool-design.md
T
morluto 1b97b0f370 fix(lifecycle): preserve observations and cleanup ownership (#4274)
* fix(application): retain completed evidence through cancellation

Validate completed provider reports before classifying cancellation and preserve Evidence and captured diagnostics. Share MCP recording and delivery across custom observation handlers. Add source-owned completion and public SDK recording controls. Refs #3923, #3927, #4261.

* fix(wasm): retain final observations through cancellation

Keep completed Evidence after finalization and through CLI/MCP delivery. Preserve output and cleanup failure precedence. Refs #4254.

* fix(browser): retain final observations and forward cancellation

Classify final CDP detach and completed scenario cleanup at the provider boundary. Translate completed partials into canonical Evidence for MCP recording, and forward CLI cancellation to passive Electron tools. Refs #4255, #4256, #4257.

* fix(ghidra): isolate cancellation of shared startup waiters

Keep startup group-owned while each caller has an independent abortable wait. Await final-owner cleanup and preserve cleanup facts before retrying. Reuse the shared abortable promise helper. Refs #3983.

* fix(mcp): keep shutdown registrations until cleanup succeeds

Retain repeated event requests while unresolved owners need cleanup, and unregister only after success. Verify coalescing and failed-owner retry. Refs #4253.

* fix(adb): own transfer snapshots and stage screen publication

Bind transfer digests to the exact private input bytes and preserve caller APK names. Retain failed descriptors and private-root owners with typed cleanup evidence; publish captures exclusively after the cancellation cutoff. Refs #4251, #4252.

* fix(verification): report ADB cleanup failures

Always close after attempted verification and keep unexpected primary failures and cleanup details visible. Exercise the production script with controlled imports, including null and undefined throws. Refs #4260.

* fix(runner): stop owned Windows commands on cancellation

Use the retained ChildProcess handle for IPC cancellation and parent disconnection. Keep the SQLite lock until observed child close, and include native Windows regressions in the curated CI lane. Native Windows results remain pending. Refs #4258.

* fix(website): canonicalize link containment boundaries

Compare resolved targets with resolved repository and site roots. Accept directory aliases while preserving escape checks and keep fixtures within their temporary owner. Refs #4264.

* docs(testing): cover completion races and cleanup retry

Clarify validation-before-cancellation, independent startup waiter controls, producer and adapter coverage, and repeated shutdown events.

* fix(filesystem): retain file owners and completed reads after close failure

* fix(session): report retained snapshot staging in cleanup status

Fixes #4277. Include both snapshot and profile file-resource scopes in the owning status fact, preserving publication receipts and cleanup retry.

* docs(mcp): clarify recovery of raw partial observations

Document the observed SDK cancellation boundary and the open design decision in #4276 without promoting raw errors to Evidence.

* fix(kuna): own the complete installation inspection lifetime

* test(kuna): cover session cleanup of shared cache resources

* fix(hopper): isolate publisher operation resource admission

* fix(hopper): preserve launcher cleanup failure classification

* fix(adb): retain ordered cleanup and screen publication outcomes

* fix(runner): retain Windows job ownership through cleanup retries

* docs: clarify inspection admission and ordered cleanup regressions

* fix(ghidra): preserve startup cleanup and request deadlines

* fix(runner): supervise stdin errors and bootstrap native controls

* docs: require queued lifecycle controls to survive admission

* fix(cli): leave cleanup resolution with resource owners

* test(adb): verify owned install snapshots through CLI

* fix(errors): exclude resolved primary cleanup resources

* fix(adb): resolve publication cleanup after its handle closes

* fix(android): resolve JADX hash cleanup after owned close

* refactor(android): share hash failure cause options

* fix(kuna): gate commands on retained descriptor cleanup

* docs: cover cleanup admission and owner-confirmed recovery

* refactor(artifacts): bind cleanup errors to confirmed release

* fix(adb): resolve recovered cleanup at retained owners

* fix(kuna): resolve installation cleanup after owned close

* fix(kuna): gate cache reads on retained hash cleanup

* fix(setup): preserve publisher cleanup admission failures

* fix(android): resolve the reported retirement cleanup error

* test(runner): scope post-spawn fault control to POSIX

* test(adb): cover persistent snapshot owner retirement

* fix(kuna): preserve file outcomes with explicit cleanup ownership

* fix(artifacts): resolve errors through retained cleanup owners

* docs(lifecycle): carry cleanup ownership in typed outcomes

* fix(adb): bind staged screen errors to their cleanup lease

* fix(kuna): bind profile cleanup through a close capability

* fix(artifacts): retain descriptor resources in cleanup errors

* fix(pcap): bind existing errors to snapshot retirement

* fix(windows): load source owner through its file URL

* test(build): copy the command runner helper into fixtures

* fix(mcp): own shared observation providers in the transport

* fix(jadx): resolve every retained cleanup retry error

* fix(kuna): bind retry errors to every retained cleanup owner

* fix(adb): bind admission and shutdown errors to exact cleanup leases

* fix(windows): resolve command paths before native ownership

* test(publisher): assert exact descriptor recovery without release metadata

* docs(lifecycle): require every retry error to follow its owners

* fix(kuna): keep hash cleanup state stable across concurrent retries

* test(windows): exercise surviving descendants with scoped shutdown deadlines

* fix(uninstall): preserve primary read errors through cleanup failure

* test(publisher): assert cleanup Result and exact owner recovery

* test(windows): observe supervisor resources through IPC disconnect

* fix(browser): retain typed cancellation after interrupted actions

* test(publisher): assert ownership phases without retry-count coupling

* fix(lifecycle): preserve abort reasons through shared provider execution

* fix(jadx): preserve execution and late hash cancellation causes

* fix(adb): compose late cancellation with exact cleanup failures

* test(mcp): use the shared budget for session workflows
2026-10-16 11:48:59 +08:00

11 KiB

MCP tool design

Decide whether to add a tool

Design around the analyst question. Inspect the current canonical contracts, provider capabilities, and nearest existing tool before changing the surface. Extend a tool when intent and result contract match; add one for a distinct outcome or materially different authority. Public names and semantics remain provider-neutral, with exact coverage reported per provider.

Keep the catalog complete; report capability/session availability instead of truncating schemas. Serialized bytes alone do not establish agent usability.

Choose the tool shape

Shape Use it for Result
inspect Facts about one target, object, or resource Relevant fields, locations, and facet availability
search / list Candidate discovery Stable ordering and context; pagination when the query or format needs it
trace Relationships across evidence Typed edges, supporting facts, and unresolved paths
compare Explicitly paired artifacts or observations Identity, comparable coverage, and supported deltas
workflow A recurring outcome needing composition Inline answer, contributing evidence, and partial facets
observe / capture Runtime behavior Authority, launch/attach effects, lifecycle, and cleanup status

These are task shapes, not mandatory prefixes. Name tools for the action and object callers reason about. Avoid unrelated discovery/execution/mutation modes inside one tool.

Prefer primitives; compose workflows

A primitive reports a reusable fact about an identified object or relationship. A format-specific decoder qualifies; one application's business interpretation belongs outside the general contract. Expose provider APIs when they add a useful capability that existing tools cannot express.

Compose a workflow when repeated use demonstrates a multi-source outcome REA can join without hiding caller choices or uncertainty. Batch homogeneous reads when that removes repeated work while preserving per-item outcomes. Keep prompts optional and avoid prescribing call sequences that a direct tool can replace.

Agents can compose experiments with ordinary commands, scripts, and local fixture servers. Custom orchestration languages, replay engines, or separate prepare/execute plans need an observed requirement those primitives cannot satisfy. A plan without execution does not establish runtime behavior.

Define the contract

  • Use strict inputs with explicit target identity and required fields. Preserve meaningful target, action, capture, and output choices; remove ignored options and repeated permission declarations.
  • Return the requested facts inline with artifact/provider identity, source locations, Evidence references, coverage, and relevant confidence and limitations. Use supported selection, summaries or views when they answer the question; preserve access to complete source Evidence and honor existing contracts and requested detail. Missing coverage is unknown, not absence. Avoid extra lookups solely to understand the answer or its provenance.
  • Keep observed, derived, and inferred relationships distinct. Preserve unresolved edges and partial provider facts instead of requiring every provider to supply the same metadata.
  • For caller/callee reads, relationship_coverage describes only the provider-observed direct relationship set. complete means that set was returned without a known read limit; it does not claim every possible runtime edge. Provider and execution caveats stay in limitations and do not imply incomplete relationship coverage. Missing coverage remains unknown.
  • Declare process, filesystem, network, UI, and mutation effects truthfully; enforce the requested target and lifecycle.
  • Add limits only for real format, protocol, authority, or measured resource constraints. Report pagination, truncation, cancellation, and partial failure. An assumed agent budget is insufficient justification for a limit.

Own the lifecycle

For tools that acquire resources, identify the owner and the observations that establish each transition. Include temporary files and one-shot commands, as well as persistent engine sessions. Use the existing provider contracts; providers need not expose identical state machines.

  • Keep target admission, provider readiness, active work, observed process exit, and resource release separate. A valid process handle, successful launcher, or selected target establishes only its own fact. Publish available identity and exit observations even when launch fails; retain unknown ownership.
  • Authenticate and capture identity through the same boundary on initial and recovery handshakes. Bound abandoned unauthenticated listeners where the protocol permits it; that deadline must not expire authenticated idle sessions or long-running native work. Listener expiry does not prove target closure.
  • Shared startup has one producer and independently cancellable caller waits. Keep it running while any caller still needs it. The final departing caller waits for owned cleanup and retains its failure. Provider closure is distinct from caller cancellation. Retained cleanup capabilities block replacement; derive readiness from the current startup's completion and live connection.
  • For cancellable observations, carry cancellation through result consumption and awaited cleanup. Before returning success, check the signal after the last awaited finalization step. Retain collected observations under typed cancellation. Preserve an existing primary failure and let incomplete cleanup retain its own classification. Preserve the caller's abort reason as the typed cancellation error's cause, including when cancellation is observed only after provider completion. Client cancellation alone does not prove native work stopped. For atomic publication, document the cancellation cutoff and preserve any committed output's publication receipt. Queued and retried work must retain the complete accepted execution controls, including non-default lifecycle deadlines and cancellation signals.
  • Release only owned resources. Attaching owns a connection or session unless the provider contract explicitly establishes target ownership. A helper exit, missing reply, or missing PID does not establish target exit.
  • Retain successful child cleanup and retry only unresolved children, including when close options change. Preserve observed child failure or activity when another child's health is unavailable; idle plus missing coverage stays unknown. Bind cleanup failures to their exact resource owner. Resolve current cleanup uncertainty only after that owner confirms release, while preserving the original failure and completed observations. Later failure composition must report only resources whose cleanup remains unresolved. Carry cleanup capabilities explicitly in typed outcomes so consumers can bind returned errors to exact owners without reparsing diagnostic cause chains. Bind every caller-visible retry error and aggregate, including an error already produced by the provider. An aggregate remains incomplete until all owners it reports are released; releasing one child cannot clear another child's status.
  • Await one-shot cleanup without discarding the completed result or primary diagnostic. Preserve partial observations, remaining resources, and the cleanup owner's recovery instructions. CLI adapters should reuse finishAnalysisCommand where its contract applies rather than duplicate close/finally handling.
  • Give descriptor-backed reads an owner before opening the file. A rejected close must retain the exact handle and preserve any earlier failure. If an abortable caller stops waiting, keep the underlying work in that owner's lifetime so shutdown cannot report release before finalization settles. Admit the entire inspection once, including later reads and finalization. Helpers must borrow that admission or use an operation child scope; re-entering the same draining scope can make cleanup wait for its own caller. Retry retained cleanup before admitting new work and keep admission blocked while it remains unresolved. Link the originating cleanup error to its exact owner so confirmed release resolves its cleanup marker while preserving the earlier diagnostic. Shared CLI/MCP adapters cannot infer that confirmation.
  • Release child descriptors before removing their containing root. Retain a composite owner when either stage fails so retry preserves that ordering. A Windows command leader's exit does not establish Job Object closure. Keep the process-tree observer and command lock alive through failed cleanup retries until the owned tree's close event.

Exercise the applicable lifecycle regression scenarios through the producing owner and affected CLI/MCP consumers.

Implement and evaluate

Put provider protocols/parsing in adapters and shared CLI/MCP workflows in the application layer. Share Evidence provenance, unknown projection, and session retention semantics; matching payloads alone do not establish parity.

Measure the resource before choosing a budget and enforce it before the costly step. Account for value alternatives, trace-frame products, retained records, serialization expansion, and temporary-file growth separately. Derive facet completeness from examined records and exhaustiveness, rather than diagnostic prose or an empty failure list. Preserve observations independently of cleanup success.

Update canonical contracts, output schemas, examples, generated artifacts, and relevant docs together. Verify converted advertised schemas and success, malformed, unavailable, cancellation, and partial outcomes through affected consumers. Real-engine claims require the matching provider lane; see testing.md.

For fixed tuples in public contracts, use src/domain/fixedTupleSchema.ts: Zod's tuple projection omits implicit length bounds and Draft 2020-12 items, which some clients require even alongside prefixItems. Tuples with a rest schema also need explicit minimum lengths. Check parser/schema parity for empty, short, overlong, and malformed values in both input and output projections.

When discoverability changes, evaluate representative broad, direct, and negative tasks on the intended host. Record host/model/catalog, selected tools, arguments, errors, retries, and task outcomes. Schema validity and keyword heuristics do not establish successful tool selection. See mcp-contracts.md for shipped runtime behavior.