Make it your team's harness
The harness ships an execution standard and a deliberately small bundled library. Everything that is specific to what you work on — the journeys, the assertions, the checklists, the review knowledge — lives in a library you own and point the harness at. MetaMask Perps is the worked example on this page.
The harness supplies the execution standard; your library supplies the domain. A team shares one library. An engineer can add their own on top. Neither requires a change to the harness.
What lives where
| Layer | Owns | Does not own |
|---|---|---|
mm-harness |
Launch, typed actions, graph execution, screenshots, video, traces, and artifact contracts. | Anything specific to one team's domain. |
| Your library | Reusable setup, journeys, assertions, cleanup, platform variants, and checklist templates. | Wallet credentials, generated evidence, or product source. |
| Product checkout | The real Extension, Mobile, or Core code and runtime being proved. | A substitute test implementation. |
Point the harness at a library
One environment variable does it. The Perps library is private, so start by asking its owners for access — a clone failure means your GitHub account does not have it yet.
$ git clone git@github.com:MetaMask/experimental-metamask-recipe-perps.git \
"$HOME/shared-library/metamask-recipe-perps"
$ export RECIPE_LIBRARY_PATH="perps=$HOME/shared-library/metamask-recipe-perps"
$ mm-harness run --list
name=path gives the source its alias; without one the directory name is used. Separate
several libraries with : — order is precedence, first wins:
$ export RECIPE_LIBRARY_PATH="mine=$HOME/my-recipes:perps=$HOME/shared-library/metamask-recipe-perps"
Use --library name=/path for a single command instead — useful when a tool shell loses
an exported variable. Resolution follows your library order, then the bundled MetaMask library.
A directory at ~/.farmslot/recipe-library is picked up automatically as
personal when you set no library at all. It is a fallback, not an addition: as soon
as RECIPE_LIBRARY_PATH or --library is set, only those sources are used.
List it explicitly to keep both.
The list shows only recipes runnable in the current product checkout. A recipe marked
[perps] came from that library; [metamask] means it ships with the
harness. Read the selected platform variant with --describe before running it.
What a team library actually holds
Every path below is real, from the Perps library. Copy the shape, not the contents — it is the reference structure for a MetaMask team building its own.
manifests/extension.action-manifest.json types the actions you add
actions/extension/perps/ domain actions the product needs
recipes/mobile/perps/ per-adapter journeys
recipes/extension/perps/
recipes/core/perps/
checklists/dev/ your own Recipe Cook checklist templates
checklists/fix-bug/
docs/ the knowledge: authoring rules,
performance evidence, analytics
The docs/ and checklists/ directories are the part teams underestimate.
They are how a team's hard-won conventions — how performance evidence is gathered here, which
analytics events matter, what a finished task looks like — stop living in one engineer's head.
One team surface sits outside the recipe library: the static antipattern packs the PR QA review
reads, which ship with the skills as
references/antipatterns/<repo>/<pack>.md. Today there is a
perps pack for Mobile, Extension, and Core, and a Mobile-only
unit-vs-cv pack. A pack is advisory: it never changes an acceptance-criterion
verdict.
Your first live proof
Complete the walkthrough first so the app, device, and wallet fixture are
ready. With RECIPE_LIBRARY_PATH exported, the library's recipes are just names.
Extension or Mobile
$ mm-harness run perps.open-market --describe
$ mm-harness run perps.open-market network=testnet market=BTC --plan
$ mm-harness run perps.open-market network=testnet market=BTC \
--record-video=full-run --artifacts-dir ./perps-open-market-evidenceCore
$ mm-harness run perps.snapshot-market --describe
$ mm-harness run perps.snapshot-market network=testnet market=BTC \
--artifacts-dir ./perps-market-evidence
A passing run writes summary.json, per-node results in trace.json, and an
artifact manifest beside screenshots or video. Visual review surfaces can render those same
recipe-derived artifacts without changing the proof graph.
What the Perps team put in theirs
A sample of the library's recipes, as an idea of the granularity that works: small, parameterized, composed rather than duplicated.
| Claim | Recipe | Platforms |
|---|---|---|
| Reach a live market detail screen | perps.open-market | Mobile, Extension |
| Read positions and orders | perps.snapshot-market | Mobile, Extension, Core |
| Place, assert, and clean up an order | perps.prove-order | Mobile, Extension, Core |
| Manage or close a position | perps.manage-position | Mobile, Extension, Core |
| Prove real MetaMetrics emissions | perps.analytics-lifecycle | Mobile, Extension |
| Measure the open-market journey | perps.performance-open-market | Mobile, Extension |
| Exercise edit-order contracts | perps.prove-edit-order | Core |
Always run --describe and --plan before a state-changing recipe. The
library defaults to testnet and cleans up testnet state. Never turn an onboarding proof into a
mainnet mutation; mainnet operations require the recipe's explicit real-funds confirmation.
Starting your team's library
- Create a repository with the directory shape above. It holds no accounts, fixtures, credentials, or generated evidence.
- Parameterize stable choices such as market, side, order type, and network.
- Compose existing setup, assertion, and cleanup recipes before adding a new top-level recipe.
- Add a platform variant only when the products genuinely differ.
- If a locator or screen assertion fails, classify it first. Intentional product change may mean small recipe drift; otherwise preserve the failure and fix the product, runtime, or harness layer that owns it.
- Re-run every caller after a shared node changes and attach the fresh evidence.
Keep it small. Compose or repair the closest recipe before creating another one — a library that grows a scenario per ticket stops being maintainable faster than the product does.