EIP‑1193, CIP‑30, and Cosmos in one mental model


Introduction

If you build cross‑chain dApps in React, wallet integration is where entropy shows up first.

Ethereum wallets follow EIP‑1193 and usually inject a provider on window.ethereum.(Ethereum Improvement Proposals) Cardano wallets follow CIP‑30 and inject window.cardano.* APIs for each wallet (Nami, Eternl, Flint, etc.).(cips.cardano.org) Keplr covers Cosmos SDK chains and injects window.keplr plus helpers like getOfflineSigner.(docs.keplr.app)

The goal on the frontend is simple: one React connection layer, multiple ecosystems, predictable UX.

I’ll walk through how I think about MetaMask (EIP‑1193), Nami (CIP‑30), and Keplr in a single React app, with minimal focus on code and more on contracts, flows, and failure modes.


1. Three standards, one pattern

At a high level, all three follow the same idea: a browser extension injects a JavaScript object into the page; your dApp discovers it, asks for permission, then calls standardized methods.

ASCII view:

+------------------------+-------------------------+------------------------+
| Ethereum               | Cardano                 | Cosmos (Keplr)         |
+------------------------+-------------------------+------------------------+
| EIP‑1193 provider      | CIP‑30 API              | Keplr provider         |
| window.ethereum        | window.cardano. | window.keplr           |
|                        | (e.g. "nami")          | window.getOfflineSigner|
+------------------------+-------------------------+------------------------+

EIP‑1193 defines a minimal, event‑driven provider API intended to be available on window.ethereum, with a generic request style method and events like accountsChanged and chainChanged.(Ethereum Improvement Proposals)

CIP‑30 defines a dApp–wallet web bridge for Cardano, where each wallet injects a cardano[walletName] object that exposes an enable() method returning a capability‑scoped API for UTxO queries, address discovery, and transaction signing.(cips.cardano.org)

Keplr defines a browser‑injected window.keplr provider plus helpers for Cosmos’ preferred tooling (CosmJS offline signers etc.), and recommends calling keplr.enable(chainId) before using the signer.(docs.keplr.app)

Different names, same theme: injected provider, permission request, ecosystem‑specific methods.


2. EIP‑1193 and MetaMask: the Ethereum side

EIP‑1193 exists to standardize the “wallet provider” object that browser wallets expose. It defines:

  • availability on window.ethereum in web environments
  • a request({ method, params }) entrypoint for JSON‑RPC calls
  • an event interface for connect, disconnect, accountsChanged, chainChanged(Ethereum Improvement Proposals)

The typical MetaMask flow in React is conceptually:

1. Discover provider:
   is there an EIP‑1193 provider present

2. Request accounts:
   ask user to connect (e.g. eth_requestAccounts).

3. Listen for changes:
   update UI when accounts or chain change.

4. Use request():
   send JSON‑RPC calls via provider (sign, send tx, read chain state).

On modern Ethereum frontends there is also EIP‑6963 for multi‑wallet discovery (solving “which wallet owns window.ethereum?” races). MetaMask and others now recommend supporting it so multiple injected wallets can coexist cleanly.(zan.top)

From a React perspective I keep the abstraction thin: the “Ethereum connector” in my app owns the EIP‑1193 provider, normalises events into “connected, accounts, chain”, and never exposes raw provider details to the rest of the tree.


3. CIP‑30 and Nami: the Cardano side

CIP‑30 defines the Cardano dApp–wallet web bridge. The spec describes how wallets inject JavaScript into webpages and the exact API a dApp can expect once a user enables access.(cips.cardano.org)

The important pieces for a React app are:

  • Discovery. Each wallet injects a property on window.cardano. For Nami you typically check for window.cardano.nami and inspect its apiVersion and other metadata.

  • Enable. You call nami.enable() (or more generally cardano[wallet].enable()) to request permission. This returns a promise resolving to a CIP‑30 API object if the user approves.(cips.cardano.org)

  • API surface. The returned API gives you methods for reading network id, addresses, UTxOs, collateral, and for signing and submitting transactions (built with cardano-serialization-lib or similar).(cips.cardano.org)

ASCII view of the flow:

[ window.cardano.nami ]
            |
            v
    enable() requested
            |
   +--------+--------+
   | user approves   | user rejects
   v                 v
[ CIP-30 API ]     [ error ]

Libraries like dcSpark’s Adalib wrap this into a higher‑level “connector” interface, presenting a unified Cardano wallet API to the dApp.(Medium)

For React, I treat CIP‑30 much like EIP‑1193: a typed “Cardano connector” that hides wallet‑specific quirks and yields a standard set of capabilities: read addresses, get UTxOs, sign and submit transactions, listen to network info.


4. Keplr and Cosmos: the IBC‑centric side

Keplr is the de‑facto wallet for Cosmos SDK chains. It supports multiple ecosystems now, but from a Cosmos dApp point of view the main entrypoints are:(docs.keplr.app)

  • window.keplr – the browser‑injected provider
  • keplr.enable(chainId) – request permission for a specific chain
  • window.getOfflineSigner(chainId, signOptions) – get a signer compatible with CosmJS

The official docs recommend enabling Keplr before using the OfflineSigner, since that call may prompt the user to unlock the wallet and approve access.(docs.keplr.app)

In practice the flow mirrors the others:

1. Discover:
   is window.keplr present

2. Enable:
   keplr.enable(chainId) to request access for the cosmos chain you care about.

3. Get signer:
   obtain an OfflineSigner for CosmJS, then hand it to your client library.

4. Use:
   query, sign, and broadcast, plus handle chain suggestions if needed.

From React’s angle, the “Keplr connector” tracks which chainIds you’ve enabled, which address you’re currently using, and any errors encountered. The actual queries and broadcast go through CosmJS or other Cosmos SDK clients, not through random custom code spread across components.(tutorials.cosmos.network)


5. Designing a unified wallet layer in React

Once you look past names and chain specifics, the three ecosystems share a common shape:

- discover provider
- request permission / enable
- derive basic identity (address, network)
- expose capabilities (sign, send, read)
- listen to changes (accounts/network)

I formalise that as an internal “WalletConnector” contract in the app, independent of any library:

WalletConnector
  id          : "metamask" | "nami" | "keplr" | ...
  ecosystem   : "ethereum" | "cardano" | "cosmos"
  connect()   : ask user, then resolve to "connected" state
  disconnect(): clear local state (wallet keeps secrets)
  getAccounts(): one or more addresses
  getNetwork(): chain info
  capabilities: signTx, signMessage, sendTx, ...

Each concrete connector adapts its underlying standard:

MetaMask connector:
   uses EIP‑1193 provider, maps request() and events.

Nami connector:
   uses CIP‑30 API from cardano.nami.enable().

Keplr connector:
   uses window.keplr + OfflineSigner for CosmJS.

ASCII picture of the adapter idea:

[ MetaMask  ] --+
[ Nami      ] --+--> [ WalletConnector interface ] --> React state / hooks
[ Keplr     ] --+

This lets the React app talk to “the selected wallet” without constantly branching on if (ecosystem === 'cardano') .... New wallets are new connectors, not new code paths everywhere.

Multi‑wallet helpers like Web3Modal on the Ethereum side or Cardano connector libraries all exist because this adapter pattern is useful; they just ship a lot of the wiring for you.(Medium)


6. UX and state: connect flows that don’t confuse people

Once you abstract the providers, the hardest part is UX: when do you ask to connect, what do you show while waiting, and how do you represent partial access

A few principles I keep:

Connection and selection are not the same thing. “Select Nami” or “Select MetaMask” is choosing a connector; “Connect” is the moment you call enable() or request() and prompt the user. Keep those two as separate steps in your UI so users understand what is happening.

State diagram for one wallet:

[ not available ]   (extension missing)
[ available ]  → [ selected ] → [ connecting ] → [ connected ]
                                       ↓
                                   [ error ]

Network awareness. All three ecosystems can run multiple networks (testnets, custom chains). Your React state should track both “wallet says we are on chain X” and “this dApp is configured for chains Y/Z”. For Ethereum, that’s EVM chainId. For Cardano, CIP‑30 exposes getNetworkId (mainnet vs testnet). For Keplr, you explicitly pass chainId into enable().(cips.cardano.org)

Display mismatches clearly: wrong network banners, disabled actions, and a call‑to‑action (“switch to Cardano mainnet in Nami”, “switch to Cosmos Hub in Keplr”).

Statelessness around secrets. The React app never deals with mnemonics or private keys. All three APIs are designed so that signing happens inside the wallet; dApps request signatures, they do not construct or persist sensitive material.(Ethereum Improvement Proposals)


7. Security and permissions

Standards give you structure, not security. The wallets enforce key custody, but your integration affects how hard it is for users to see what they’re signing.

A few things I pay attention to:

Scope requests narrowly. Keplr asks for a specific chainId; CIP‑30 wallets scope access per site; EIP‑1193 wallets often show which methods are being requested. Avoid generic “connect to everything” flows when you only need read‑only access.

Prefer explicit, audited signing flows. For Ethereum, that means clear separation between “sign message to authenticate” and “sign transaction to move funds”; EIP‑1193 providers expose different methods (eth_sendTransaction, personal_sign, etc.).(0xjac) For Cardano and Cosmos, show human‑readable summaries where possible before triggering transaction signing.

Handle absence gracefully. If no provider is found (no MetaMask, no Nami, no Keplr), guide the user to install a wallet or use a fallback like WalletConnect, don’t just fail silently. Many multi‑wallet UX guides emphasize this first‑run experience as a key conversion point.(Medium)


8. Experience callouts

Production note – Cardano multi‑wallet. Once you standardise on CIP‑30 you stop caring which Cardano wallet the user prefers. Nami, Eternl, Flint all expose the same base API. The only real difference in the React app is the icon and display name; transaction building and signing stay identical.

Production note – EIP‑1193 and discovery. On the Ethereum side, multi‑wallet discovery is where complexity creeps in. EIP‑1193 gives you the shape of the provider, but not how to choose between multiple injected providers. EIP‑6963 fills that gap; building your “Ethereum connector” on top of it means you don’t have to invent yet another discovery hack.(zan.top)

Production note – Cosmos chain proliferation. With Keplr, the hard part isn’t connecting once; it’s handling dozens of Cosmos chains. Centralising “which chainIds do we support and what are their RPC endpoints?” in one configuration layer makes the React side much easier to reason about.


Conclusion

Under the hood, MetaMask, Nami, and Keplr speak different dialects: EIP‑1193, CIP‑30, and Cosmos‑specific providers.

From a React dApp’s point of view, they all fit the same pattern:

- discover a browser-injected provider
- request permission in a user-visible way
- normalise "who am I" and "which network am I on"
- expose a small set of capabilities to the rest of the app
- listen for account/network changes and keep state in sync

If you design a clean wallet connector layer around those ideas—one contract, multiple adapters—the rest of your UI can treat “connect wallet” as a single, predictable action, regardless of whether the user lives on Ethereum, Cardano, Cosmos, or all three at once.

Completion scope and production contract

This completion review turns the earlier conceptual treatment into a release-oriented engineering contract. It treats Wallet Integration in React: MetaMask, Nami, and Keplr as a typed web, wallet, or blockchain API component, follows a user intent, API representation, wallet request, stream update, or transaction lifecycle event through validation and durable state, and separates normative requirements from implementation policy. The normative baseline is the published API or wallet specification, TypeScript model, and chain confirmation rules; deployment defaults, caching, retry limits, and operator thresholds are explicitly local policy. 10

The intended audience is experienced developers and architects. Readers should understand the surrounding chain or application model, typed data structures, persistence, and basic security engineering. The scope includes correctness, implementation boundaries, deterministic tests, failure recovery, security, performance, and observability. It does not claim that the educational companion is a drop-in replacement for a maintained protocol or cryptographic library. Production adoption requires an independent threat model, compatibility testing against the authoritative implementation, and operational ownership. 11

The mental model used throughout is deliberately strict: untrusted input crosses browser, wallet extension, transport, API, cache, and accessibility boundaries; a validator derives facts under the published API or wallet specification, TypeScript model, and chain confirmation rules; accepted transitions update server-derived data, wallet session, UI intent, and explicit transaction state; and observers consume committed facts, never optimistic intermediate mutations. A guarantee is stated only when it follows from those rules and assumptions. Heuristics such as fee selection, caching, peer scoring, timeouts, user messaging, or alert thresholds remain policy and may be tuned without redefining validity. 12

Reader contract and scope

For Wallet Integration in React: MetaMask, Nami, and Keplr, this review makes the exact user decision and the prerequisites needed to make it safely explicit. Start from one user intent, API representation, wallet request, stream update, or transaction lifecycle event and write down its origin, canonical representation, validation context, authority, and durable outcome. The typed web, wallet, or blockchain API component must not infer a stronger fact from transport success, cache presence, or an upstream acknowledgement. Its authoritative state is server-derived data, wallet session, UI intent, and explicit transaction state, and every projection must remain rebuildable or reconcilable from committed facts. This framing distinguishes a protocol guarantee from an operational convenience and gives reviewers a concrete place to challenge an assumption. 10

The principal failure to design against is an attractive example being mistaken for a complete production design. Address it before optimizing by defining a narrow ownership boundary, stable identities, bounded resource use, and a machine-readable outcome for every rejected transition. Record a scope statement, excluded concerns, and a reviewable acceptance criterion. A reviewer should be able to trace each accepted result to input bytes, rule or policy version, prior state, and commit identity without relying on prose logs. When the authority cannot be reached or context is incomplete, return an explicit unavailable or pending state; do not convert uncertainty into acceptance.

Precise vocabulary and authority

Treat precise vocabulary and authority as part of the executable design of Wallet Integration in React: MetaMask, Nami, and Keplr, not as documentation added after coding. The relevant operating envelope includes initial load, pagination, reconnect, chain switch, signing, pending confirmation, and partial API failure. For each mode, identify which state is authoritative, which work may be retried, what is bounded, and which observation proves progress. This is especially important across browser, wallet extension, transport, API, cache, and accessibility boundaries, where delivery and processing are different events and where local time or arrival order may not reflect authoritative order. 11

A useful review asks how the design behaves under stale closure, duplicate update, wallet rejection, chain mismatch, schema drift, reconnect storm, and misleading confirmation. The unsafe outcome is teams using the same word for incompatible states or guarantees. Prevent it with explicit preconditions and postconditions, and retain a glossary tied to the normative authority for every overloaded term as release evidence. Use stable codes rather than exception text, keep policy configuration versioned, and attach the accepted policy or rule version to durable results. An frontend or API-platform operator must be able to stop intake, drain or quarantine work, compare local state with authority, and resume without inventing a second side effect.

Trust assumptions

The implementation of Wallet Integration in React: MetaMask, Nami, and Keplr should expose which actors, clocks, stores, libraries, and upstream systems may fail or act maliciously through types and module boundaries. Parse external representations once, preserve the original identity when audit or replay needs it, and pass validated domain values inward. Mutations of server-derived data, wallet session, UI intent, and explicit transaction state belong behind one authoritative transition function or transaction boundary. Network clients, storage adapters, user interfaces, and telemetry exporters must not duplicate consensus or business rules. That separation keeps deterministic logic testable and prevents a library upgrade from silently redefining validity. 12

Assume that an implicit trusted component invalidating the claimed guarantee will eventually occur in a staging fault test or production incident. The control is not a catch-all retry: classify the outcome, decide whether the identical operation is safe to repeat, bound attempts and elapsed time, and surface terminal work for reconciliation. The minimum evidence is a trust-boundary diagram and an assumption register with owners. Keep addresses, wallet permissions, access tokens, KYC data, transaction intent, and browser storage out of ordinary logs, and prefer hashes, version identifiers, counts, and sanitized reason codes. Any emergency bypass must be narrow, time-limited, approved, and observable.

Architecture and ownership

Verification for Wallet Integration in React: MetaMask, Nami, and Keplr must demonstrate component responsibilities and the direction in which facts and commands move at several layers. Small tests cover deterministic transforms and boundary values; contract tests pin serialized forms; integration tests exercise real adapters; replay tests compare state roots or projections; and fault tests interrupt work at every commit boundary. The dataset must include normal history, malformed input, duplicates, reordering, maximum-size values, and changes of rule or schema version. A passing example is evidence about that environment and dataset, not a universal performance claim. 10

Make two components both believing they own the same transition a named negative test. The release packet should retain a context diagram, ownership table, and dependency rule, exact dependency and tool versions, the deterministic command, and its result. For performance work, report warm-up, repetitions, concurrency, percentiles, resource limits, and the point where backpressure begins. For security work, include abuse cases and independent review. A change is ready only when failures leave server-derived data, wallet session, UI intent, and explicit transaction state safe, recovery is rehearsed, and telemetry explains both user-visible outcome and operator action.

Canonical representation

For Wallet Integration in React: MetaMask, Nami, and Keplr, this review makes the byte-level or schema-level representation used for hashing, comparison, storage, and transport explicit. Start from one user intent, API representation, wallet request, stream update, or transaction lifecycle event and write down its origin, canonical representation, validation context, authority, and durable outcome. The typed web, wallet, or blockchain API component must not infer a stronger fact from transport success, cache presence, or an upstream acknowledgement. Its authoritative state is server-derived data, wallet session, UI intent, and explicit transaction state, and every projection must remain rebuildable or reconcilable from committed facts. This framing distinguishes a protocol guarantee from an operational convenience and gives reviewers a concrete place to challenge an assumption. 11

The principal failure to design against is semantically equal values producing different identifiers or verification outcomes. Address it before optimizing by defining a narrow ownership boundary, stable identities, bounded resource use, and a machine-readable outcome for every rejected transition. Record golden encodings, round-trip tests, and rejection of non-canonical forms. A reviewer should be able to trace each accepted result to input bytes, rule or policy version, prior state, and commit identity without relying on prose logs. When the authority cannot be reached or context is incomplete, return an explicit unavailable or pending state; do not convert uncertainty into acceptance.

State-machine model

Treat state-machine model as part of the executable design of Wallet Integration in React: MetaMask, Nami, and Keplr, not as documentation added after coding. The relevant operating envelope includes initial load, pagination, reconnect, chain switch, signing, pending confirmation, and partial API failure. For each mode, identify which state is authoritative, which work may be retried, what is bounded, and which observation proves progress. This is especially important across browser, wallet extension, transport, API, cache, and accessibility boundaries, where delivery and processing are different events and where local time or arrival order may not reflect authoritative order. 12

A useful review asks how the design behaves under stale closure, duplicate update, wallet rejection, chain mismatch, schema drift, reconnect storm, and misleading confirmation. The unsafe outcome is an impossible intermediate state becoming durable after interruption. Prevent it with explicit preconditions and postconditions, and retain a transition table exercised by positive, negative, and replay tests as release evidence. Use stable codes rather than exception text, keep policy configuration versioned, and attach the accepted policy or rule version to durable results. An frontend or API-platform operator must be able to stop intake, drain or quarantine work, compare local state with authority, and resume without inventing a second side effect.

Invariants

The implementation of Wallet Integration in React: MetaMask, Nami, and Keplr should expose properties that must hold before and after every accepted operation through types and module boundaries. Parse external representations once, preserve the original identity when audit or replay needs it, and pass validated domain values inward. Mutations of server-derived data, wallet session, UI intent, and explicit transaction state belong behind one authoritative transition function or transaction boundary. Network clients, storage adapters, user interfaces, and telemetry exporters must not duplicate consensus or business rules. That separation keeps deterministic logic testable and prevents a library upgrade from silently redefining validity. 10

Assume that local success concealing corruption in a related aggregate or index will eventually occur in a staging fault test or production incident. The control is not a catch-all retry: classify the outcome, decide whether the identical operation is safe to repeat, bound attempts and elapsed time, and surface terminal work for reconciliation. The minimum evidence is executable assertions at the narrowest authoritative boundary. Keep addresses, wallet permissions, access tokens, KYC data, transaction intent, and browser storage out of ordinary logs, and prefer hashes, version identifiers, counts, and sanitized reason codes. Any emergency bypass must be narrow, time-limited, approved, and observable.

Validation pipeline

Verification for Wallet Integration in React: MetaMask, Nami, and Keplr must demonstrate cheap structural checks, contextual checks, authoritative verification, and commit order at several layers. Small tests cover deterministic transforms and boundary values; contract tests pin serialized forms; integration tests exercise real adapters; replay tests compare state roots or projections; and fault tests interrupt work at every commit boundary. The dataset must include normal history, malformed input, duplicates, reordering, maximum-size values, and changes of rule or schema version. A passing example is evidence about that environment and dataset, not a universal performance claim. 11

Make expensive or stateful work running before malformed input is rejected a named negative test. The release packet should retain ordered validation stages with stable machine-readable rejection codes, exact dependency and tool versions, the deterministic command, and its result. For performance work, report warm-up, repetitions, concurrency, percentiles, resource limits, and the point where backpressure begins. For security work, include abuse cases and independent review. A change is ready only when failures leave server-derived data, wallet session, UI intent, and explicit transaction state safe, recovery is rehearsed, and telemetry explains both user-visible outcome and operator action.

Error semantics

For Wallet Integration in React: MetaMask, Nami, and Keplr, this review makes the distinction between invalid input, conflict, unavailable dependency, retryable interruption, and internal defect explicit. Start from one user intent, API representation, wallet request, stream update, or transaction lifecycle event and write down its origin, canonical representation, validation context, authority, and durable outcome. The typed web, wallet, or blockchain API component must not infer a stronger fact from transport success, cache presence, or an upstream acknowledgement. Its authoritative state is server-derived data, wallet session, UI intent, and explicit transaction state, and every projection must remain rebuildable or reconcilable from committed facts. This framing distinguishes a protocol guarantee from an operational convenience and gives reviewers a concrete place to challenge an assumption. 12

The principal failure to design against is blind retries amplifying a permanent failure or changing user intent. Address it before optimizing by defining a narrow ownership boundary, stable identities, bounded resource use, and a machine-readable outcome for every rejected transition. Record typed errors mapped consistently across logs, metrics, APIs, and queues. A reviewer should be able to trace each accepted result to input bytes, rule or policy version, prior state, and commit identity without relying on prose logs. When the authority cannot be reached or context is incomplete, return an explicit unavailable or pending state; do not convert uncertainty into acceptance.

Concurrency control

Treat concurrency control as part of the executable design of Wallet Integration in React: MetaMask, Nami, and Keplr, not as documentation added after coding. The relevant operating envelope includes initial load, pagination, reconnect, chain switch, signing, pending confirmation, and partial API failure. For each mode, identify which state is authoritative, which work may be retried, what is bounded, and which observation proves progress. This is especially important across browser, wallet extension, transport, API, cache, and accessibility boundaries, where delivery and processing are different events and where local time or arrival order may not reflect authoritative order. 10

A useful review asks how the design behaves under stale closure, duplicate update, wallet rejection, chain mismatch, schema drift, reconnect storm, and misleading confirmation. The unsafe outcome is a check-then-act race accepting two individually plausible operations. Prevent it with explicit preconditions and postconditions, and retain a linearization argument plus stress tests at the chosen contention boundary as release evidence. Use stable codes rather than exception text, keep policy configuration versioned, and attach the accepted policy or rule version to durable results. An frontend or API-platform operator must be able to stop intake, drain or quarantine work, compare local state with authority, and resume without inventing a second side effect.

Idempotency and replay

The implementation of Wallet Integration in React: MetaMask, Nami, and Keplr should expose how duplicate delivery, process restart, and historical backfill preserve the same result through types and module boundaries. Parse external representations once, preserve the original identity when audit or replay needs it, and pass validated domain values inward. Mutations of server-derived data, wallet session, UI intent, and explicit transaction state belong behind one authoritative transition function or transaction boundary. Network clients, storage adapters, user interfaces, and telemetry exporters must not duplicate consensus or business rules. That separation keeps deterministic logic testable and prevents a library upgrade from silently redefining validity. 11

Assume that at-least-once delivery creating a second side effect will eventually occur in a staging fault test or production incident. The control is not a catch-all retry: classify the outcome, decide whether the identical operation is safe to repeat, bound attempts and elapsed time, and surface terminal work for reconciliation. The minimum evidence is stable operation identities, deduplication state, and deterministic replay fixtures. Keep addresses, wallet permissions, access tokens, KYC data, transaction intent, and browser storage out of ordinary logs, and prefer hashes, version identifiers, counts, and sanitized reason codes. Any emergency bypass must be narrow, time-limited, approved, and observable.

Persistence and atomicity

Verification for Wallet Integration in React: MetaMask, Nami, and Keplr must demonstrate which facts commit together and how derived views catch up at several layers. Small tests cover deterministic transforms and boundary values; contract tests pin serialized forms; integration tests exercise real adapters; replay tests compare state roots or projections; and fault tests interrupt work at every commit boundary. The dataset must include normal history, malformed input, duplicates, reordering, maximum-size values, and changes of rule or schema version. A passing example is evidence about that environment and dataset, not a universal performance claim. 12

Make a crash exposing a cursor that claims work whose state was not committed a named negative test. The release packet should retain transaction boundaries, durable checkpoints, and reconciliation queries, exact dependency and tool versions, the deterministic command, and its result. For performance work, report warm-up, repetitions, concurrency, percentiles, resource limits, and the point where backpressure begins. For security work, include abuse cases and independent review. A change is ready only when failures leave server-derived data, wallet session, UI intent, and explicit transaction state safe, recovery is rehearsed, and telemetry explains both user-visible outcome and operator action.

References