@deeeed/metamask-harness 0.17.0 → 0.17.2

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 (106) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/README.md +90 -140
  3. package/adapters/core/cleanup.sh +0 -0
  4. package/adapters/core/inject.sh +0 -0
  5. package/adapters/extension/cleanup.mjs +0 -0
  6. package/adapters/extension/console-tail.mjs +51 -24
  7. package/adapters/extension/ensure-browser.sh +0 -0
  8. package/adapters/extension/inject.mjs +5 -9
  9. package/adapters/extension/launch-browser.cjs +0 -0
  10. package/adapters/extension/launch-webpack.cjs +83 -0
  11. package/adapters/extension/launch.sh +8 -11
  12. package/adapters/extension/live.sh +2 -0
  13. package/adapters/extension/readiness.mjs +0 -0
  14. package/adapters/extension/reattach.sh +1 -0
  15. package/adapters/extension/refresh-build.sh +0 -0
  16. package/adapters/extension/seed-fixture.sh +4 -12
  17. package/adapters/extension/sidepanel-toggle.sh +4 -1
  18. package/adapters/extension/snapshot-dist.sh +0 -0
  19. package/adapters/extension/stamp-runtime-title.cjs +49 -0
  20. package/adapters/extension/start-watch.sh +16 -4
  21. package/adapters/extension/stop-viewers.sh +1 -1
  22. package/adapters/extension/sync-webpack-dist.cjs +105 -0
  23. package/adapters/extension/verify.sh +1 -1
  24. package/adapters/extension/wallet-fixture-state.cjs +0 -0
  25. package/adapters/manifest.json +46 -38
  26. package/adapters/mobile/bridge-runtime/cdp-bridge.cjs +0 -0
  27. package/adapters/mobile/bridge-runtime/console-forwarder.cjs +1 -1
  28. package/adapters/mobile/bridge-runtime/lib/target-discovery.cjs +2 -3
  29. package/adapters/mobile/bridge-runtime/setup-wallet.sh +0 -0
  30. package/adapters/mobile/cleanup.sh +16 -176
  31. package/adapters/mobile/inject.sh +88 -671
  32. package/adapters/mobile/launch-console-forwarder.cjs +39 -0
  33. package/adapters/mobile/lib/metro-listener.sh +0 -0
  34. package/adapters/mobile/lib/tmux-viewer.sh +0 -0
  35. package/adapters/mobile/open-device.sh +0 -0
  36. package/adapters/mobile/prewarm-bundle.sh +0 -0
  37. package/adapters/mobile/start-console-forwarder.sh +70 -0
  38. package/adapters/mobile/start-metro.sh +0 -41
  39. package/adapters/mobile/stop-metro.sh +0 -0
  40. package/adapters/mobile/verify.sh +4 -61
  41. package/adapters/mobile/wait-for-bridge.sh +0 -0
  42. package/adapters/mobile/yarn-setup.sh +0 -0
  43. package/adapters/shared/activate-repo-node.sh +0 -0
  44. package/adapters/shared/activate-repo-ruby.sh +0 -0
  45. package/adapters/shared/cli-ux.sh +0 -0
  46. package/adapters/shared/ensure-runner-deps.sh +0 -0
  47. package/adapters/shared/harness-path.sh +0 -0
  48. package/adapters/shared/hash-helpers.sh +0 -0
  49. package/adapters/shared/install-repo-deps.sh +0 -0
  50. package/adapters/shared/json-field.sh +0 -0
  51. package/adapters/shared/open-debug.mjs +90 -36
  52. package/adapters/shared/open-log-window.sh +0 -0
  53. package/adapters/shared/reap-checkout-metros.sh +0 -0
  54. package/adapters/shared/resolve-slot-ports-core.mjs +0 -6
  55. package/adapters/shared/resolve-slot-ports.mjs +0 -0
  56. package/adapters/shared/resolve-slot-ports.sh +0 -5
  57. package/adapters/shared/sync-wallet-fixture.sh +0 -0
  58. package/adapters/shared/tmux-session.sh +0 -0
  59. package/adapters/shared/tmux-viewer.sh +0 -0
  60. package/adapters/shared/update-check-worker.mjs +37 -0
  61. package/dist/adapters/extension/console-capture.js +84 -0
  62. package/dist/adapters/extension/runtime-decision.js +14 -2
  63. package/dist/adapters/extension/surface.js +3 -2
  64. package/dist/adapters/mobile/prepare.js +22 -7
  65. package/dist/adapters/mobile/surface.js +1 -1
  66. package/dist/adapters/resolve-slot-ports.js +0 -2
  67. package/dist/adapters/slot-ports.js +8 -0
  68. package/dist/cli.js +3 -11
  69. package/dist/commands/call.js +9 -4
  70. package/dist/commands/debug.js +38 -31
  71. package/dist/commands/fixtures.js +18 -10
  72. package/dist/commands/launch/index.js +10 -0
  73. package/dist/commands/logs.js +8 -6
  74. package/dist/commands/manifest.js +5 -17
  75. package/dist/commands/run-engine.js +1 -32
  76. package/dist/commands/run-report.js +1 -1
  77. package/dist/commands/run.js +50 -3
  78. package/dist/commands/status-probe.js +1 -1
  79. package/dist/commands/update.js +15 -28
  80. package/dist/doctor.js +1 -4
  81. package/dist/harness.js +5 -123
  82. package/dist/live-adapter-contract.js +6 -20
  83. package/dist/mm-harness-cli.js +6 -43
  84. package/dist/paths.js +2 -5
  85. package/dist/run-diagnostics.js +35 -40
  86. package/dist/runtime-context.js +26 -8
  87. package/docs/CHEATSHEET.md +39 -52
  88. package/docs/CLI-ERGONOMICS-HUMAN-QA.md +48 -104
  89. package/docs/CODE-MAP.md +1 -1
  90. package/docs/VIDEO-DEMO-VALIDATION.md +62 -0
  91. package/docs/architecture.md +65 -375
  92. package/docs/live-adapter-contract.md +3 -1
  93. package/library/actions/core/perps/_controller.mjs +10 -55
  94. package/library/manifests/mobile.action-manifest.json +1 -1
  95. package/package.json +2 -2
  96. package/scripts/completions.sh +0 -0
  97. package/scripts/install-completions.sh +0 -0
  98. package/adapters/mobile/overlay/app/dev-tools/AgenticService/AgentStepHud.tsx.patch +0 -185
  99. package/adapters/mobile/overlay/app/dev-tools/AgenticService/AgenticService.ts.patch +0 -1662
  100. package/adapters/mobile/overlay/compat/README.md +0 -28
  101. package/adapters/mobile/overlay/compat/rn81-message-event-source.patch +0 -42
  102. package/adapters/shared/resolve-farmslot-ports-core.mjs +0 -3
  103. package/adapters/shared/resolve-farmslot-ports.mjs +0 -5
  104. package/adapters/shared/resolve-farmslot-ports.sh +0 -7
  105. package/docs/CLI-SPEC.md +0 -1142
  106. package/docs/MENTAL-MODEL.md +0 -295
@@ -1,398 +1,88 @@
1
- # MetaMask Recipe Runner Architecture
1
+ # Architecture
2
2
 
3
- **Control flows DOWN only a DAG, never a loop.**
4
-
5
- ```
6
- bin/mm-harness ← entry (thin bootstrap)
7
-
8
-
9
- ┌───────────────────────────────────────────────┐
10
- │ src/ THE BRAIN — all TypeScript │
11
- │ CLI · decisions · state machines · run │
12
- │ engine · healing/errors · evidence │
13
- └───────────────────────────────────────────────┘
14
- │ spawns │ reads
15
- ▼ ▼
16
- adapters/<platform>/ library/
17
- DEVICE LEAVES (shell) VOCABULARY (data)
18
- one op · args in · recipes · actions ·
19
- result out · exit flows — pure JSON/mjs
20
- (yarn-setup, start-metro,
21
- open-device, xcrun, adb,
22
- webpack, chrome)
23
- ```
24
-
25
- | dir | is | rule |
26
- |---|---|---|
27
- | `bin/` | entry | one thin bootstrap |
28
- | `src/` | the brain | ALL TypeScript, ALL decisions/sequencing |
29
- | `adapters/<platform>/` | device leaves | one op each · never orchestrates · **never calls `mm-harness`** |
30
- | `library/` | vocabulary | pure recipe/action/flow data |
31
-
32
- **The one rule everything follows:** nothing under `adapters/` or `library/` ever calls `mm-harness`. TS orchestrates and spawns leaves; leaves do one device op and exit; vocabulary is data. (External callers — farm hooks, skills, overlay delegate — invoking `mm-harness` at the top level is fine; the ban is on the harness's OWN scripts calling back in.)
33
-
34
- **Why:** every past mess (half-wired porcelain, commands referenced-but-unbuilt, stub tests faking the thing under test) came from control looping back up. Down-only makes that class of bug unrepresentable — and means less code.
35
-
36
- ---
37
-
38
- This repo is the MetaMask-specific adapter layer for Recipe Protocol v1. It is
39
- not the protocol engine, not a skills repo, not a slot farm, and not product
40
- code. Its job is to publish reviewable MetaMask capabilities and run them
41
- against a local Mobile or Extension checkout without committing harness runtime
42
- into that checkout.
43
-
44
- The core model is standalone: install or check out this runner, point it at a
45
- MetaMask checkout, and run `mm-harness`. Farm/slot orchestration is only a
46
- way to scale the same loop across many checkouts, machines, ports, and agents;
47
- it is not required to understand or use the runner.
48
-
49
- ## TL;DR
50
-
51
- The four questions that come up whenever this repo needs explaining.
52
-
53
- **Responsibility split.** This runner owns the logic; skills and farms are a
54
- minimum overlay that call its CLI. Rule of thumb: if a skill's shell script is
55
- doing more than parsing flags and calling `mm-harness`, that logic
56
- belongs in this runner instead. Concrete example in flight: the
57
- `recipe-harness` skill currently locates and execs runner internals in
58
- hundreds of lines of bash (resolve runner source, dispatch to
59
- `adapters/<platform>/{inject,cleanup}.{sh,mjs}` — `.sh` for Mobile/Core,
60
- `.mjs` for Extension); the fix is a
61
- [`mm-harness <install|verify|cleanup|live>`](CLI-SPEC.md) command here, so the
62
- skill thins to a few lines that call it. Same pattern applies to every other
63
- `recipe-*` skill over time.
64
-
65
- **Language policy — bash vs ts vs mjs vs cjs.** Current mix: 65 `.sh`, 62
66
- `.mjs`, 17 `.ts`, 9 `.cjs` (excluding `node_modules`).
67
-
68
- | Extension | Where | Use for | Why |
69
- |---|---|---|---|
70
- | `.ts` | `src/**` | CLI parsing, manifests, adapter binding, runtime decisions — typed/testable/shared logic | Compiled and typed; the maintainable core that everything else should grow into |
71
- | `.mjs` | `library/actions/**`, `adapters/**`, `scripts/**` | Standalone action adapters and injected runtime helpers | Run-anywhere with zero build step, whether invoked from a published package or a raw checkout |
72
- | `.cjs` | scattered, quarantined | Compatibility islands: portable `require()` from shell, bridge code for older runtime contexts | Only when the runtime context makes ESM brittle — not a default choice |
73
- | `.sh` | `bin/`, `adapters/{mobile,extension,core}/`, `adapters/shared/` | Thin OS/device glue at the edge: `simctl`, `adb`, Chrome process flags, git-exclude, filesystem setup | Must run before/without Node business logic; never recipe graph execution or MetaMask domain semantics |
74
-
75
- The mix is bash-heavy today because sandbox setup crosses OS/device boundaries
76
- (see "Why there are shell scripts" below) — that is current reality, not the
77
- target ratio. The direction is less bash over time: when a shell helper starts
78
- accumulating domain behavior, move it into `.ts` or a focused `.mjs` adapter
79
- (see [Runtime File Conventions](runtime-file-conventions.md) for the full
80
- rule).
81
-
82
- **Packaging — one package, not several.** This stays one npm package while the
83
- runtime API is still moving; see [Package Boundaries](package-boundaries.md)
84
- for the full rationale. Splitting now would force every wrapper (skills,
85
- farmslot) to pin two coordinated versions before the boundary between "recipe
86
- execution" and "runtime lifecycle" has stabilized — coordination cost for no
87
- current benefit. Split when the runtime CLI contract is stable, all wrappers
88
- call it exclusively (no copied adapter scripts), and the two areas have gone
89
- several releases without a change that touches both.
90
-
91
- ## Layer map (front door → engine → schema)
92
-
93
- The whole stack is three layers with a strictly one-way dependency. `mm-harness`
94
- is the MetaMask-facing **front door**; underneath it the runner is a thin
95
- **adapter layer** over a fat generic **engine** (`@farmslot/recipe-harness`) and
96
- **schema** (`@farmslot/protocol`). The runner never re-implements engine logic —
97
- it imports it.
98
-
99
- ```text
100
- mm-harness (the one bin — no per-platform binaries) ← front door: one verb vocabulary
101
- │ launch · logs · debug · fixtures (daily loop) · run/flows/doctor/actions/manifest (prove)
102
- │ install/verify/cleanup/live (runtime overlay lifecycle)
103
-
104
- metamask-recipe-runner (MetaMask ADAPTER layer) ← this repo
105
- │ MetaMask action manifests, metamask.* live adapters, Mobile/Extension
106
- │ runtime overlay install, runtime health/decision probes
107
-
108
- @farmslot/recipe-harness (GENERIC engine) ← npm dependency
109
- │ recipe graph execution, official ui.* transports, RecordingTarget
110
- │ evidence framework, runtime modules, generic run/flows/validate CLI
111
-
112
- @farmslot/protocol (GENERIC schema) ← npm dependency
113
- │ Recipe v1 schema, action-manifest document, artifact contract
114
-
115
- MetaMask app under test
116
- │ local debug runtime only; product source should not own runner logic
117
-
118
- summary.json · trace.json · artifact-manifest.json · screenshots/logs
119
- ```
120
-
121
- **Dependency direction is one-way and provable.** The runner *depends on*
122
- `@farmslot/recipe-harness` + `@farmslot/protocol` (see `package.json`), and
123
- `src/**` *imports* the generic contracts rather than forking them:
124
-
125
- - `src/cli.ts` imports `RecipeRunResult` from `@farmslot/recipe-harness`
126
- and delegates library `flows` to the harness CLI (`importRecipeHarnessCli`).
127
- - `src/runner.ts` wires `metamask.*` adapters INTO the farmslot
128
- `RecipeRunner`; it does not implement graph execution.
129
- - `src/manifest.ts` loads *MetaMask* manifests typed by the *farmslot*
130
- protocol document type.
131
- - `src/recording-target.ts` is a MetaMask provider over the farmslot
132
- `RecordingTarget` contract.
133
-
134
- **The invariant both repos assert:** farmslot stays MetaMask-free; the runner
135
- never re-implements engine logic, only binds to it. Decision lens for placing any
136
- new capability: *would a non-MetaMask project reuse this unchanged?* → yes =
137
- generic (farmslot); encodes MetaMask app knowledge = runner.
138
-
139
- > Note on naming: "harness" refers to the tool (`mm-harness`) and the generic
140
- > engine package (`@farmslot/recipe-harness`). The per-checkout files the tool
141
- > installs into a target are the **runtime overlay** (`install`/`verify`/
142
- > `cleanup`/`live`), never "the harness" — see [CLI-SPEC.md](CLI-SPEC.md).
143
-
144
- ## One-minute model
3
+ `mm-harness` is the MetaMask adapter and CLI over the generic Recipe Protocol
4
+ engine. Control flows downward only:
145
5
 
146
6
  ```text
147
- human CLI / optional skill wrapper
148
- resolve + invoke
149
-
150
- metamask-recipe-runner
151
- MetaMask action manifests, live adapters, runtime overlay install, runtime probes
152
-
153
- Recipe harness package
154
- │ generic graph execution, official ui.* transports, traces, artifacts
155
-
156
- Recipe protocol package
157
- │ Recipe v1 schema, manifest contract, artifact contract
158
-
159
- MetaMask app under test
160
- │ local debug runtime only; product source should not own runner logic
161
-
162
- summary.json · trace.json · artifact-manifest.json · screenshots/logs
7
+ bin/mm-harness
8
+ src/ typed CLI, decisions, orchestration, evidence
9
+ → @farmslot/recipe-harness generic graph execution and transports
10
+ → @farmslot/protocol recipe, manifest, trace, artifact schemas
11
+ adapters/ focused OS, browser, simulator, and device leaves
12
+ → library/ MetaMask actions, flows, recipes, manifests
13
+ MetaMask checkout app under test
163
14
  ```
164
15
 
165
- Dependency direction is one-way. Wrappers call the runner; the runner calls the
166
- shared harness/protocol packages; product apps are only driven at runtime.
16
+ Nothing under `adapters/` or `library/` calls back into `mm-harness`. TypeScript
17
+ owns sequencing; a leaf performs one platform operation and exits; the library
18
+ defines composable behavior.
167
19
 
168
- ## Package shape
20
+ ## Ownership
169
21
 
170
- This repo remains one npm package for now, but it has two internal areas: recipe execution and runtime lifecycle. See [Package Boundaries](package-boundaries.md) for the stability policy and why we are not splitting packages yet.
171
-
172
- ## The two subsystems
173
-
174
- This repo has two intentionally different kinds of code. Keeping them separate
175
- is the main way to understand the repository.
176
-
177
- | Subsystem | Question it answers | Primary files | Should contain | Should not contain |
178
- |---|---|---|---|---|
179
- | Recipe capability/execution | “What can a MetaMask recipe do, and how does a node execute?” | `library/manifests/`, `library/recipes/`, `src/runner.ts`, `src/adapters.ts`, `src/live-adapter-contract.ts`, `library/actions/` | action manifests, domain actions, UI transport binding, adapter outputs, proof semantics | Metro startup, Chrome process flags, simulator boot, git-exclude/rsync cleanup |
180
- | Runtime lifecycle / sandbox helpers | “How do I give an agent an isolated app session that is ready to inspect or run recipes?” | `adapters/porcelain/`, `adapters/{mobile,extension,core}/`, `adapters/shared/` | install/sync harness, start/reuse Metro or Chrome, prewarm bundles, open Extension full-screen or popup-style, prepare dedicated profiles/fixtures, check build/runtime health, cleanup local files | new recipe schema, graph traversal, MetaMask business semantics, task-specific proof logic |
181
-
182
- When reviewing a change, first decide which subsystem it touches. Recipe changes
183
- should be validated against manifests and action artifacts. Runtime lifecycle changes should be validated by install/launch/live/verify
184
- behavior on a real checkout. Some commands cross the boundary, but they should
185
- do so by delegating: sandbox helpers get the runtime ready, then
186
- `mm-harness run` executes the recipe.
187
-
188
- ## Ownership boundaries
189
-
190
- | Layer | Owns | Must not own |
22
+ | Layer | Owns | Does not own |
191
23
  |---|---|---|
192
- | Human CLI / optional wrapper | Target selection, runner invocation, evidence handoff | Copied adapter scripts, recipe graph execution, product runtime logic |
193
- | This runner | MetaMask action manifests, `metamask.*` adapters, Mobile/Extension harness install, runtime health/decision commands | Shared Recipe v1 schema, generic `ui.*` semantics, task-specific acceptance criteria |
194
- | `Recipe harness package` | Recipe graph execution, standard core/ui adapters, trace/summary/artifact writing | MetaMask wallet/Perps behavior |
195
- | `Recipe protocol package` | Recipe/manifest/artifact schemas | Runtime control or product-specific actions |
196
- | Product checkout | App code and debug hooks exposed by the app | Harness scripts, runner copy, skills, private workflow logic |
24
+ | `@farmslot/protocol` | Recipe and evidence schemas | execution or product behavior |
25
+ | `@farmslot/recipe-harness` | graph execution, generic `ui.*`, traces and artifacts | MetaMask behavior |
26
+ | this package | MetaMask actions, runtime control, CLI, diagnostics | task acceptance criteria or copied engine logic |
27
+ | skills/checklists | task workflow and proof expectations | runtime or runner implementation |
28
+ | product checkout | application and explicit debug hooks | harness scripts or private workflow logic |
197
29
 
198
- Rule of thumb: if code describes **what MetaMask can do**, it belongs here. If it
199
- describes **how Recipe v1 works**, it belongs in the shared protocol/runtime packages. If it
200
- describes **how an agent should work**, it belongs in skills.
30
+ A capability reusable without MetaMask belongs in the generic engine. A durable
31
+ MetaMask operation belongs here. Ticket-specific assertions stay in the task's
32
+ recipe or checklist.
201
33
 
202
- ## Key files and directories
34
+ ## Repository layout
203
35
 
204
36
  | Path | Responsibility |
205
37
  |---|---|
206
- | `src/cli.ts` | Typed command handlers: manifests, actions, doctor, runtime health/decision/launch, `run`. |
207
- | `src/runner.ts` | Creates the Recipe runner by combining shared core/ui adapters with MetaMask live adapters. Enables the Recipe HUD metadata. |
208
- | `src/adapters.ts` | MetaMask adapter binding and `ui.*` transport selection for Mobile vs Extension. Refuses static placeholders for live-only proof actions. |
209
- | `src/live-adapter-contract.ts` | Script adapter contract and lookup rules for `library/actions/<platform>/<domain>/*.mjs`. |
210
- | `library/manifests/*.action-manifest.json` | Reviewable capability contract. A recipe may only call declared actions. |
211
- | `library/actions/mobile/` | Mobile action implementations. Talks to the runner bridge and app-exposed `globalThis.__AGENTIC__` hooks. |
212
- | `library/actions/extension/` | Extension action implementations. Talks to Chrome/extension pages over CDP. |
213
- | `adapters/mobile/inject.sh` | Installs/syncs the Mobile runtime overlay under the configured harness root and protects cleanup/git-exclude behavior. |
214
- | `adapters/extension/inject.mjs` | Installs/syncs Extension runtime helpers under the configured harness root. |
215
- | `adapters/mobile/` | Runner-owned Mobile launch/live/inject/cleanup and verify helpers copied into installed harnesses. |
216
- | `adapters/extension/` | Runner-owned Extension launch/live/watch/windows/wallet-state/readiness helpers (orchestration) and verify (recipe) copied into installed harnesses. |
217
- | `adapters/shared/path-defaults.json` | Single source for default `recipeHarnessRoot` and `recipeRuntimeDir`. |
218
- | `adapters/shared/harness-path.sh`, `src/paths.ts` | Shell and TypeScript accessors for those defaults plus validation. |
219
- | `library/recipes/` | Reusable smoke/runner.action-validation recipes only. Task-specific proof recipes stay task-local. |
220
- | `library/library.json`, `library/flows/` | Canonical recipe library: minimal, stable flows recipes can `call`. Personal/team libraries shadow it by default — see [Recipe Libraries](recipe-libraries.md). |
221
- | `docs/` | Runner architecture, contracts, and operational conventions. |
222
-
223
- ## Runtime paths and installed harnesses
224
-
225
- Defaults are centralized in `adapters/shared/path-defaults.json`:
226
-
227
- ```json
228
- {
229
- "recipeHarnessRoot": "temp/recipe/harness",
230
- "recipeRuntimeDir": "temp/recipe/runtime"
231
- }
232
- ```
233
-
234
- All shell, standalone Node, and TypeScript code must read these through the
235
- shared helpers instead of hardcoding defaults. Environment overrides are allowed
236
- through `RECIPE_HARNESS_ROOT` and `RECIPE_RUNTIME_DIR`, but they must stay safe
237
- relative paths.
238
-
239
- Install commands write a small runtime package into the target checkout:
38
+ | `bin/mm-harness` | one public executable |
39
+ | `src/commands/` | public command behavior |
40
+ | `src/adapters/` | typed product decisions and orchestration |
41
+ | `adapters/<platform>/` | focused shell/Node runtime leaves |
42
+ | `adapters/shared/` | cross-platform OS glue |
43
+ | `adapters/*/overlay/` | the only files installed or patched into a checkout |
44
+ | `library/actions/` | executable MetaMask action adapters |
45
+ | `library/flows/` | reusable parameterized behavior |
46
+ | `library/recipes/` | reusable proof recipes |
47
+ | `library/manifests/` | declared capability surface |
48
+ | `scripts/` | repository development checks only |
49
+
50
+ All TypeScript belongs under `src/`. New standalone runtime helpers use ESM
51
+ unless their host runtime requires CommonJS. Shell remains limited to operations
52
+ that genuinely need shell/device tools.
53
+
54
+ ## Runtime isolation
55
+
56
+ Each checkout owns its runtime state:
240
57
 
241
58
  ```text
242
- <target>/<recipeHarnessRoot>/<adapter>/
243
- manifest.json installed source/revision/cleanup metadata
244
- action-manifest.json snapshot of the adapter manifest
245
- runner/bin/mm-harness delegate back to the resolved runner source
246
- runner/recipes/ reusable recipe snapshot
247
- scripts/ adapter runtime helpers copied from this repo
59
+ temp/recipe/runtime/ ports, processes, fixture, logs, runtime identity
60
+ temp/recipe/harness/ installed runtime overlay
248
61
  ```
249
62
 
250
- The installed harness exists so a running slot has stable helper paths even when
251
- called from skills, orchestration hooks, or a human shell. The source of truth remains
252
- this runner.
253
-
254
- ## Recipe execution vs sandbox lifecycle
255
-
256
- `mm-harness run <recipe.json> --adapter ...` is the recipe path. It creates
257
- a shared Recipe runner (`src/runner.ts`), validates the recipe against the
258
- manifest, executes nodes, and writes artifacts. If a bug is about action fields,
259
- trace output, adapter semantics, or whether a recipe proves a claim, start in
260
- `library/manifests/`, `src/`, `library/actions/`, and `library/recipes/`.
261
-
262
- `prepare`, `launch`, `live`, `verify`, `status`, `decision`, and `ready` are
263
- sandbox lifecycle paths. They give the agent a reproducible local app session:
264
- Mobile with Metro/dev-client/simulator and the bridge online; Extension with an
265
- isolated browser profile, unpacked extension loaded, and a known home/popup-style
266
- UI target. If a bug is about Metro, bundle prewarm, simulator launch, Chrome CDP,
267
- Extension full-screen vs popup presentation, build freshness, wallet fixture
268
- placement, git-exclude, or cleanup, start in `adapters/{mobile,extension,core}/`.
269
-
270
- Do not put recipe graph traversal into shell scripts. Shell scripts may prepare
271
- or inspect the sandboxed runtime, then delegate graph execution to
272
- `mm-harness run`.
273
-
274
- ## HUD vs bridge vs product hooks
275
-
276
- These names are easy to mix up; they are different concerns.
277
-
278
- | Term | What it is | Why it exists |
279
- |---|---|---|
280
- | Recipe HUD | A visual overlay driven by Recipe runner metadata (`intent`, current node, status). | Makes screenshots/videos explain what the recipe is doing without exposing secrets. |
281
- | Mobile bridge | Runner-side CDP/Hermes bridge process under `adapters/mobile/bridge-runtime/`. | Lets the runner call app-exposed commands, read state, press UI targets, and capture status from React Native. |
282
- | `globalThis.__AGENTIC__` | Development-only in-app command surface exposed by the Mobile overlay/patch. | Gives the bridge a stable API for route/status/wallet/UI operations when the app lacks a built-in automation API. |
283
- | Extension CDP hooks | Chrome DevTools Protocol access to extension pages/background state hooks. | Lets the runner inspect/drive the unpacked Extension without patching product source. |
284
-
285
- The HUD does not control the app. The bridge/CDP control the app. The HUD only
286
- renders proof context.
287
-
288
- ## Mobile runtime shape
289
-
290
- Mobile has the most moving parts because React Native does not expose a browser
291
- DOM by default.
292
-
293
- ```text
294
- mm-harness launch ios|android
295
- │ prepareMobile() in-process → yarn-setup / start-metro / open-device leaves
296
-
297
- adapters/mobile/bridge-runtime/cdp-bridge.cjs
298
- │ connects to Hermes / RN debug runtime
299
-
300
- globalThis.__AGENTIC__ inside the app
301
- │ route/status/wallet/ui commands + optional HUD rendering
302
-
303
- library/actions/mobile/{wallet,perps,ui,platform}/*.mjs
304
- ```
305
-
306
- Ideally the app would expose a product-owned debug automation surface directly,
307
- so the runner would not need to patch or inject Mobile client files at all. The
308
- current injection exists as a compatibility bridge: it lets recipes run against
309
- older Mobile versions, historical PR branches, and eval/replay runs that do not yet include that
310
- client-side automation surface.
311
-
312
- The Mobile injection currently may touch development-only product files on older
313
- checkouts to install the `AgenticService`, navigation hook, and HUD mount. That
314
- is the fragile compatibility path, not the desired long-term product contract.
315
- Those product patches are local runtime state: never commit them to MetaMask
316
- Mobile. The runner also installs ignored helper files under
317
- `temp/recipe/harness/mobile`.
318
-
319
- Mobile `ensure_*` actions must be idempotent: if the wallet is already unlocked,
320
- `metamask.wallet.ensure_unlocked` should report success or converge cheaply, not
321
- fail because the starting state differed.
322
-
323
- ## Extension runtime shape
324
-
325
- Extension does not need an in-product source patch. The runner works through an
326
- unpacked `dist/chrome` build and Chrome CDP.
327
-
328
- ```text
329
- mme-recipe / adapters/extension/*.sh
330
- │ checks dist freshness, build health, fixture/profile state
331
-
332
- Chrome for Testing with --load-extension=<runtime-dist>
333
- │ CDP target discovery + deterministic extension id
334
-
335
- library/actions/extension/{wallet,perps,ui,platform}/*.mjs
336
- │ extension page/background hooks and UI events
337
- ```
338
-
339
- `runtime-launch --start-watch` is the clean-build path. Without `--start-watch`,
340
- verify can prove the existing runtime is reachable but may fail `dist-freshness`
341
- if `dist/chrome` does not match `HEAD`. That failure is intentional: it prevents
342
- silently proving stale product code.
343
-
344
- ## Why there are shell scripts
345
-
346
- The shell scripts are large because sandbox setup crosses OS/device boundaries:
347
- `simctl`, `adb`, Metro, Watchman, Chrome process flags, isolated browser
348
- profiles, git exclude files, symlink safety checks, and cleanup all live outside
349
- Node's typed domain logic. Their purpose is to give an agent a reliable app
350
- session, not to define recipe semantics.
351
-
352
- Allowed in shell:
353
-
354
- - parsing CLI flags for lifecycle commands;
355
- - calling OS/device tools;
356
- - copying/removing installed harness files;
357
- - starting/stopping/reusing local dev servers;
358
- - writing small runtime summaries from command results.
359
-
360
- Not allowed in shell:
361
-
362
- - Recipe v1 graph execution;
363
- - MetaMask domain semantics that can live in `src/**/*.ts` or `library/actions/**/*.mjs`;
364
- - duplicated action manifest logic;
365
- - product-specific business decisions beyond runtime boot/health checks.
366
-
367
- When a shell helper starts accumulating domain behavior, move that behavior into
368
- TypeScript or a focused `.mjs` adapter and keep shell as the launcher.
369
-
370
- ## Adding or changing capabilities
63
+ Defaults live in `adapters/shared/path-defaults.json`. Every browser profile,
64
+ simulator/device selection, port, watcher, and evidence directory must resolve
65
+ from the current checkout so parallel slots do not share state.
371
66
 
372
- 1. Add/adjust the shared capability in both Mobile and Extension manifests when
373
- the concept exists on both platforms.
374
- 2. Implement durable behavior under `library/actions/<platform>/<domain>/`.
375
- 3. Keep parameterized actions instead of multiplying action names.
376
- 4. Ensure every `ensure_*` action proves a postcondition.
377
- 5. Use official `ui.*` actions and screenshot claims for visible acceptance
378
- criteria; do not add task-specific `metamask.*` actions for exact copy,
379
- styling, ticket IDs, or one-off selectors.
380
- 6. Update smoke/runner.action-validation recipes only when the capability is reusable.
67
+ Extension uses an isolated Chrome profile and CDP port. Its normal watcher builds
68
+ to product `dist/chrome`, then synchronizes successful output into the isolated
69
+ runtime extension. Mobile owns an isolated Metro/device identity. Core remains
70
+ headless.
381
71
 
382
- ## Standalone use first; farm later
72
+ ## Proof boundary
383
73
 
384
- This runner should make sense without any slot farm:
74
+ `launch` prepares the runtime. `run` validates and executes a recipe through the
75
+ generic engine. Runtime setup may seed declared fixtures before proof begins;
76
+ proof actions must not mutate hidden product state to fabricate a result.
385
77
 
386
- 1. choose a local MetaMask Mobile or Extension checkout;
387
- 2. run `mm-harness launch <platform>` (or `mm-harness install` for the overlay only);
388
- 3. run `mm-harness doctor` (or the advanced `runtime-health`);
389
- 4. run `mm-harness run <recipe.json> --adapter <platform> --artifacts-dir <dir>`.
78
+ Each run produces a summary, trace, diagnostics where applicable, and an artifact
79
+ manifest. Application warnings/errors are side findings unless a recipe assertion
80
+ turns them into a proof failure.
390
81
 
391
- Skills are optional workflow wrappers around that same CLI. A wrapper may resolve
392
- the runner source and package evidence, but it should dispatch `install`,
393
- `launch`, `live`, `verify`, and `cleanup` to this repo or to the installed
394
- harness helper. It should not carry Mobile or Extension adapter scripts.
82
+ ## Change checks
395
83
 
396
- Farm/slot orchestration belongs one layer outside this runner. It can scale the
397
- standalone loop by choosing checkouts, machines, ports, simulators, and agents,
398
- but it should not change the runner contract or copy runner implementation.
84
+ - Put the change in the owning layer; do not duplicate behavior in a skill or leaf.
85
+ - Keep actions parameterized and manifests honest.
86
+ - Validate the machine contract and the visible human outcome.
87
+ - Exercise affected platforms on real checkouts when runtime behavior changes.
88
+ - Run `yarn check` and update `CHANGELOG.md` for user-visible changes.
@@ -19,7 +19,9 @@ $METAMASK_RECIPE_LIVE_ADAPTER_DIR/shared/<domain>/<action-local-name>.mjs
19
19
  <runner>/library/actions/shared/<domain>/<action-local-name>.mjs
20
20
  ```
21
21
 
22
- Legacy external `.js` adapters are still resolved for compatibility, and `.sh` remains available for edge orchestration. New committed adapter code should use grouped `.mjs` modules. Fully-qualified flat filenames are still searched after grouped paths, but new adapter code should use grouped modules. Do not add `ui.*` files here; official UI actions go through shared base transports. Examples:
22
+ Action adapters are grouped `.mjs` modules. Flat files, CommonJS, and shell
23
+ adapters are not part of the contract. Do not add `ui.*` files here; official UI
24
+ actions go through shared base transports. Examples:
23
25
 
24
26
  ```text
25
27
  library/actions/extension/perps/ensure_positions.mjs
@@ -90,12 +90,7 @@ function buildInfrastructure(stubbed) {
90
90
  // The fixture has the standard { accounts: [{ type, value, name }] } shape from
91
91
  // wallet-fixture.json.sample. The recipe node selects an account by name via
92
92
  // `account_name` (default "dev1"). The viem account derived from the fixture
93
- // entry is the authoritative source for both the address AND the signing key
94
- // no separate MM_TEST_ACCOUNT_ADDRESS env var needed.
95
- //
96
- // Env-var fallback: if no fixture is present (e.g. direct CLI invocation),
97
- // MM_TEST_ACCOUNT_SRP / MM_TEST_ACCOUNT_PRIVATE_KEY + MM_TEST_ACCOUNT_ADDRESS
98
- // are still accepted for backward compatibility.
93
+ // entry is the authoritative source for both the address and signing key.
99
94
 
100
95
  /**
101
96
  * Load wallet-fixture.json and return the named account entry.
@@ -155,26 +150,6 @@ function viemAccountFromFixtureEntry(entry) {
155
150
  return privateKeyToAccount(normalized);
156
151
  }
157
152
 
158
- // Env-var fallback constants (used only when wallet-fixture.json is absent).
159
- const SIGNER_PRIVATE_KEY_ENV = 'MM_TEST_ACCOUNT_PRIVATE_KEY';
160
- const SIGNER_MNEMONIC_ENV = 'MM_TEST_ACCOUNT_SRP';
161
-
162
- function signerFromEnv() {
163
- const pk = process.env[SIGNER_PRIVATE_KEY_ENV]?.trim();
164
- if (pk && pk.length > 0) {
165
- const normalized = pk.startsWith('0x') ? pk : `0x${pk}`;
166
- if (!/^0x[0-9a-fA-F]{64}$/u.test(normalized)) {
167
- throw new Error(`${SIGNER_PRIVATE_KEY_ENV} is not a 32-byte hex private key.`);
168
- }
169
- return privateKeyToAccount(normalized);
170
- }
171
- const mnemonic = process.env[SIGNER_MNEMONIC_ENV]?.trim();
172
- if (mnemonic && mnemonic.split(/\s+/u).length >= 12) {
173
- return mnemonicToAccount(mnemonic, { addressIndex: 0 });
174
- }
175
- return null;
176
- }
177
-
178
153
  /**
179
154
  * Resolve the account name to use for signing.
180
155
  * Precedence: node.account_name → node.account (if not an address) → "dev1".
@@ -198,9 +173,7 @@ function resolveAccountName(input) {
198
173
 
199
174
  /**
200
175
  * Resolve the viem signer and EVM address for writes.
201
- * Primary: wallet-fixture.json account selected by name.
202
- * Fallback: MM_TEST_ACCOUNT_PRIVATE_KEY / MM_TEST_ACCOUNT_SRP env vars
203
- * (requires MM_TEST_ACCOUNT_ADDRESS for address verification).
176
+ * The wallet fixture account selected by name is the only signing source.
204
177
  *
205
178
  * @param input - Adapter input (context.projectRoot, node.account_name).
206
179
  * @returns { account: ViemAccount, address: string }
@@ -209,7 +182,6 @@ async function resolveSignerFromFixture(input) {
209
182
  const projectRoot = input.context?.projectRoot;
210
183
  const accountName = resolveAccountName(input);
211
184
 
212
- // Primary: fixture
213
185
  if (projectRoot) {
214
186
  const entry = await loadFixtureAccount(projectRoot, accountName);
215
187
  if (entry) {
@@ -218,32 +190,15 @@ async function resolveSignerFromFixture(input) {
218
190
  }
219
191
  }
220
192
 
221
- // Fallback: env vars (no fixture present — direct CLI use)
222
- const account = signerFromEnv();
223
- if (!account) {
224
- throw new Error(
225
- `core perps writes require a wallet-fixture.json with account "${accountName}", ` +
226
- `or env vars ${SIGNER_PRIVATE_KEY_ENV} / ${SIGNER_MNEMONIC_ENV} + MM_TEST_ACCOUNT_ADDRESS.`,
227
- );
228
- }
229
- const envAddress = String(process.env.MM_TEST_ACCOUNT_ADDRESS ?? '').trim();
230
- if (!/^0x[0-9a-fA-F]{40}$/u.test(envAddress)) {
231
- throw new Error(
232
- `Env-var fallback requires MM_TEST_ACCOUNT_ADDRESS (a 0x EVM address) to verify the signer.`,
233
- );
234
- }
235
- if (account.address.toLowerCase() !== envAddress.toLowerCase()) {
236
- throw new Error(
237
- `Env-var signer derives ${account.address} but MM_TEST_ACCOUNT_ADDRESS is ${envAddress}; signatures would be invalid.`,
238
- );
239
- }
240
- return { account, address: envAddress };
193
+ throw new Error(
194
+ `core perps writes require wallet-fixture.json account "${accountName}". ` +
195
+ `Next: run mm-harness fixtures set`,
196
+ );
241
197
  }
242
198
 
243
199
  /**
244
200
  * Resolve the account address for reads (no signing required).
245
- * Primary: wallet-fixture.json account selected by name (address derived from key).
246
- * Fallback: node.account / node.address / MM_TEST_ACCOUNT_ADDRESS env var.
201
+ * Prefer the wallet fixture account; explicit node addresses support read-only calls.
247
202
  */
248
203
  async function requireAccountAddress(input) {
249
204
  const projectRoot = input.context?.projectRoot;
@@ -261,13 +216,13 @@ async function requireAccountAddress(input) {
261
216
  }
262
217
  }
263
218
 
264
- // Fallback: explicit address from node or env
219
+ // Read-only calls may name an address directly.
265
220
  const fromNode = input.node?.account ?? input.node?.address ?? input.node?.userAddress;
266
- const address = String(fromNode ?? process.env.MM_TEST_ACCOUNT_ADDRESS ?? '').trim();
221
+ const address = String(fromNode ?? '').trim();
267
222
  if (!/^0x[0-9a-fA-F]{40}$/u.test(address)) {
268
223
  throw new Error(
269
224
  `core perps reads require a wallet-fixture.json with account "${accountName}", ` +
270
- `or a 0x EVM address via node.account / MM_TEST_ACCOUNT_ADDRESS.`,
225
+ `or a 0x EVM address via node.account.`,
271
226
  );
272
227
  }
273
228
  return address;
@@ -156,7 +156,7 @@
156
156
  ]
157
157
  },
158
158
  "ui.press": {
159
- "description": "Press a React Native component by exact testID/test_id, a legacy selector identifier, or contained visible text.",
159
+ "description": "Press a React Native component by exact testID/test_id, selector identifier, or contained visible text.",
160
160
  "schema": {
161
161
  "type": "object",
162
162
  "properties": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deeeed/metamask-harness",
3
- "version": "0.17.0",
3
+ "version": "0.17.2",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "mm-harness": "bin/mm-harness"
@@ -71,4 +71,4 @@
71
71
  "url": "https://github.com/MetaMask/experimental-metamask-harness/issues"
72
72
  },
73
73
  "homepage": "https://github.com/MetaMask/experimental-metamask-harness#readme"
74
- }
74
+ }
File without changes
File without changes