@deeeed/metamask-harness
v0.16.0
Published
Architecture: [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md)
Readme
MetaMask Recipe Runner — mm-harness
Architecture: docs/ARCHITECTURE.md
mm-harness is built for the agent but easy for the human to use and
understand. It is the MetaMask recipe harness: one front door to launch the app,
prove behavior with recipes, and manage the per-checkout runtime overlay. It is a
thin MetaMask adapter over the generic @farmslot/recipe-harness engine — the
runner never re-implements graph execution, it binds to it. The agent gets full
depth (explicit flags, --json, ports, build tiers); the human gets short
commands, positional targets, good defaults, completions, and teaching
help/errors. Run it from inside a MetaMask Mobile/Extension/Core checkout and the
platform is auto-detected.
DAILY LOOP launch · logs · debug · fixtures what a teammate runs many times a day
DISCOVER actions · call · flows compose recipes from the vocabulary + flow library
PROVE run · doctor run recipes, check readiness
RUNTIME OVERLAY install · verify · cleanup manage the per-checkout runtime overlayOne bin, no per-platform binaries. mm-harness is the only command. Grammar:
mm-harness <command> [target] [flags] — the human types the bare command
(platform auto-detected) or a positional target (mm-harness launch ios); depth
lives in FLAGS with good defaults, never in extra command names. It ships shell
completions (zsh + bash) for commands, targets, and flags.
Naming: "harness" is the tool (mm-harness) and the generic engine package
(@farmslot/recipe-harness); the files it installs into a checkout are the
runtime overlay. Read MENTAL-MODEL.md first (the
before→after map), then CLI-SPEC.md for the full contract.
One package, two responsibilities:
- Recipe layer: manifests, recipes, Mobile/Extension adapters, proof output.
- Runtime layer: start/reuse Metro or Chrome, seed fixtures, wait for readiness.
Farmslot and skills are wrappers around this CLI; they should not copy runner logic.
Quick start
Run from inside a MetaMask checkout (platform auto-detected):
# 1) Launch the app (installs the runtime overlay on first use, boots Metro/build)
mm-harness launch ios # mobile: ios | android; extension: just `launch`
# 2) Run a recipe and write evidence
mm-harness run recipe.json --adapter mobile --artifacts-dir /tmp/recipe-artifacts --jsonMobile and Extension runs also write diagnostics.json from application events
emitted during that run. Mobile combines its bounded in-app issue buffer with
run-scoped log bytes; Extension owns a CDP console subscription for the run.
These redacted warnings/errors are non-blocking side findings: they help spot
adjacent bugs without claiming the recipe or current change caused them. Core
is headless, so this is N/A there.
Outputs: summary.json, trace.json, screenshots, logs, and an artifact
manifest.
Mental model
mm-harness launch # runtime/orchestration: the app is ready
mm-harness run # runner/proof: actions execute and evidence is savedDo not mix those layers. library/ = what recipes ARE (recipes, actions, manifests — the content). src/ = what EXECUTES them (TypeScript CLI/engine glue, typed adapters). adapters/ = what CONTROLS the app and its instances (start, windows, wallet state, health, ports, parallel).
Useful commands
# Capabilities (compose recipes from the vocabulary + flow library)
mm-harness actions --adapter mobile --categories --json # compact discovery
mm-harness actions --adapter mobile --category ui --json # bounded vocabulary
mm-harness actions --adapter mobile --raw # raw action-manifest dump
mm-harness call unlock --adapter extension # run one action via the real engine path
mm-harness flows --json # reusable library flows, with provenance
# Readiness + proof
mm-harness doctor # read-only readiness check (no launch)
mm-harness doctor --fix # heal the overlay without launching
mm-harness run recipe.json --plan --adapter mobile # validate + plan, touch nothing
# Daily loop (run from inside a checkout)
mm-harness launch ios # launch (mobile: ios | android)
mm-harness logs # tail Metro/webpack + app logs
mm-harness debug # open the debug console
mm-harness fixtures sync # sync fixtures; `fixtures set` seeds the wallet
# Keep the install current
mm-harness update # upgrade to the published latest (prints old → new)
mm-harness update --check --json # report only — { current, latest, updateAvailable }A passive once-a-day nudge prints one stderr line when a newer version is
published (mm-harness <cur> → <latest> available · run: mm-harness update). It
never blocks and never auto-updates. Silence it with MM_HARNESS_NO_UPDATE_CHECK=1
(auto-off in CI).
Set RECIPE_LOG_UI=compact|full|quiet (default compact) and RECIPE_LOG_EVENTS=10
to tune the compact log view during launch and Metro/webpack startup.
Layout
bin/ the mm-harness entrypoint
src/ what EXECUTES recipes: TypeScript CLI/engine glue +
typed per-adapter logic (src/adapters/)
adapters/ what CONTROLS the app and its instances: launch/live/
watch/windows/wallet state/health/inject/cleanup per
platform, shared glue, porcelain, overlay payload +
manifest.json
library/ what recipes ARE: recipes/ (recipe JSONs), actions/
(per-platform implementations), manifests/ (capability)
scripts/ dev tooling (yarn check, local farmslot link, e2e
validation, adapter-surface-doctor)
docs/ details when this README is not enoughDefaults for installed harness/runtime paths live in
adapters/shared/path-defaults.json.
Runtime-readiness ownership
Generic readiness mechanics live in @farmslot/recipe-harness (0.3+):
@farmslot/recipe-harness/runtime/deps-readiness— install fingerprint + baseline@farmslot/recipe-harness/runtime/log-analysis— bundle-log boundaries, unresolved-module scoping@farmslot/recipe-harness/runtime/metro-probe— Metro/statusreachability
This repo adds MetaMask adapters only: adapters/mobile/deps-markers.ts,
adapters/mobile/runtime-decision.ts, adapters/extension/runtime-decision.ts,
and shell launch in adapters/porcelain/mm-recipe / adapters/porcelain/mme-recipe. Do not re-copy harness
primitives into adapters/shared/. CI installs harness from npm — publish
@farmslot/recipe-harness before bumping the dependency here.
Dev/prod harness (MM_HARNESS_BIN)
The global npm install owns the mm-harness on PATH — that is the prod
harness. To run a dev checkout with active pre-release changes alongside it,
set MM_HARNESS_BIN to that checkout's bin/mm-harness:
export MM_HARNESS_BIN=/path/to/checkout/bin/mm-harness # dev: this checkout owns the run
unset MM_HARNESS_BIN # prod: the installed/global binMM_HARNESS_BIN is the single override point: when set, the entrypoint hands the
whole invocation to that path before doing any dependency work; when unset it
resolves the installed bin. A dev checkout is never installed globally, so there is
no PATH collision — dev is reached only through MM_HARNESS_BIN (or a shell
alias). A per-farm environment can export it so the dev farm dispatches through the
dev harness while the prod farm leaves it unset.
Runtime notes
tmuxis recommended for long-lived Metro/webpack processes; standalone use falls back to detachednohupwhere possible.- Mobile may inject a local development bridge/HUD into older checkouts. Do not commit those product patches.
- Extension does not patch product source; it drives
dist/chromethrough Chrome CDP.
Validate changes
yarn check
bash -n bin/mm-harness adapters/porcelain/{metamask-recipe,mm-recipe,mme-recipe} adapters/{mobile,extension,core}/*.sh
node --check adapters/extension/inject.mjs adapters/extension/cleanup.mjs adapters/extension/readiness.mjs adapters/extension/launch-browser.cjs adapters/shared/open-debug.mjs adapters/shared/cli-home.mjs adapters/shared/log-tui.mjs adapters/shared/progress.mjs adapters/shared/recipe-paths.mjs scripts/check.mjsMore detail: Architecture, Package boundaries, Runtime file conventions.
