@deeeed/metamask-harness 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (158) hide show
  1. package/CHANGELOG.md +161 -0
  2. package/README.md +140 -0
  3. package/bin/mm-harness +99 -0
  4. package/docs/CHEATSHEET.md +61 -0
  5. package/docs/CLI-SPEC.md +915 -0
  6. package/docs/MENTAL-MODEL.md +295 -0
  7. package/docs/architecture.md +367 -0
  8. package/docs/extension-runtime-commands.md +60 -0
  9. package/docs/harness-cli.md +43 -0
  10. package/docs/live-adapter-contract.md +188 -0
  11. package/docs/package-boundaries.md +47 -0
  12. package/docs/perps-flow-catalog.md +235 -0
  13. package/docs/recipe-libraries.md +95 -0
  14. package/docs/runtime-file-conventions.md +36 -0
  15. package/library/actions/core/perps/_controller.mjs +727 -0
  16. package/library/actions/core/perps/assert_orders.mjs +53 -0
  17. package/library/actions/core/perps/assert_positions.mjs +52 -0
  18. package/library/actions/core/perps/close_orders.mjs +97 -0
  19. package/library/actions/core/perps/close_positions.mjs +118 -0
  20. package/library/actions/core/perps/ensure_orders.mjs +40 -0
  21. package/library/actions/core/perps/ensure_positions.mjs +37 -0
  22. package/library/actions/core/perps/place_order.mjs +201 -0
  23. package/library/actions/core/perps/read_account.mjs +30 -0
  24. package/library/actions/core/perps/read_orders.mjs +27 -0
  25. package/library/actions/core/perps/read_positions.mjs +27 -0
  26. package/library/actions/core/perps/start_state.mjs +92 -0
  27. package/library/actions/core/perps/teardown_state.mjs +86 -0
  28. package/library/actions/extension/perps/assert_orders.mjs +11 -0
  29. package/library/actions/extension/perps/assert_positions.mjs +11 -0
  30. package/library/actions/extension/perps/close_orders.mjs +8 -0
  31. package/library/actions/extension/perps/close_positions.mjs +8 -0
  32. package/library/actions/extension/perps/ensure_orders.mjs +4 -0
  33. package/library/actions/extension/perps/ensure_positions.mjs +4 -0
  34. package/library/actions/extension/perps/perps.mjs +730 -0
  35. package/library/actions/extension/perps/place_order.mjs +7 -0
  36. package/library/actions/extension/perps/read_orders.mjs +4 -0
  37. package/library/actions/extension/perps/read_positions.mjs +3 -0
  38. package/library/actions/extension/platform/cdp.mjs +541 -0
  39. package/library/actions/extension/ui/navigate.mjs +44 -0
  40. package/library/actions/extension/wallet/ensure_unlocked.mjs +36 -0
  41. package/library/actions/extension/wallet/read_state.mjs +27 -0
  42. package/library/actions/extension/wallet/select_account.mjs +48 -0
  43. package/library/actions/extension/wallet/setup.mjs +35 -0
  44. package/library/actions/mobile/app-overlay/app/dev-tools/AgenticService/AgentStepHud.tsx.patch +185 -0
  45. package/library/actions/mobile/app-overlay/app/dev-tools/AgenticService/AgenticService.ts.patch +1662 -0
  46. package/library/actions/mobile/bridge-runtime/cdp-bridge.cjs +686 -0
  47. package/library/actions/mobile/bridge-runtime/lib/cdp-eval.cjs +110 -0
  48. package/library/actions/mobile/bridge-runtime/lib/config.cjs +39 -0
  49. package/library/actions/mobile/bridge-runtime/lib/issue-capture.cjs +446 -0
  50. package/library/actions/mobile/bridge-runtime/lib/target-discovery.cjs +204 -0
  51. package/library/actions/mobile/bridge-runtime/lib/ws-client.cjs +108 -0
  52. package/library/actions/mobile/bridge-runtime/setup-wallet.sh +442 -0
  53. package/library/actions/mobile/perps/assert_orders.mjs +11 -0
  54. package/library/actions/mobile/perps/assert_positions.mjs +11 -0
  55. package/library/actions/mobile/perps/close_orders.mjs +8 -0
  56. package/library/actions/mobile/perps/close_positions.mjs +8 -0
  57. package/library/actions/mobile/perps/ensure_orders.mjs +4 -0
  58. package/library/actions/mobile/perps/ensure_positions.mjs +4 -0
  59. package/library/actions/mobile/perps/perps.mjs +709 -0
  60. package/library/actions/mobile/perps/place_order.mjs +7 -0
  61. package/library/actions/mobile/perps/read_orders.mjs +4 -0
  62. package/library/actions/mobile/perps/read_positions.mjs +3 -0
  63. package/library/actions/mobile/platform/bridge.mjs +283 -0
  64. package/library/actions/mobile/ui/navigate.mjs +38 -0
  65. package/library/actions/mobile/wallet/ensure_unlocked.mjs +107 -0
  66. package/library/actions/mobile/wallet/home.mjs +35 -0
  67. package/library/actions/mobile/wallet/read_state.mjs +40 -0
  68. package/library/actions/mobile/wallet/select_account.mjs +48 -0
  69. package/library/actions/mobile/wallet/setup.mjs +220 -0
  70. package/library/flows/perps.flows.json +64 -0
  71. package/library/library.json +7 -0
  72. package/library/manifests/core.action-manifest.json +1282 -0
  73. package/library/manifests/extension.action-manifest.json +1749 -0
  74. package/library/manifests/mobile.action-manifest.json +1753 -0
  75. package/library/recipes/action-validation.extension.recipe.json +417 -0
  76. package/library/recipes/action-validation.mobile.recipe.json +422 -0
  77. package/library/recipes/order-lifecycle.core.recipe.json +78 -0
  78. package/library/recipes/perps-lifecycle.recipe.json +194 -0
  79. package/library/recipes/read-markets.core.recipe.json +38 -0
  80. package/library/recipes/smoke.extension.recipe.json +31 -0
  81. package/library/recipes/smoke.mobile.recipe.json +31 -0
  82. package/library/recipes/trading-lifecycle.core.recipe.json +76 -0
  83. package/orchestration/compat-overlays/README.md +19 -0
  84. package/orchestration/compat-overlays/mobile/README.md +13 -0
  85. package/orchestration/compat-overlays/mobile/rn81-message-event-source.patch +42 -0
  86. package/orchestration/core/cleanup.sh +37 -0
  87. package/orchestration/core/inject.sh +154 -0
  88. package/orchestration/doctor.mjs +72 -0
  89. package/orchestration/extension/cleanup.mjs +60 -0
  90. package/orchestration/extension/console-tail.mjs +228 -0
  91. package/orchestration/extension/ensure-browser.sh +416 -0
  92. package/orchestration/extension/ensure-ready.ts +185 -0
  93. package/orchestration/extension/extension-id.ts +107 -0
  94. package/orchestration/extension/inject.mjs +266 -0
  95. package/orchestration/extension/launch-browser.cjs +216 -0
  96. package/orchestration/extension/launch.sh +175 -0
  97. package/orchestration/extension/live.sh +320 -0
  98. package/orchestration/extension/pin-remote-flags.cjs +45 -0
  99. package/orchestration/extension/readiness.mjs +414 -0
  100. package/orchestration/extension/refresh-build.sh +190 -0
  101. package/orchestration/extension/runtime-decision.ts +445 -0
  102. package/orchestration/extension/runtime.ts +407 -0
  103. package/orchestration/extension/seed-fixture.sh +177 -0
  104. package/orchestration/extension/sidepanel-toggle.sh +291 -0
  105. package/orchestration/extension/snapshot-dist.sh +84 -0
  106. package/orchestration/extension/start-watch.sh +339 -0
  107. package/orchestration/extension/wallet-fixture-state.cjs +1086 -0
  108. package/orchestration/lib/activate-repo-node.sh +144 -0
  109. package/orchestration/lib/cli-color.mjs +84 -0
  110. package/orchestration/lib/cli-commands.mjs +243 -0
  111. package/orchestration/lib/cli-home.mjs +354 -0
  112. package/orchestration/lib/cli-ux.sh +252 -0
  113. package/orchestration/lib/cli-version.mjs +123 -0
  114. package/orchestration/lib/ensure-runner-deps.sh +56 -0
  115. package/orchestration/lib/harness-path.sh +55 -0
  116. package/orchestration/lib/hash-helpers.sh +44 -0
  117. package/orchestration/lib/json-field.sh +23 -0
  118. package/orchestration/lib/log-tui.mjs +304 -0
  119. package/orchestration/lib/open-debug.mjs +317 -0
  120. package/orchestration/lib/path-defaults.json +4 -0
  121. package/orchestration/lib/progress.mjs +107 -0
  122. package/orchestration/lib/recipe-paths.mjs +26 -0
  123. package/orchestration/lib/resolve-farmslot-ports.sh +144 -0
  124. package/orchestration/manifest.json +358 -0
  125. package/orchestration/mobile/cleanup.sh +192 -0
  126. package/orchestration/mobile/deps-markers.ts +21 -0
  127. package/orchestration/mobile/inject.sh +681 -0
  128. package/orchestration/mobile/launch.sh +137 -0
  129. package/orchestration/mobile/live.sh +125 -0
  130. package/orchestration/mobile/runtime-decision.ts +292 -0
  131. package/orchestration/porcelain/metamask-recipe +99 -0
  132. package/orchestration/porcelain/mm-recipe +1591 -0
  133. package/orchestration/porcelain/mme-recipe +1181 -0
  134. package/package.json +59 -0
  135. package/runner/extension/verify.sh +511 -0
  136. package/runner/mobile/verify.sh +501 -0
  137. package/runner/src/adapters.ts +601 -0
  138. package/runner/src/cli.ts +1820 -0
  139. package/runner/src/commands/debug.ts +44 -0
  140. package/runner/src/commands/fixtures.ts +99 -0
  141. package/runner/src/commands/launch.ts +397 -0
  142. package/runner/src/commands/logs.ts +60 -0
  143. package/runner/src/commands/shared.ts +138 -0
  144. package/runner/src/completions-cache.ts +86 -0
  145. package/runner/src/doctor.ts +203 -0
  146. package/runner/src/harness.ts +516 -0
  147. package/runner/src/heal-bounds.ts +179 -0
  148. package/runner/src/index.ts +6 -0
  149. package/runner/src/live-adapter-contract.ts +274 -0
  150. package/runner/src/manifest.ts +47 -0
  151. package/runner/src/mm-harness-cli.ts +488 -0
  152. package/runner/src/paths.ts +198 -0
  153. package/runner/src/recording-target.ts +147 -0
  154. package/runner/src/run-recording.ts +329 -0
  155. package/runner/src/runner.ts +108 -0
  156. package/runner/src/types.ts +57 -0
  157. package/scripts/completions.sh +125 -0
  158. package/scripts/install-completions.sh +62 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,161 @@
1
+ # Changelog
2
+
3
+ ## 0.2.0 - 2026-07-03
4
+
5
+ ### Changed
6
+
7
+ - **Docs realigned to the shipped single-bin surface**: `README.md`, `docs/harness-cli.md` (reduced to a pointer at `CLI-SPEC.md`), `docs/recipe-libraries.md`, `docs/extension-runtime-commands.md`, `docs/package-boundaries.md`, and `docs/live-adapter-contract.md` no longer teach the retired `metamask-recipe`/`mm-recipe`/`mme-recipe` names or "planned/stub" daily-loop claims — every command example is `mm-harness`. (`docs/MENTAL-MODEL.md` and `docs/CLI-SPEC.md` retain the old names by design: they are the before→after migration contract.)
8
+ - **Published package trimmed to runtime needs**: the `files` field drops dev-only helpers (`scripts/check.mjs`, `scripts/quality/`, `scripts/link-local-farmslot.mjs`, `scripts/validate-action-e2e-artifacts.mjs`) and repo-dev docs (`AGENTS.md`, `CLAUDE.md`), keeping `bin`, `runner`, `orchestration`, `library`, the runtime completion scripts, `docs`, `README.md`, and `CHANGELOG.md`. The `files` field now also negates `scripts/README.md` (dev-only) so the published tarball no longer ships it alongside the two runtime completion scripts.
9
+ - **Comment-hygiene guard extended**: `scripts/quality/comment-hygiene.mjs` now also fails on presentation vocabulary (`verb`/`daily`) in comments and object-property keys (previously only filenames and declarations), enforced with word boundaries so `verbatim`/`verbose` and user-facing help titles like `DAILY LOOP` stay exempt. Source comments and the `verb: 'rebuild'` progress field were reworded/renamed to `command`.
10
+ - **Single-bin public surface (wave 4)**: `mm-harness` is now the only npm bin. The three legacy bin files (`mm-recipe`, `mme-recipe`, `metamask-recipe`) are internalized to `orchestration/porcelain/` — they remain fully functional as implementation details (exec'd by `mm-harness launch/logs/debug/fixtures` and the inject-script delegates) but are no longer installed into `$PATH` as separate executables. Package renamed from `@deeeed/metamask-recipe-runner` to `@deeeed/metamask-harness` (effective on next publish). The `harness` top-level subcommand form is removed from the CLI; overlay verbs (`install`/`verify`/`cleanup`) are addressed directly (`mm-harness install`, `mm-harness verify`, `mm-harness cleanup`). Shell completions updated to register `mm-harness`. The `completions/_recipe` zsh completion file is removed (dynamic `mm-harness` completions shipped in wave 5).
11
+
12
+ ### Fixed
13
+
14
+ - **`flows --json --target <dir>` no longer errors**: the bare-`flows` default-to-`list` check inspected every token, so a flag value (the `--target` argument) was mistaken for a subcommand and the args were mangled (`unknown option '--json'`). It now inspects only the first positional token against the engine's real subcommand set (`list`/`promote`) and drops the runner-level `--target` (flows resolution is adapter-global), so `flows`, `flows --json`, `flows --json --target <dir>`, and `flows promote --help` all behave.
15
+ - **`run --json` / `call --json` emit parseable JSON on stdout**: the engine's library-resolution log (`Recipe libraries: …`, `logger.info` → stdout) polluted stdout ahead of the JSON payload. In `--json` mode the engine logger is now routed to stderr, so stdout is machine-only for both success and failure runs.
16
+ - **`run --json` (non-plan) now honors the validates-first contract and the mm-harness envelope**: (1) it pre-validates adapter-aware through the SAME static path `run --plan` uses, so an invalid recipe exits 5 with a structured `RECIPE_VALIDATION_FAILED` error on parseable stdout (was exit 1 with the engine's validation text mixed into output) — and, because pre-validation reads no libraries, the `Recipe libraries: …` log never reaches stdout on the validation-failure path; (2) success now emits the envelope (`schemaVersion`, `command`, `adapter`, `status`, `exitCode`, `recovered[]`, `mutations[]`) with the engine result nested under `result`, matching `launch`/`verify`/`doctor` (was the raw engine result for `core` / a flattened result for device adapters). The recipe-running bound (exit 4) still fires before the recipe is read.
17
+ - **`flows` help no longer advertises `--adapter`**: flow resolution is adapter-global, and the engine rejects `--adapter` on `flows` (`unknown option`). `flows --help` dropped the flag from its accepted-options list rather than presenting a flag that errors.
18
+ - **`doctor --fix` is implemented**: the parser now knows `--fix` is boolean (parses with `--json`/`--adapter` in any order). `--fix` runs the shared self-healing steps WITHOUT launching the app — auto-ensures the runtime overlay if missing (same bounds; never a fixture reseed) — and reports `fixed[]` / `failed[]` (`--json` adds both arrays). Exit 0 = all healed, 1 = partial/failed. Read-only `doctor` is unchanged.
19
+ - **`mm-harness` help text matches shipped behavior**: `doctor`'s summary and `doctor --help` now list `--fix` (with a one-line description + `mm-harness doctor --fix --json` example) instead of saying "`--fix` coming"; `flows`'s summary and `flows --help` now describe the current multi-source resolution (`personal > team > canonical`, highest tier wins, `--library <name=path>`) instead of the stale "requires @farmslot/recipe-harness >= 0.3.3 — pending publish" note.
20
+ - **App-logic failures surface the original output verbatim**: `launch`, `run`, and `call` now carry the original failure text in `error.originalError` (`--json`) and in human output; the classification note accompanies it, never replaces it. When a recovery bound fires, `error.code` names the exact bound (`APP_LOGIC_FAILURE`, `WALLET_STATE_REQUIRED`, `RECIPE_RUNNING`, `SAME_RECOVERY_TWICE`) instead of a generic `LAUNCH_FAILED`.
21
+ - **`launch --sidepanel` selects the display mode**: it composes `mme-recipe sidepanel open` after the base rebuild (grounded in `orchestration/extension/sidepanel-toggle.sh`), so `--sidepanel` and `--fullscreen` compose different porcelain calls and the mode reaches orchestration instead of being metadata-only.
22
+ - **`run`/`call` route failures through the shared heal bounds**: app-logic failures surface verbatim with no healing (exit 1); infra failures get one bounded recovery re-run, then the same-recovery-twice bound refuses a loop (exit 4).
23
+ - **`call <short-name>` resolves a name that uniquely matches a substring of an action's final segment**: the flagship `mm-harness call unlock` example returned `ACTION_UNKNOWN` because fuzzy resolution only matched exact trailing segments — `unlock` never matched `metamask.wallet.ensure_unlocked`. Resolution is now tiered (exact full name → exact final segment → unique substring of a final segment); an exact match always beats a substring, a unique substring resolves, and an ambiguous fragment still exits 2 listing candidates.
24
+ - **Extension launch no longer dies before the browser on a fresh checkout**: `orchestration/porcelain/mme-recipe` `rebuild` truncated `<runtime-dir>/rebuild.log` without creating the directory, so a checkout without the runtime dir failed with "No such file or directory" under `set -euo pipefail`. The log write now `mkdir -p`s its parent, matching the other log writers in the file.
25
+ - **Mobile launch no longer crashes or loops on a bare checkout**: `mm-recipe ios|android` on a checkout with no slot suffix left `WATCHER_PORT` unbound, crashing at Metro startup under `set -u`; `init_context` now defaults it to Metro's well-known `8081` (farm dispatch still sets its slot-derived port). Launching without installed dependencies now fails fast with a teaching error ("dependencies not installed — run yarn install / yarn setup first") instead of a Metro-restart retry loop, and the launch path honors a failed Metro start rather than hanging in bundle prewarm.
26
+ - **Standalone extension launch cannot attach to or kill a browser it did not launch (SAFETY)**: `orchestration/extension/launch-browser.cjs` previously terminated whatever process held the requested `--cdp-port` — including a personal Chrome — and could target a shared/default profile. It now (a) refuses a CDP port unless every listener provably loads our `--user-data-dir` (else a teaching error, never a kill/attach), and (b) refuses a shared (`~/.chrome-farmslot`) or OS-default browser profile, so standalone launches stay on a per-checkout isolated user-data-dir. Farm dispatch's runtime-context profile/port injection is unaffected.
27
+
28
+ ### Changed
29
+
30
+ - **CLI commands organized by domain**: the `launch`/`logs`/`debug`/`fixtures` handlers moved to `runner/src/commands/*.ts` and the shared healing/bounds policy to `runner/src/heal-bounds.ts` (was `runner/src/daily-verbs.ts`). Added a `Code quality rules` section to `AGENTS.md` and a `scripts/quality/comment-hygiene.mjs` guard (wired into `yarn check`) that fails on development-history comments and presentation-vocabulary identifiers in `runner/src`.
31
+
32
+ ### Added
33
+
34
+ - **`doctor --json` reports the runtime-context section**: `doctor` now emits a `runtimeContext` object (and a readable section in human mode) so the slot's runtime context is inspectable without hunting for files. It reads `agentic-runtime.json` (`RECIPE_RUNTIME_CONTEXT` override, else `<target>/temp/recipe/runtime/agentic-runtime.json`) via the harness's own reader — no duplicate parser — and lists every field (`slotId`, `extensionId`, `cdpPort`, `runtimeStart.approved`/`.command`/`.readyUrl`) with its current `value`, `source` (`env` override > `file` > `default`), the `envVar` that overrides it, and a one-line `customize` hint. When the file is absent the section is still present with `fileExists:false` and the path where it WOULD live (written by farmslot prepare/dispatch), and every field falls back to `env`/`default`. Grounded in `runner/src/doctor.ts` `runtimeContextSummary()`, contract-tested in `tests/contract/mm-harness-final-review.test.sh`.
35
+ - **Bundled dynamic shell completions — SHIPPED (refactor wave 5)**: `mm-harness` ships zsh + bash tab-completion in the npm package (`scripts/completions.sh`), installed via `mm-harness completions install` (delegates to `scripts/install-completions.sh`, which prints the `source` line to add to `~/.zshrc` / `~/.bashrc`; the farm installer companion can auto-source it later — not touched here). Completion is **dynamic**: commands, the `launch` positional (`ios|android`), `fixtures` (`sync|set`), and per-command flags are static from the surface, while **action names for `call`** come live from `mm-harness completion-candidates actions` and **flow refs for `run`** from `mm-harness completion-candidates flows` (both cache-backed per-checkout, silent static fallback on miss — never blocks the shell). The rewritten `scripts/completions.sh` targets `mm-harness` (not the retired `recipe`/`mm-recipe`/`mme-recipe` names); the legacy `completions/_recipe` was already removed in wave 4a.
36
+ - **`completion-candidates flows` — REAL (refactor wave 5)**: now enumerates the resolved recipe-library flows via `loadRecipeLibraries` on `@farmslot/recipe-harness >= 0.3.3` (was degraded to empty in wave 2). Flow-ref candidates are adapter-global, so they resolve even outside a checkout / without `--adapter`.
37
+ - **Env-gap flags — SHIPPED (refactor wave 5, CLI-SPEC Part 4)**: every remaining env var a human was told to type now has a flag with resolution order **flag > env > config/default** (the flag sets the env var the porcelain reads, so it wins; an absent flag leaves the agent/CI env untouched). `launch --device` (`IOS_SIMULATOR` for ios · `ADB_SERIAL`/`ANDROID_SERIAL`/`ANDROID_DEVICE` for android), `launch --cdp-port` (`CDP_PORT`/`RECIPE_CDP_PORT`), `launch --watcher-port` (`WATCHER_PORT`/`METRO_PORT`/`RECIPE_WATCHER_PORT`) — ports are numeric (teaching exit 2 otherwise); `logs --events` (`RECIPE_LOG_EVENTS`); `fixtures set --fixture` now resolves flag > `RECIPE_WALLET_FIXTURE` > canonical default on both adapters. Each is documented in `--help`. Precedence is contract-tested.
38
+ - **Contract coverage (wave 5)**: `tests/contract/mm-harness-wave5.test.sh` proves the completion scripts are syntactically valid (`bash -n` / `zsh -n`), target `mm-harness` + source the dynamic candidates, that `completion-candidates` return real data (the `call unl<TAB>` demo: candidates output contains `unlock`; flow refs contain `perps.clean_market_testnet`), and the env-gap flag precedence (flag beats env) for `launch --device`/`--cdp-port`/`--watcher-port`, `logs --events`, and `fixtures set --fixture`.
39
+ - **DAILY LOOP verbs — REAL (refactor wave 3)**: `launch`, `logs`, `debug`, and `fixtures sync|set` are no longer teaching stubs — they are real commands (`runner/src/commands/`). The typed layer owns POLICY, HEALING, TEACHING, and the `--json` contract; it does NOT rewrite orchestration — each verb COMPOSES the same porcelain paths `mm-recipe` / `mme-recipe` use today (grounded in their case arms), and `launch --verify` composes the overlay `live` sequence via `handleHarness`. Composition seams `MM_HARNESS_MM_RECIPE_BIN` / `MM_HARNESS_MME_RECIPE_BIN` / `MM_HARNESS_INSTALL_BIN` make every path contract-testable without a device.
40
+ - **`launch [ios|android]`** — flagship. Mobile: target MANDATORY (missing → teaching exit 2); extension: no target (`--fullscreen` default, `--sidepanel`); core: teaching error (headless). Tiers: default quick relaunch (`mm-recipe <ios|android>` / `mme-recipe rebuild`), `--build` (`mm-recipe up` / `mme-recipe build`), `--watch` (extension webpack watcher), `--verify` (absorbs the retired `live` — overlay ensure → launch → CDP/bridge poll → smoke verify). `--runway` is mobile-only (teaching error elsewhere). Quick-launch **change detection**: native inputs changed vs the recorded build baseline (git diff on `ios/`,`android/`,`package.json`,`yarn.lock` / webpack config) → refuse quick with a teaching error pointing at `--build`.
41
+ - **`logs`** — composes the porcelain `logs` (+ `--full`, `--source metro|app`); teaching exit 2 when nothing is running (no `metro.log`/`webpack.log`), pointing at `launch`.
42
+ - **`debug`** — extension `debug` (`--worker` → `debug worker`), mobile `debug` (`--dev-menu` → `dev-menu`); cross-platform flag misuse teaches (exit 2).
43
+ - **`fixtures sync|set`** — wallet DATA only. `sync` composes the porcelain `sync`; `set` composes `mm-recipe setup-wallet <fixture>` (mobile) or reuses `call`'s one-node engine path `metamask.wallet.setup` (extension GAP); the password is read FROM the fixture, and `set` always prints the fixture edit path. `--fixture <path>` agent override.
44
+ - **`--heal=off|infra-only|auto` (refactor wave 3)**: implemented on `launch` (default `auto`) and `run`/`call` (default `infra-only`). Auto-ensures the runtime overlay when missing (inline install, first-install notice on stderr in human mode / `mutations[]` in `--json`; `--heal=off` disables auto-install). `launch` runtime healing recovers infra transport failures (Metro/CDP) by re-running the composed launch **once** — bounds are ALWAYS enforced regardless of `--heal`: refuse when a recipe is running (`recipe.lock`/`MM_HARNESS_RECIPE_RUNNING`), when the same recovery already failed once this invocation, and — the WALLET RULE — healing NEVER touches fixtures: a recovery that would need a seeded wallet stops with `recoverable:false` + `userAction: "run mm-harness fixtures set"`. `--json` gains `recovered[]` (stable codes: `metro.restarted`/`chrome.reopened`…), `mutations[]`, `phase`, and `recoverable`/`attemptedRecoveries[]` on failure.
45
+ - **Exit-code taxonomy — complete (wave 3)**: `3` (infra failure not auto-healed — `--heal=off` or a bound hit) and `4` (bounded / not-retryable — same recovery failed, recipe running, wallet write needed) now land on `launch` alongside `0`/`1`/`2`/`5`, per CLI-SPEC Part 5 §5.6.
46
+ - **Contract coverage (wave 3)**: `tests/contract/mm-harness-wave3.test.sh` proves launch grammar (mobile-target mandatory, core refusal, `--runway` on extension → exit 2), tier selection incl. change-detection refusal, heal bounds (same-recovery-twice, wallet-rule with `recoverable:false`, recipe-running, `--heal=off` → exit 3), auto-ensure notice + `mutations[]`, and logs/debug/fixtures happy + teaching paths — all via the stub-recorder seams. `tests/contract/mm-harness-taxonomy.test.sh` updated: the DAILY LOOP verbs are now REAL, not planned stubs.
47
+ - **`mm-harness call <action>` — REAL (refactor wave 2)**: `call` executes ONE action from the adapter vocabulary AS a single-node recipe through the **real engine path** — the same `runRecipe` path `run` uses, so it writes the same `summary.json` / `trace.json` / `artifact-manifest.json` evidence (one execution path, two doors: `call` = one node, `run` = a graph). Fuzzy short-name resolution (`unlock` → `metamask.wallet.unlock`; ambiguous → exit 2 listing candidates; unknown → exit 2 pointing at `mm-harness actions`), repeatable `--arg k=v` mapped to the action's node fields, adapter auto-detect from `--target`/cwd, and a `--json` shape (`{ command, adapter, action, resolvedAction, args, status, summaryPath, tracePath, artifactManifestPath, exitCode }`). Always-validates its one-node recipe adapter-aware first (exit 5 on validation failure). `handleCall` in `runner/src/cli.ts`; promoted from teaching stub to a real DISCOVER verb in `mm-harness --help`.
48
+ - **`mm-harness run --plan` — REAL (refactor wave 2)**: plan-only mode validates the recipe adapter-aware (`validateRecipeDocument` schema + `validateRecipeWithManifest` action existence/platform vs the adapter manifest) and prints the plan **without touching any device or writing artifacts**. `--json` = `{ status, adapter, recipe, findings[], plan[] }`; each `plan[]` item carries `confidence: static|conditional` (static = schema/action-existence/manifest/fixture-FILE/resolved-path checks; conditional = runtime intent like "would auto-ensure the overlay / launch the app / execute N nodes"). Exit 0 = plan valid · 5 = validation failure · 2 = recipe missing/unparseable. Shared helper `validateRecipeAdapterAware` (reused by `call`). Grounded on the pinned `@farmslot/protocol`; recipe library `call.ref` external-flow-id resolution + the `run` execute path's always-validate extend when `@farmslot/recipe-harness >= 0.3.3` publishes (tagged in code + `docs/CLI-SPEC.md`).
49
+ - **Exit-code taxonomy (started, wave 2)**: `run` / `run --plan` / `call` follow the documented taxonomy (CLI-SPEC Part 5 §5.6): `0` ok · `1` recipe/runtime fail · `2` usage/bad args · `5` validation failure (`3`/`4` infra/bounded land with the wave-3 healing path).
50
+ - **Dynamic completions cache skeleton (wave 2)**: `runner/src/completions-cache.ts` defines the per-checkout cache file (`{runtimeDir}/.completion-cache.json`: `version`, `updatedAt`, `candidates`), a short TTL, and invalidation on `mm-harness install`. A hidden `mm-harness completion-candidates <actions|flows>` command (sourced by the wave-5 shell completion scripts) returns cache-backed manifest action names for `actions`; `flows` degrades to empty until `@farmslot/recipe-harness >= 0.3.3` exposes the recipe-library API. New contract coverage in `tests/contract/mm-harness-wave2.test.sh`.
51
+ - **`mm-harness` single front-door CLI (refactor wave 1)**: `bin/mm-harness` is now a real bin (same node/tsx resolution as `bin/metamask-recipe`, registered in `package.json`) backed by a **commander**-based entry (`runner/src/mm-harness-cli.ts`) — the same CLI library `@farmslot/recipe-harness` uses. It presents only the validated end-state surface with grouped `--help` (DAILY LOOP / DISCOVER / PROVE / RUNTIME OVERLAY), one purpose line + one copy-pasteable example per verb. Real verbs route to the existing dispatch in `runner/src/cli.ts` (no logic forked): `actions` (+ new `--raw` flag that dumps the raw action manifest, replacing the retired `manifest` verb), `flows` (bare = list; `flows promote`), `run`, `doctor`, and top-level `install`/`verify`/`cleanup` (byte-identical to `metamask-recipe harness <verb>`, reusing `runner/src/harness.ts`). Planned verbs (`launch`, `logs`, `debug`, `fixtures sync|set`) were honest teaching stubs at this point (promoted to real in wave 3); `call <action>` was a stub (promoted to real in wave 2). Retired names (`manifest`, `live`, `runtime-*`, `self-test`) are not on this surface; they stay reachable via the untouched legacy bins until the caller-sweep wave. `cli.ts` gains an exported, guarded `main` so the commander front reuses it in-process. New contract coverage in `tests/contract/mm-harness-taxonomy.test.sh` (grouped help, stub teaching, overlay dispatch parity, `actions --raw`, `flows` routing parity). Full contract: `docs/MENTAL-MODEL.md` + `docs/CLI-SPEC.md`.
52
+ - **`metamask-recipe harness <install|verify|cleanup|live>` command**: the recipe-harness lifecycle now lives in the runner as typed, tested TypeScript (`runner/src/harness.ts`) instead of the skill's ~670 lines of bash. It resolves the runner root (the runner itself), auto-detects the adapter from `--target` when `--platform`/`--adapter` is omitted, and dispatches to the correct orchestration file per subcommand/platform with identical behavior to the skill: `install`→`orchestration/<platform>/inject.{sh,mjs}`, `cleanup`→`inject`'s cleanup counterpart (preferring an installed overlay's `.runner-source`), `verify`/`live` for mobile/extension→installed `scripts/<action>.sh` else `orchestration|runner|scripts/<platform>/<action>.sh`, and `core verify`→the installed core delegate's `doctor`. Preserves the extension runtime-context env hydration (`agentic-runtime.json` → `--cdp-port`/`--prepare-cmd` and `RECIPE_*` exports) and forwards remaining args verbatim (use `--` for passthrough). Human-friendly by default: `--help` teaches every verb with a copy-pasteable example, the platform is auto-detected from the checkout (flags are overrides only, so bare `metamask-recipe harness verify` works inside a repo), progress and `✓`/`✗` status print to stderr while the orchestration script's stdout/artifacts stay byte-identical to the skill path, failures print the cause plus the exact next command, and `--json` emits a single machine-readable summary for agents. Lets the recipe-harness skill thin to a caller. New contract test `tests/contract/harness-cli.test.sh`.
53
+ - **Canonical recipe library**: `library/library.json` + `library/flows/perps.flows.json` seed the runner's repo-native flow source with `perps.clean_market_testnet` (wallet unlock + Perps start-state convergence as one `call`-able flow). `metamask-recipe run` resolves flows from ordered library sources — developer `--library` entries or `RECIPE_LIBRARY_PATH`, then the personal library at `<farmslot home>/recipe-library`, then this runner's `library/` last, so personal/team flows win by default and shadowing is reported in run output and `summary.json`. New `metamask-recipe flows list|promote` delegates to the harness flows CLI with the same source chain. Requires a `@farmslot/recipe-harness` with recipe-library support; older harnesses keep working (run ignores libraries, `flows`/`--library` explain the upgrade). Installed harnesses snapshot `runner/flows/` + `runner/library.json`; `scripts/check.mjs` validates committed flow catalogs against the action manifests.
54
+ - **Architecture TL;DR**: `docs/architecture.md` gains a top-of-doc summary answering the four recurring questions — scope, runner-owns-logic/skills-stay-thin responsibility split (with the in-flight `recipe-harness` thinning as the concrete example), the bash/ts/mjs/cjs language policy, and why this stays one package. No behavior change.
55
+
56
+ ## 0.1.9 - 2026-07-01
57
+
58
+ ### Fixed
59
+
60
+ - **Node 24 type-stripping under `node_modules`**: `bin/metamask-recipe` detected Node source-TypeScript support by running a probe `.ts` in `/tmp`, which passes on Node 24. But Node refuses type-stripping for files under `node_modules` (`ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING`), so when the runner is installed as a dependency the probe was a false positive and `node runner/src/cli.ts` failed (broke extension preflight). The check now skips the node-source path when `RUNNER_DIR` is under `node_modules`, falling through to `dist/cli.js` or `tsx`.
61
+
62
+ ## 0.1.8 - 2026-06-30
63
+
64
+ ### Fixed
65
+
66
+ - **Runner dependency bootstrap**: new `orchestration/lib/ensure-runner-deps.sh` installs `@farmslot/recipe-harness` when a cloned runner checkout has no `node_modules` (uses `npm install`, not `yarn`, so parent product `.yarnrc.yml` settings such as `approvedGitRepositories` do not block install). Invoked from `bin/metamask-recipe`, all adapter `inject` paths, and optional local `link-local-farmslot` when `FARMSLOT_ROOT` is set.
67
+ - **Extension Node pin in tmux/nohup**: `activate-repo-node.sh` now prefers `.tool-versions`, tries `.nvmrc` major.minor candidates (e.g. `24.13` → `24.13.0`), and is installed into the harness overlay. `start-watch.sh` / `refresh-build.sh` refuse to run `yarn start` when Node cannot be pinned (prevents Homebrew Node 26 from breaking webpack).
68
+ - **Farmslot CDP port alignment**: new `resolve-farmslot-ports.sh` reads pool JSON by repo path so `mme-recipe` / `recipe sync` use the same `cdp_port` as slot-view shortcuts (e.g. mme-6 → 7666). `mme-recipe sync` rewrites `agentic-runtime.json` after harness refresh.
69
+ - **`recipe build` semantics**: `mme-recipe build` runs a full rebuild when no watcher is active; `refresh` stays fast-path only.
70
+ - **Runtime-launch CLI**: removed log-regex failure classification from `cli.ts`; failures point at `launch.log` / summary artifacts instead of mislabeling (e.g. webpack errors as `wallet_fixture_not_ready`).
71
+
72
+ ### Changed
73
+
74
+ - **Mobile AgenticService path**: overlay and inject/cleanup/verify paths now target `app/dev-tools/AgenticService` (was `app/core/AgenticService`), matching metamask-mobile's dev-tools relocation. Navigation/App patches use dynamic `import('../../dev-tools/...')` and `dev-tools/AgenticService/AgentStepHud` imports.
75
+ - **Step HUD bus decoupling**: `emitStepHud` / `registerStepHudCallback` live in `AgentStepHud.tsx.patch` so `AgenticService` no longer exports the HUD registry; the bridge stays off the static import graph for release dead-code elimination. `AgenticService.install` is guarded with `if (!__DEV__) return`.
76
+ - **Mobile inject idempotency**: skip copying overlay files when checkout content already matches (`cmp` before `cp`); Navigation/App patches no-op when already present (avoids spurious git diffs on re-install).
77
+ - **Mobile harness upgrade path**: re-inject upgrades legacy `app/core/AgenticService` Navigation/App imports to `app/dev-tools/AgenticService` when the old harness patch is already present.
78
+
79
+ ## 0.1.7 - 2026-06-28
80
+
81
+ ### Fixed
82
+
83
+ - **Orchestration doctor drift**: list `extension/console-tail` in `orchestration/manifest.json`. The live console streamer added in 0.1.6 was unlisted, so `orchestration/doctor.mjs` (and the `orchestration-doctor` contract test) failed the unlisted-script drift guard.
84
+ - **Mobile step HUD hidden behind native modals**: the injected `AgentStepHud` overlay (`library/actions/mobile/app-overlay/.../AgentStepHud.tsx.patch`) now wraps its view in `react-native-screens` `FullWindowOverlay`, so the dev step HUD paints in a UIWindow above native-stack modal screens (perps close-position / TPSL) instead of being occluded. Kept byte-identical to metamask-mobile `main`; a contract-test drift-guard (`tests/contract/mobile-inject.test.sh`) now asserts the overlay retains the `FullWindowOverlay` wrap.
85
+
86
+ ## 0.1.6 - 2026-06-27
87
+
88
+ ### Added
89
+
90
+ - **Live extension console tab** (`orchestration/extension/console-tail.mjs`): standalone CDP streamer that tails the running extension's service worker **and** UI-page console (`Runtime.consoleAPICalled` / `exceptionThrown` / `Log.entryAdded`), re-attaching across MV3 service-worker churn. Auto-opened as a `console-<cdp>` tmux window on the interactive bringup (`mme-recipe up` / `rebuild`) and the live/proof flow (`extension/launch.sh`); mirrored to `temp/recipe/runtime/extension-console.log`. Silently skipped when there is no tmux session or CDP (headless/CI).
91
+
92
+ ### Fixed
93
+
94
+ - **Blank dev-server tmux windows**: Metro (`bin/mm-recipe`), webpack `recipe watch` (`bin/mme-recipe`), and the harness webpack build (`orchestration/extension/start-watch.sh`) hosted the bundler in the tmux window with stdout/stderr redirected to a log file, leaving the pane empty. The bundler now runs detached and the window is a read-only `tail -F` viewer of the same log. Process/pid stop lifecycle is preserved: `mm-recipe stop` closes the Metro viewer (via a `metro.tmux` marker) and `mme-recipe stop` closes the webpack viewer. The console log is truncated per run (parity with the Metro/webpack logs), and a dead/non-debuggable CDP target is backed off instead of re-attached every poll.
95
+
96
+ ## 0.1.5 - 2026-06-26
97
+
98
+ ### Added
99
+
100
+ - **Mobile runtime-decision** (`orchestration/mobile/runtime-decision.ts`): deps + Metro bundle gate for `mm-recipe up`, parity with extension `runtime-decision`. Decisions: `install` (missing/stale deps or unresolved modules), `launch` (Metro down, cold bundle, or stale log with deps satisfied), `ready` (deps current + successful bundle + Metro up).
101
+ - **`orchestration/mobile/deps-markers.ts`**: MetaMask Mobile product/native marker tables consumed by harness `deps-readiness`.
102
+ - **`mm-recipe decision`**: expose mobile runtime-decision for dry inspection.
103
+ - **CI** `.github/workflows/check.yml`: `yarn check`, contract tests (`tests/contract/run.sh`), and shell syntax on every PR/push to `main`.
104
+ - **Contract tests** `tests/contract/mobile-runtime-decision.test.sh`, `tests/contract/mobile-mm-recipe-up-install.test.sh`.
105
+
106
+ ### Changed
107
+
108
+ - **Runtime-readiness ownership**: generic deps/log/metro primitives import from `@farmslot/recipe-harness/runtime/*` (**^0.3.0**). MetaMask keeps adapter-specific probes (`deps-markers`, Nitro staleness, webpack/CDP) and shell launch only. Publish harness before bumping the dependency — CI installs from npm, not `FARMSLOT_ROOT`.
109
+ - **Extension runtime-decision**: `depsCheck` / `recordDepsBaseline` import from `@farmslot/recipe-harness/runtime/deps-readiness` (no local copy).
110
+ - **`mm-recipe up`**: runs runtime-decision first (`install` → `yarn setup`, `launch` → Metro + dev client, `ready` → bridge wait). `prewarm_bundle` failures now propagate.
111
+ - **Stale Metro log recovery**: when cited unresolved modules are already in `node_modules`, relaunch with cleared Metro cache instead of re-running `yarn setup` in a loop. Stop Metro after `yarn setup`; honor `MOBILE_METRO_CLEAR` even when Metro is already listening.
112
+ - **Native module staleness**: detect `[runtime not ready]` Nitro HybridObject registration failures in `metro.log` (e.g. `NitroFetch` missing from the installed dev client) and run `recipe up` with `--preflight-mode rebuild-native` instead of fast relaunch.
113
+
114
+ ### Fixed
115
+
116
+ - **`runtime-decision --record-baseline`**: `--record` on `run` remains the video alias; baseline recording uses `--record-baseline` (or `--record` only on `runtime-decision`).
117
+ - **`mm-recipe up` / `mme-recipe up` install loop**: one `yarn setup` / `yarn install` per invocation; fail if decision stays `install`. Record deps baseline after successful install and after bridge-ready.
118
+ - **`yarn setup` failures** now propagate from `run_mobile_install`.
119
+ - **Stale Metro unresolved errors**: ignore `Unable to resolve` lines superseded by a later successful bundle (`lastOk > lastErr`).
120
+ - **Android `rebuild-native`**: `launch_android` honors `MOBILE_PREFLIGHT_MODE` via `ensure_android_app_for_mode` (parity with iOS).
121
+ - **Stale native-module log lines**: ignore Nitro HybridObject errors superseded by a later successful bundle.
122
+ - **`mm-recipe up --json`**: emit a single final decision JSON document after the invocation completes (suppress interim install/launch reports).
123
+ - **Unresolved-module log scoping**: only cite `Unable to resolve` lines after the last successful bundle when classifying current failures.
124
+ - **`moduleExistsInNodeModules`**: require `package.json` in the resolved package root (ignore empty `node_modules` dirs).
125
+ - **Persistent bundle errors**: repeated identical Metro bundle failures surface `blocked` / `bundle-error-persistent` instead of relaunch loops.
126
+ - **`PLATFORM` inference**: `yarn setup` defaults from Android adb env / `RECIPE_HARNESS_PLATFORM`, not hardcoded `ios`.
127
+ - **Optional native markers**: when `PLATFORM` is `ios` or `android`, check `ios/Podfile.lock` or `android/gradle.properties` for partial deps.
128
+
129
+ ### Removed
130
+
131
+ - **`orchestration/lib/deps-readiness.ts`**: moved upstream to `@farmslot/recipe-harness/runtime/deps-readiness` (0.3.0).
132
+
133
+ ### Documentation
134
+
135
+ - **README**, **CLAUDE.md**, **AGENTS.md**: runtime-readiness ownership boundary (harness vs MetaMask adapter) and changelog/CI expectations for agents.
136
+
137
+ ## 0.1.4 - 2026-06-24
138
+
139
+ - Add `orchestration/lib/activate-repo-node.sh`: manager-agnostic Node pin from `.nvmrc` / `.tool-versions` (asdf, fnm, mise, nvm, then matching PATH) for non-interactive webpack spawns.
140
+ - Wire `start-watch.sh` and `refresh-build.sh` to activate the repo-declared Node before `yarn start`, including tmux windows where global PATH can shadow version managers.
141
+ - Register `activate-repo-node` and `pin-remote-flags` in `orchestration/manifest.json` so `doctor.mjs` tracks the full extension surface.
142
+
143
+ ## 0.1.3 - 2026-06-13
144
+
145
+ - Add the headless `core` adapter for MetaMask core e2e (HyperLiquid perps testnet, gated mainnet support): `core` action manifest, live adapter, core recipes, and recipe-harness install/cleanup scripts. Mainnet writes require both `node.network: "mainnet"` and `CORE_PERPS_ALLOW_MAINNET_WRITES=1`.
146
+ - Improve handling of stale Metro listeners and session management.
147
+ - Clarify runner/runtime boundaries and centralize runtime helpers in the runner.
148
+ - Note: the 0.1.2 npm artifact was packed before the core adapter landed and lacks `manifests/core.action-manifest.json`; 0.1.3 republishes current main.
149
+
150
+ ## 0.1.2 - 2026-06-10
151
+
152
+ - Declare `ui.key_press` for Mobile and Extension recipe manifests so trusted keyboard input recipes validate against the runner action manifest.
153
+
154
+ ## 0.1.1 - 2026-06-06
155
+
156
+ - Harden harness setup so fallback installs are more reliable when the local skill installer is unavailable.
157
+ - Configure npm scope/cache settings for reproducible package installs and publishes.
158
+ - Prepare pilot npm distribution as `@deeeed/metamask-recipe-runner`; intended to migrate to org ownership if ADR-58 is accepted.
159
+ - Add `mm-recipe` and `mme-recipe` human-friendly wrappers for Mobile and Extension recipe control.
160
+ - Keep `metamask-recipe` as the single package bin; `mm-recipe` and `mme-recipe` are repo/local convenience wrappers.
161
+ - Improve Extension Perps order placement by resolving market price from background market data, stream cache, or visible UI before submitting.
package/README.md ADDED
@@ -0,0 +1,140 @@
1
+ # MetaMask Recipe Runner — `mm-harness`
2
+
3
+ **`mm-harness` is built for the agent but easy for the human to use and
4
+ understand.** It is the MetaMask recipe harness: one front door to launch the app,
5
+ prove behavior with recipes, and manage the per-checkout runtime overlay. It is a
6
+ thin MetaMask adapter over the generic `@farmslot/recipe-harness` engine — the
7
+ runner never re-implements graph execution, it binds to it. The agent gets full
8
+ depth (explicit flags, `--json`, ports, build tiers); the human gets short
9
+ commands, positional targets, good defaults, completions, and teaching
10
+ help/errors. Run it from inside a MetaMask Mobile/Extension/Core checkout and the
11
+ platform is auto-detected.
12
+
13
+ ```
14
+ DAILY LOOP launch · logs · debug · fixtures what a teammate runs many times a day
15
+ DISCOVER actions · call · flows compose recipes from the vocabulary + flow library
16
+ PROVE run · doctor run recipes, check readiness
17
+ RUNTIME OVERLAY install · verify · cleanup manage the per-checkout runtime overlay
18
+ ```
19
+
20
+ **One bin, no per-platform binaries.** `mm-harness` is the only command. Grammar:
21
+ `mm-harness <command> [target] [flags]` — the human types the bare command
22
+ (platform auto-detected) or a positional target (`mm-harness launch ios`); depth
23
+ lives in FLAGS with good defaults, never in extra command names. It ships shell
24
+ completions (zsh + bash) for commands, targets, and flags.
25
+
26
+ Naming: **"harness" is the tool** (`mm-harness`) and the generic engine package
27
+ (`@farmslot/recipe-harness`); the files it installs into a checkout are the
28
+ **runtime overlay**. Read [MENTAL-MODEL.md](docs/MENTAL-MODEL.md) first (the
29
+ before→after map), then [CLI-SPEC.md](docs/CLI-SPEC.md) for the full contract
30
+ ([harness-cli.md](docs/harness-cli.md) is a short pointer to it).
31
+
32
+ One package, two responsibilities:
33
+
34
+ - **Recipe layer:** manifests, recipes, Mobile/Extension adapters, proof output.
35
+ - **Runtime layer:** start/reuse Metro or Chrome, seed fixtures, wait for readiness.
36
+
37
+ Farmslot and skills are wrappers around this CLI; they should not copy runner
38
+ logic.
39
+
40
+ ## Quick start
41
+
42
+ Run from inside a MetaMask checkout (platform auto-detected):
43
+
44
+ ```bash
45
+ # 1) Launch the app (installs the runtime overlay on first use, boots Metro/build)
46
+ mm-harness launch ios # mobile: ios | android; extension: just `launch`
47
+
48
+ # 2) Run a recipe and write evidence
49
+ mm-harness run recipe.json --adapter mobile --artifacts-dir /tmp/recipe-artifacts --json
50
+ ```
51
+
52
+ Outputs: `summary.json`, `trace.json`, screenshots, logs, and an artifact
53
+ manifest.
54
+
55
+ ## Mental model
56
+
57
+ ```text
58
+ mm-harness launch # runtime/orchestration: the app is ready
59
+ mm-harness run # runner/proof: actions execute and evidence is saved
60
+ ```
61
+
62
+ Do not mix those layers. library/ = what recipes ARE (recipes, actions,
63
+ manifests — the content). runner/ = what EXECUTES them (engine, verify,
64
+ evidence). orchestration/ = what CONTROLS the app and its instances (start,
65
+ windows, wallet state, health, ports, parallel).
66
+
67
+ ## Useful commands
68
+
69
+ ```bash
70
+ # Capabilities (compose recipes from the vocabulary + flow library)
71
+ mm-harness actions --adapter mobile --json # the action vocabulary
72
+ mm-harness actions --adapter mobile --raw # raw action-manifest dump
73
+ mm-harness call unlock --adapter extension # run one action via the real engine path
74
+ mm-harness flows --json # reusable library flows, with provenance
75
+
76
+ # Readiness + proof
77
+ mm-harness doctor # read-only readiness check (no launch)
78
+ mm-harness doctor --fix # heal the overlay without launching
79
+ mm-harness run recipe.json --plan --adapter mobile # validate + plan, touch nothing
80
+
81
+ # Daily loop (run from inside a checkout)
82
+ mm-harness launch ios # launch (mobile: ios | android)
83
+ mm-harness logs # tail Metro/webpack + app logs
84
+ mm-harness debug # open the debug console
85
+ mm-harness fixtures sync # sync fixtures; `fixtures set` seeds the wallet
86
+ ```
87
+
88
+ Set `RECIPE_LOG_UI=compact|full|quiet` (default `compact`) and `RECIPE_LOG_EVENTS=10`
89
+ to tune the compact log view during launch and Metro/webpack startup.
90
+
91
+ ## Layout
92
+
93
+ ```text
94
+ bin/ CLI and platform convenience commands
95
+ runner/ what EXECUTES recipes: engine glue (runner/src) +
96
+ per-platform verify (engine, verify, evidence)
97
+ orchestration/ what CONTROLS the app and its instances: launch/live/
98
+ watch/windows/wallet state/health/inject/cleanup per
99
+ platform, shared lib, manifest.json + doctor.mjs
100
+ library/ what recipes ARE: recipes/ (recipe JSONs), actions/
101
+ (per-platform implementations), manifests/ (capability)
102
+ scripts/ dev tooling (yarn check, local farmslot link, e2e validation)
103
+ docs/ details when this README is not enough
104
+ ```
105
+
106
+ Defaults for installed harness/runtime paths live in
107
+ `orchestration/lib/path-defaults.json`.
108
+
109
+ ## Runtime-readiness ownership
110
+
111
+ Generic readiness mechanics live in `@farmslot/recipe-harness` (0.3+):
112
+
113
+ - `@farmslot/recipe-harness/runtime/deps-readiness` — install fingerprint + baseline
114
+ - `@farmslot/recipe-harness/runtime/log-analysis` — bundle-log boundaries, unresolved-module scoping
115
+ - `@farmslot/recipe-harness/runtime/metro-probe` — Metro `/status` reachability
116
+
117
+ This repo adds **MetaMask adapters only**: `orchestration/mobile/deps-markers.ts`,
118
+ `orchestration/mobile/runtime-decision.ts`, `orchestration/extension/runtime-decision.ts`,
119
+ and shell launch in `orchestration/porcelain/mm-recipe` / `orchestration/porcelain/mme-recipe`. Do not re-copy harness
120
+ primitives into `orchestration/lib/`. CI installs harness from npm — publish
121
+ `@farmslot/recipe-harness` before bumping the dependency here.
122
+
123
+ ## Runtime notes
124
+
125
+ - `tmux` is recommended for long-lived Metro/webpack processes; standalone use
126
+ falls back to detached `nohup` where possible.
127
+ - Mobile may inject a local development bridge/HUD into older checkouts. Do not
128
+ commit those product patches.
129
+ - Extension does not patch product source; it drives `dist/chrome` through Chrome
130
+ CDP.
131
+
132
+ ## Validate changes
133
+
134
+ ```bash
135
+ yarn check
136
+ bash -n bin/mm-harness orchestration/porcelain/{metamask-recipe,mm-recipe,mme-recipe} orchestration/{mobile,extension,core}/*.sh runner/{mobile,extension}/verify.sh
137
+ node --check orchestration/extension/inject.mjs orchestration/extension/cleanup.mjs orchestration/extension/readiness.mjs orchestration/extension/launch-browser.cjs orchestration/lib/open-debug.mjs orchestration/lib/cli-home.mjs orchestration/lib/log-tui.mjs orchestration/lib/progress.mjs orchestration/lib/recipe-paths.mjs scripts/check.mjs
138
+ ```
139
+
140
+ More detail: [Architecture](docs/architecture.md), [Package boundaries](docs/package-boundaries.md), [Runtime file conventions](docs/runtime-file-conventions.md).
package/bin/mm-harness ADDED
@@ -0,0 +1,99 @@
1
+ #!/usr/bin/env bash
2
+ # mm-harness — the single front door for the MetaMask recipe loop.
3
+ #
4
+ # Same node/tsx resolution mechanism as bin/metamask-recipe, but the entry is the
5
+ # commander-based CLI (runner/src/mm-harness-cli.ts) that presents only the
6
+ # validated end-state surface. Legacy names (metamask-recipe/mm-recipe/mme-recipe,
7
+ # manifest/live/runtime-*) stay reachable via their own bins until the caller sweep.
8
+ set -euo pipefail
9
+ SOURCE="${BASH_SOURCE[0]}"
10
+ while [ -L "$SOURCE" ]; do
11
+ SOURCE_DIR="$(cd "$(dirname "$SOURCE")" && pwd -P)"
12
+ TARGET="$(readlink "$SOURCE")"
13
+ if [[ "$TARGET" == /* ]]; then
14
+ SOURCE="$TARGET"
15
+ else
16
+ SOURCE="$SOURCE_DIR/$TARGET"
17
+ fi
18
+ done
19
+ SCRIPT_DIR="$(cd "$(dirname "$SOURCE")" && pwd -P)"
20
+ RUNNER_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
21
+
22
+ ENSURE_DEPS="$RUNNER_DIR/orchestration/lib/ensure-runner-deps.sh"
23
+ if [ -f "$ENSURE_DEPS" ]; then
24
+ # shellcheck disable=SC1090
25
+ bash "$ENSURE_DEPS" "$RUNNER_DIR"
26
+ fi
27
+
28
+ ENTRY_TS="$RUNNER_DIR/runner/src/mm-harness-cli.ts"
29
+ ENTRY_DIST="$RUNNER_DIR/dist/mm-harness-cli.js"
30
+
31
+ find_protocol_root() {
32
+ local start="$1"
33
+ local dir
34
+ dir="$(cd "$start" && pwd -P)"
35
+ while [ "$dir" != "/" ]; do
36
+ if [ -f "$dir/packages/recipe-harness/package.json" ] && [ -f "$dir/packages/protocol/package.json" ]; then
37
+ printf '%s\n' "$dir"
38
+ return 0
39
+ fi
40
+ if [ -f "$dir/farmslot/packages/recipe-harness/package.json" ] && [ -f "$dir/farmslot/packages/protocol/package.json" ]; then
41
+ printf '%s\n' "$dir/farmslot"
42
+ return 0
43
+ fi
44
+ dir="$(dirname "$dir")"
45
+ done
46
+ return 1
47
+ }
48
+
49
+ node_can_run_source_typescript() {
50
+ # Node refuses type-stripping for files under node_modules (ERR_UNSUPPORTED_NODE_
51
+ # MODULES_TYPE_STRIPPING). When this runner is installed as a dependency, the
52
+ # source lives under node_modules and cannot run under plain node — a /tmp probe
53
+ # would be a false positive. Skip the node-source path in that case and fall
54
+ # through to dist/tsx.
55
+ case "$RUNNER_DIR/" in
56
+ */node_modules/*) return 1 ;;
57
+ esac
58
+ local probe_dir probe_file
59
+ probe_dir="$(mktemp -d "${TMPDIR:-/tmp}/mm-harness-node-ts.XXXXXX")"
60
+ probe_file="$probe_dir/probe.ts"
61
+ printf 'const answer: number = 42; if (answer !== 42) process.exit(1);\n' > "$probe_file"
62
+ if ! node --no-warnings "$probe_file" >/dev/null 2>&1; then
63
+ rm -rf "$probe_dir"
64
+ return 1
65
+ fi
66
+ rm -rf "$probe_dir"
67
+ }
68
+
69
+ if [ -f "$ENTRY_DIST" ]; then
70
+ exec node "$ENTRY_DIST" "$@"
71
+ fi
72
+
73
+ TSX_BIN="${TSX_BIN:-$RUNNER_DIR/node_modules/.bin/tsx}"
74
+ if [ ! -x "$TSX_BIN" ] && [ -x "$RUNNER_DIR/../../.bin/tsx" ]; then
75
+ TSX_BIN="$RUNNER_DIR/../../.bin/tsx"
76
+ fi
77
+ if [ ! -x "$TSX_BIN" ]; then
78
+ if [ -z "${FARMSLOT_ROOT:-}" ] && [ -f "$RUNNER_DIR/.farmslot-root" ]; then
79
+ FARMSLOT_ROOT="$(cat "$RUNNER_DIR/.farmslot-root")"
80
+ fi
81
+ if [ -z "${FARMSLOT_ROOT:-}" ]; then
82
+ FARMSLOT_ROOT="$(find_protocol_root "$RUNNER_DIR" || find_protocol_root "$PWD" || true)"
83
+ fi
84
+ if [ -n "${FARMSLOT_ROOT:-}" ]; then
85
+ FARMSLOT_ROOT="$(cd "$FARMSLOT_ROOT" && pwd -P)"
86
+ TSX_BIN="$FARMSLOT_ROOT/node_modules/.bin/tsx"
87
+ fi
88
+ fi
89
+
90
+ if node_can_run_source_typescript; then
91
+ exec node --no-warnings "$ENTRY_TS" "$@"
92
+ fi
93
+
94
+ if [ -x "$TSX_BIN" ]; then
95
+ exec "$TSX_BIN" "$ENTRY_TS" "$@"
96
+ fi
97
+
98
+ echo "mm-harness source checkout requires dist/mm-harness-cli.js, Node.js TypeScript type stripping, or a local tsx binary. Install dependencies normally, or set TSX_BIN/FARMSLOT_ROOT for local protocol co-development." >&2
99
+ exit 1
@@ -0,0 +1,61 @@
1
+ # mm-harness — user cheatsheet
2
+
3
+ `mm-harness <command> [target] [flags]` · Run from inside any MetaMask checkout · Platform auto-detected
4
+
5
+ ---
6
+
7
+ ## Daily loop
8
+
9
+ | Situation | Command |
10
+ |---|---|
11
+ | Start / relaunch iOS | `mm-harness launch ios` |
12
+ | Start / relaunch Android | `mm-harness launch android` |
13
+ | Launch extension (fullscreen) | `mm-harness launch` |
14
+ | Launch extension in sidepanel | `mm-harness launch --sidepanel` |
15
+ | Native code changed — rebuild first | `mm-harness launch ios --build` |
16
+ | Tail Metro / webpack logs | `mm-harness logs` |
17
+ | Open DevTools / RN inspector | `mm-harness debug` |
18
+ | Seed wallet from fixture — no typing | `mm-harness fixtures set` |
19
+ | Refresh wallet fixture files on slot | `mm-harness fixtures sync` |
20
+
21
+ `launch` auto-installs the overlay on first use.
22
+ `fixtures set` reads the password **from** the fixture — never typed.
23
+
24
+ ---
25
+
26
+ ## Proof loop
27
+
28
+ ```
29
+ actions → call <action> → flows → run recipe.json --plan → run recipe.json
30
+ ```
31
+
32
+ | Command | What it does |
33
+ |---|---|
34
+ | `mm-harness actions` | Action vocabulary for this adapter |
35
+ | `mm-harness call unlock` | One action through the real engine (fuzzy name ok) |
36
+ | `mm-harness flows` | Reusable flows (personal > team > canonical) |
37
+ | `mm-harness run recipe.json --plan` | Validate + print plan, no device touched; exit 5 if invalid |
38
+ | `mm-harness run recipe.json` | Execute + write evidence (summary / trace / artifacts) |
39
+
40
+ ---
41
+
42
+ ## Health
43
+
44
+ | Command | What it does |
45
+ |---|---|
46
+ | `mm-harness doctor` | Full readiness check — fixture, runtime context, overlay. No launch. |
47
+ | `mm-harness doctor --fix` | Repair overlay + runtime context without launching the app. |
48
+ | `mm-harness verify` | Check overlay/runtime is present and healthy. No launch. |
49
+ | `mm-harness cleanup` | Remove the overlay and restore the checkout. |
50
+
51
+ ---
52
+
53
+ ## For agents and scripts
54
+
55
+ **`--json`** — machine-readable output on every command; includes `recovered[]`, `mutations[]`, `schemaVersion`.
56
+
57
+ **`--heal`** — `off` (fail fast, repro-preserving) · `infra-only` (heal transport, never wallet; `run`/`call` default) · `auto` (auto-ensure overlay + heal; `launch` default).
58
+
59
+ **Exit codes** — `0` success · `1` recipe/action failed · `2` bad args / missing target · `5` validation failure (fix recipe).
60
+
61
+ **Tab completion** — `mm-harness completions install` prints the lines to add to `~/.zshrc` / `~/.bashrc`.