@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
@@ -0,0 +1,367 @@
1
+ # MetaMask Recipe Runner Architecture
2
+
3
+ This repo is the MetaMask-specific adapter layer for Recipe Protocol v1. It is
4
+ not the protocol engine, not a skills repo, not a slot farm, and not product
5
+ code. Its job is to publish reviewable MetaMask capabilities and run them
6
+ against a local Mobile or Extension checkout without committing harness runtime
7
+ into that checkout.
8
+
9
+ The core model is standalone: install or check out this runner, point it at a
10
+ MetaMask checkout, and run `mm-harness`. Farm/slot orchestration is only a
11
+ way to scale the same loop across many checkouts, machines, ports, and agents;
12
+ it is not required to understand or use the runner.
13
+
14
+ ## TL;DR
15
+
16
+ The four questions that come up whenever this repo needs explaining.
17
+
18
+ **Responsibility split.** This runner owns the logic; skills and farms are a
19
+ minimum overlay that call its CLI. Rule of thumb: if a skill's shell script is
20
+ doing more than parsing flags and calling `mm-harness`, that logic
21
+ belongs in this runner instead. Concrete example in flight: the
22
+ `recipe-harness` skill currently locates and execs runner internals in
23
+ hundreds of lines of bash (resolve runner source, dispatch to
24
+ `orchestration/<platform>/{inject,cleanup}.{sh,mjs}` — `.sh` for Mobile/Core,
25
+ `.mjs` for Extension); the fix is a
26
+ [`mm-harness <install|verify|cleanup|live>`](harness-cli.md) command here, so the
27
+ skill thins to a few lines that call it. Same pattern applies to every other
28
+ `recipe-*` skill over time.
29
+
30
+ **Language policy — bash vs ts vs mjs vs cjs.** Current mix: 65 `.sh`, 62
31
+ `.mjs`, 17 `.ts`, 9 `.cjs` (excluding `node_modules`).
32
+
33
+ | Extension | Where | Use for | Why |
34
+ |---|---|---|---|
35
+ | `.ts` | `runner/src/**` | CLI parsing, manifests, adapter binding, runtime decisions — typed/testable/shared logic | Compiled and typed; the maintainable core that everything else should grow into |
36
+ | `.mjs` | `library/actions/**`, `orchestration/**`, `scripts/**` | Standalone action adapters and injected runtime helpers | Run-anywhere with zero build step, whether invoked from a published package or a raw checkout |
37
+ | `.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 |
38
+ | `.sh` | `bin/`, `orchestration/{mobile,extension,core}/`, `runner/{mobile,extension}/`, `orchestration/lib/` | 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 |
39
+
40
+ The mix is bash-heavy today because sandbox setup crosses OS/device boundaries
41
+ (see "Why there are shell scripts" below) — that is current reality, not the
42
+ target ratio. The direction is less bash over time: when a shell helper starts
43
+ accumulating domain behavior, move it into `.ts` or a focused `.mjs` adapter
44
+ (see [Runtime File Conventions](runtime-file-conventions.md) for the full
45
+ rule).
46
+
47
+ **Packaging — one package, not several.** This stays one npm package while the
48
+ runtime API is still moving; see [Package Boundaries](package-boundaries.md)
49
+ for the full rationale. Splitting now would force every wrapper (skills,
50
+ farmslot) to pin two coordinated versions before the boundary between "recipe
51
+ execution" and "runtime lifecycle" has stabilized — coordination cost for no
52
+ current benefit. Split when the runtime CLI contract is stable, all wrappers
53
+ call it exclusively (no copied adapter scripts), and the two areas have gone
54
+ several releases without a change that touches both.
55
+
56
+ ## Layer map (front door → engine → schema)
57
+
58
+ The whole stack is three layers with a strictly one-way dependency. `mm-harness`
59
+ is the MetaMask-facing **front door**; underneath it the runner is a thin
60
+ **adapter layer** over a fat generic **engine** (`@farmslot/recipe-harness`) and
61
+ **schema** (`@farmslot/protocol`). The runner never re-implements engine logic —
62
+ it imports it.
63
+
64
+ ```text
65
+ mm-harness (the one bin — no per-platform binaries) ← front door: one verb vocabulary
66
+ │ launch · logs · debug · fixtures (daily loop) · run/flows/doctor/actions/manifest (prove)
67
+ │ install/verify/cleanup/live (runtime overlay lifecycle)
68
+
69
+ metamask-recipe-runner (MetaMask ADAPTER layer) ← this repo
70
+ │ MetaMask action manifests, metamask.* live adapters, Mobile/Extension
71
+ │ runtime overlay install, runtime health/decision probes
72
+
73
+ @farmslot/recipe-harness (GENERIC engine) ← npm dependency
74
+ │ recipe graph execution, official ui.* transports, RecordingTarget
75
+ │ evidence framework, runtime modules, generic run/flows/validate CLI
76
+
77
+ @farmslot/protocol (GENERIC schema) ← npm dependency
78
+ │ Recipe v1 schema, action-manifest document, artifact contract
79
+
80
+ MetaMask app under test
81
+ │ local debug runtime only; product source should not own runner logic
82
+
83
+ summary.json · trace.json · artifact-manifest.json · screenshots/logs
84
+ ```
85
+
86
+ **Dependency direction is one-way and provable.** The runner *depends on*
87
+ `@farmslot/recipe-harness` + `@farmslot/protocol` (see `package.json`), and
88
+ `runner/src/**` *imports* the generic contracts rather than forking them:
89
+
90
+ - `runner/src/cli.ts` imports `RecipeRunResult` from `@farmslot/recipe-harness`
91
+ and delegates library `flows` to the harness CLI (`importRecipeHarnessCli`).
92
+ - `runner/src/runner.ts` wires `metamask.*` adapters INTO the farmslot
93
+ `RecipeRunner`; it does not implement graph execution.
94
+ - `runner/src/manifest.ts` loads *MetaMask* manifests typed by the *farmslot*
95
+ protocol document type.
96
+ - `runner/src/recording-target.ts` is a MetaMask provider over the farmslot
97
+ `RecordingTarget` contract.
98
+
99
+ **The invariant both repos assert:** farmslot stays MetaMask-free; the runner
100
+ never re-implements engine logic, only binds to it. Decision lens for placing any
101
+ new capability: *would a non-MetaMask project reuse this unchanged?* → yes =
102
+ generic (farmslot); encodes MetaMask app knowledge = runner.
103
+
104
+ > Note on naming: "harness" refers to the tool (`mm-harness`) and the generic
105
+ > engine package (`@farmslot/recipe-harness`). The per-checkout files the tool
106
+ > installs into a target are the **runtime overlay** (`install`/`verify`/
107
+ > `cleanup`/`live`), never "the harness" — see [harness-cli.md](harness-cli.md).
108
+
109
+ ## One-minute model
110
+
111
+ ```text
112
+ human CLI / optional skill wrapper
113
+ │ resolve + invoke
114
+
115
+ metamask-recipe-runner
116
+ │ MetaMask action manifests, live adapters, runtime overlay install, runtime probes
117
+
118
+ Recipe harness package
119
+ │ generic graph execution, official ui.* transports, traces, artifacts
120
+
121
+ Recipe protocol package
122
+ │ Recipe v1 schema, manifest contract, artifact contract
123
+
124
+ MetaMask app under test
125
+ │ local debug runtime only; product source should not own runner logic
126
+
127
+ summary.json · trace.json · artifact-manifest.json · screenshots/logs
128
+ ```
129
+
130
+ Dependency direction is one-way. Wrappers call the runner; the runner calls the
131
+ shared harness/protocol packages; product apps are only driven at runtime.
132
+
133
+ ## Package shape
134
+
135
+ 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.
136
+
137
+ ## The two subsystems
138
+
139
+ This repo has two intentionally different kinds of code. Keeping them separate
140
+ is the main way to understand the repository.
141
+
142
+ | Subsystem | Question it answers | Primary files | Should contain | Should not contain |
143
+ |---|---|---|---|---|
144
+ | Recipe capability/execution | “What can a MetaMask recipe do, and how does a node execute?” | `library/manifests/`, `library/recipes/`, `runner/src/runner.ts`, `runner/src/adapters.ts`, `runner/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 |
145
+ | Runtime lifecycle / sandbox helpers | “How do I give an agent an isolated app session that is ready to inspect or run recipes?” | `bin/mm-recipe`, `bin/mme-recipe`, `orchestration/{mobile,extension,core}/`, `runner/{mobile,extension}/`, `orchestration/lib/` | 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 |
146
+
147
+ When reviewing a change, first decide which subsystem it touches. Recipe changes
148
+ should be validated against manifests and action artifacts. Runtime lifecycle changes should be validated by install/launch/live/verify
149
+ behavior on a real checkout. Some commands cross the boundary, but they should
150
+ do so by delegating: sandbox helpers get the runtime ready, then
151
+ `mm-harness run` executes the recipe.
152
+
153
+ ## Ownership boundaries
154
+
155
+ | Layer | Owns | Must not own |
156
+ |---|---|---|
157
+ | Human CLI / optional wrapper | Target selection, runner invocation, evidence handoff | Copied adapter scripts, recipe graph execution, product runtime logic |
158
+ | 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 |
159
+ | `Recipe harness package` | Recipe graph execution, standard core/ui adapters, trace/summary/artifact writing | MetaMask wallet/Perps behavior |
160
+ | `Recipe protocol package` | Recipe/manifest/artifact schemas | Runtime control or product-specific actions |
161
+ | Product checkout | App code and debug hooks exposed by the app | Harness scripts, runner copy, skills, private workflow logic |
162
+
163
+ Rule of thumb: if code describes **what MetaMask can do**, it belongs here. If it
164
+ describes **how Recipe v1 works**, it belongs in the shared protocol/runtime packages. If it
165
+ describes **how an agent should work**, it belongs in skills.
166
+
167
+ ## Key files and directories
168
+
169
+ | Path | Responsibility |
170
+ |---|---|
171
+ | `bin/metamask-recipe` | Public binary. Dispatches to typed CLI and platform convenience commands. |
172
+ | `bin/mm-recipe` | Mobile convenience/runtime UX: start/reuse Metro, prewarm bundle, launch app, query bridge, setup wallet, screenshot. |
173
+ | `bin/mme-recipe` | Extension convenience/runtime UX: install, health, decision, ready, watch/refresh/reopen, run recipes. |
174
+ | `runner/src/cli.ts` | Typed command handlers: manifests, actions, doctor, runtime health/decision/launch, `run`, self-test. |
175
+ | `runner/src/runner.ts` | Creates the Recipe runner by combining shared core/ui adapters with MetaMask live adapters. Enables the Recipe HUD metadata. |
176
+ | `runner/src/adapters.ts` | MetaMask adapter binding and `ui.*` transport selection for Mobile vs Extension. Refuses static placeholders for live-only proof actions. |
177
+ | `runner/src/live-adapter-contract.ts` | Script adapter contract and lookup rules for `library/actions/<platform>/<domain>/*.mjs`. |
178
+ | `library/manifests/*.action-manifest.json` | Reviewable capability contract. A recipe may only call declared actions. |
179
+ | `library/actions/mobile/` | Mobile action implementations. Talks to the runner bridge and app-exposed `globalThis.__AGENTIC__` hooks. |
180
+ | `library/actions/extension/` | Extension action implementations. Talks to Chrome/extension pages over CDP. |
181
+ | `orchestration/mobile/inject.sh` | Installs/syncs the Mobile runtime overlay under the configured harness root and protects cleanup/git-exclude behavior. |
182
+ | `orchestration/extension/inject.mjs` | Installs/syncs Extension runtime helpers under the configured harness root. |
183
+ | `orchestration/mobile/` + `runner/mobile/` | Runner-owned Mobile launch/live/inject/cleanup and verify helpers copied into installed harnesses. |
184
+ | `orchestration/extension/` + `runner/extension/` | Runner-owned Extension launch/live/watch/windows/wallet-state/readiness helpers (orchestration) and verify (recipe) copied into installed harnesses. |
185
+ | `orchestration/lib/path-defaults.json` | Single source for default `recipeHarnessRoot` and `recipeRuntimeDir`. |
186
+ | `orchestration/lib/harness-path.sh`, `orchestration/lib/recipe-paths.mjs`, `runner/src/paths.ts` | Shell, standalone Node, and TypeScript accessors for those defaults plus validation. |
187
+ | `library/recipes/` | Reusable smoke/action-validation recipes only. Task-specific proof recipes stay task-local. |
188
+ | `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). |
189
+ | `docs/` | Runner architecture, contracts, and operational conventions. |
190
+
191
+ ## Runtime paths and installed harnesses
192
+
193
+ Defaults are centralized in `orchestration/lib/path-defaults.json`:
194
+
195
+ ```json
196
+ {
197
+ "recipeHarnessRoot": "temp/recipe/harness",
198
+ "recipeRuntimeDir": "temp/recipe/runtime"
199
+ }
200
+ ```
201
+
202
+ All shell, standalone Node, and TypeScript code must read these through the
203
+ shared helpers instead of hardcoding defaults. Environment overrides are allowed
204
+ through `RECIPE_HARNESS_ROOT` and `RECIPE_RUNTIME_DIR`, but they must stay safe
205
+ relative paths.
206
+
207
+ Install commands write a small runtime package into the target checkout:
208
+
209
+ ```text
210
+ <target>/<recipeHarnessRoot>/<adapter>/
211
+ manifest.json installed source/revision/cleanup metadata
212
+ action-manifest.json snapshot of the adapter manifest
213
+ runner/bin/metamask-recipe delegate back to the resolved runner source
214
+ runner/recipes/ reusable recipe snapshot
215
+ scripts/ adapter runtime helpers copied from this repo
216
+ ```
217
+
218
+ The installed harness exists so a running slot has stable helper paths even when
219
+ called from skills, orchestration hooks, or a human shell. The source of truth remains
220
+ this runner.
221
+
222
+ ## Recipe execution vs sandbox lifecycle
223
+
224
+ `mm-harness run <recipe.json> --adapter ...` is the recipe path. It creates
225
+ a shared Recipe runner (`runner/src/runner.ts`), validates the recipe against the
226
+ manifest, executes nodes, and writes artifacts. If a bug is about action fields,
227
+ trace output, adapter semantics, or whether a recipe proves a claim, start in
228
+ `library/manifests/`, `runner/src/`, `library/actions/`, and `library/recipes/`.
229
+
230
+ `prepare`, `launch`, `live`, `verify`, `status`, `decision`, and `ready` are
231
+ sandbox lifecycle paths. They give the agent a reproducible local app session:
232
+ Mobile with Metro/dev-client/simulator and the bridge online; Extension with an
233
+ isolated browser profile, unpacked extension loaded, and a known home/popup-style
234
+ UI target. If a bug is about Metro, bundle prewarm, simulator launch, Chrome CDP,
235
+ Extension full-screen vs popup presentation, build freshness, wallet fixture
236
+ placement, git-exclude, or cleanup, start in `bin/mm-recipe`, `bin/mme-recipe`,
237
+ `orchestration/{mobile,extension,core}/` and `runner/{mobile,extension}/`.
238
+
239
+ Do not put recipe graph traversal into shell scripts. Shell scripts may prepare
240
+ or inspect the sandboxed runtime, then delegate graph execution to
241
+ `mm-harness run`.
242
+
243
+ ## HUD vs bridge vs product hooks
244
+
245
+ These names are easy to mix up; they are different concerns.
246
+
247
+ | Term | What it is | Why it exists |
248
+ |---|---|---|
249
+ | 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. |
250
+ | Mobile bridge | Runner-side CDP/Hermes bridge process under `library/actions/mobile/bridge-runtime/`. | Lets the runner call app-exposed commands, read state, press UI targets, and capture status from React Native. |
251
+ | `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. |
252
+ | 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. |
253
+
254
+ The HUD does not control the app. The bridge/CDP control the app. The HUD only
255
+ renders proof context.
256
+
257
+ ## Mobile runtime shape
258
+
259
+ Mobile has the most moving parts because React Native does not expose a browser
260
+ DOM by default.
261
+
262
+ ```text
263
+ mm-recipe / orchestration/mobile/*.sh
264
+ │ starts/reuses Metro, prewarms bundle, launches iOS/Android dev client
265
+
266
+ library/actions/mobile/bridge-runtime/cdp-bridge.cjs
267
+ │ connects to Hermes / RN debug runtime
268
+
269
+ globalThis.__AGENTIC__ inside the app
270
+ │ route/status/wallet/ui commands + optional HUD rendering
271
+
272
+ library/actions/mobile/{wallet,perps,ui,platform}/*.mjs
273
+ ```
274
+
275
+ Ideally the app would expose a product-owned debug automation surface directly,
276
+ so the runner would not need to patch or inject Mobile client files at all. The
277
+ current injection exists as a compatibility bridge: it lets recipes run against
278
+ older Mobile versions, historical PR branches, and eval/replay runs that do not yet include that
279
+ client-side automation surface.
280
+
281
+ The Mobile injection currently may touch development-only product files on older
282
+ checkouts to install the `AgenticService`, navigation hook, and HUD mount. That
283
+ is the fragile compatibility path, not the desired long-term product contract.
284
+ Those product patches are local runtime state: never commit them to MetaMask
285
+ Mobile. The runner also installs ignored helper files under
286
+ `temp/recipe/harness/mobile`.
287
+
288
+ Mobile `ensure_*` actions must be idempotent: if the wallet is already unlocked,
289
+ `metamask.wallet.ensure_unlocked` should report success or converge cheaply, not
290
+ fail because the starting state differed.
291
+
292
+ ## Extension runtime shape
293
+
294
+ Extension does not need an in-product source patch. The runner works through an
295
+ unpacked `dist/chrome` build and Chrome CDP.
296
+
297
+ ```text
298
+ mme-recipe / orchestration/extension/*.sh
299
+ │ checks dist freshness, build health, fixture/profile state
300
+
301
+ Chrome for Testing with --load-extension=<runtime-dist>
302
+ │ CDP target discovery + deterministic extension id
303
+
304
+ library/actions/extension/{wallet,perps,ui,platform}/*.mjs
305
+ │ extension page/background hooks and UI events
306
+ ```
307
+
308
+ `runtime-launch --start-watch` is the clean-build path. Without `--start-watch`,
309
+ verify can prove the existing runtime is reachable but may fail `dist-freshness`
310
+ if `dist/chrome` does not match `HEAD`. That failure is intentional: it prevents
311
+ silently proving stale product code.
312
+
313
+ ## Why there are shell scripts
314
+
315
+ The shell scripts are large because sandbox setup crosses OS/device boundaries:
316
+ `simctl`, `adb`, Metro, Watchman, Chrome process flags, isolated browser
317
+ profiles, git exclude files, symlink safety checks, and cleanup all live outside
318
+ Node's typed domain logic. Their purpose is to give an agent a reliable app
319
+ session, not to define recipe semantics.
320
+
321
+ Allowed in shell:
322
+
323
+ - parsing CLI flags for lifecycle commands;
324
+ - calling OS/device tools;
325
+ - copying/removing installed harness files;
326
+ - starting/stopping/reusing local dev servers;
327
+ - writing small runtime summaries from command results.
328
+
329
+ Not allowed in shell:
330
+
331
+ - Recipe v1 graph execution;
332
+ - MetaMask domain semantics that can live in `runner/src/**/*.ts` or `library/actions/**/*.mjs`;
333
+ - duplicated action manifest logic;
334
+ - product-specific business decisions beyond runtime boot/health checks.
335
+
336
+ When a shell helper starts accumulating domain behavior, move that behavior into
337
+ TypeScript or a focused `.mjs` adapter and keep shell as the launcher.
338
+
339
+ ## Adding or changing capabilities
340
+
341
+ 1. Add/adjust the shared capability in both Mobile and Extension manifests when
342
+ the concept exists on both platforms.
343
+ 2. Implement durable behavior under `library/actions/<platform>/<domain>/`.
344
+ 3. Keep parameterized actions instead of multiplying action names.
345
+ 4. Ensure every `ensure_*` action proves a postcondition.
346
+ 5. Use official `ui.*` actions and screenshot claims for visible acceptance
347
+ criteria; do not add task-specific `metamask.*` actions for exact copy,
348
+ styling, ticket IDs, or one-off selectors.
349
+ 6. Update smoke/action-validation recipes only when the capability is reusable.
350
+
351
+ ## Standalone use first; farm later
352
+
353
+ This runner should make sense without any slot farm:
354
+
355
+ 1. choose a local MetaMask Mobile or Extension checkout;
356
+ 2. run `mm-harness launch <platform>` (or `mm-harness install` for the overlay only);
357
+ 3. run `mm-harness doctor` (or the advanced `runtime-health`);
358
+ 4. run `mm-harness run <recipe.json> --adapter <platform> --artifacts-dir <dir>`.
359
+
360
+ Skills are optional workflow wrappers around that same CLI. A wrapper may resolve
361
+ the runner source and package evidence, but it should dispatch `install`,
362
+ `launch`, `live`, `verify`, and `cleanup` to this repo or to the installed
363
+ harness helper. It should not carry Mobile or Extension adapter scripts.
364
+
365
+ Farm/slot orchestration belongs one layer outside this runner. It can scale the
366
+ standalone loop by choosing checkouts, machines, ports, simulators, and agents,
367
+ but it should not change the runner contract or copy runner implementation.
@@ -0,0 +1,60 @@
1
+ # Extension runtime lifecycle (internal notes)
2
+
3
+ Internal implementation notes for the MetaMask Extension runtime lifecycle — the
4
+ self-healing path that gets a checkout from "installed" to "a live, verified
5
+ extension under CDP control". These are **not** a public command surface: the
6
+ probes below are internal functions, composed for you by the daily-loop commands
7
+ (`launch --verify`) and the readiness commands (`doctor --fix`, `verify`). The
8
+ public surface is defined in [CLI-SPEC.md](CLI-SPEC.md); this file explains what
9
+ happens underneath.
10
+
11
+ The runner is the source of truth for Extension harness injection, readiness
12
+ decisions, extension-id resolution, and live CDP health checks.
13
+
14
+ ## Public entry points
15
+
16
+ - **`install`** writes the Extension runtime overlay under the configured harness
17
+ root. CI/agents run it explicitly; the daily-loop commands auto-ensure it.
18
+ - **`verify`** confirms the overlay is present and healthy — a live CDP health
19
+ probe when a runtime is reachable, no launch.
20
+ - **`launch --verify`** installs the overlay if missing, launches/attaches the
21
+ extension, and polls CDP until the runtime is ready.
22
+ - **`doctor` / `doctor --fix`** report readiness and (with `--fix`) run the
23
+ healing steps below WITHOUT starting the app.
24
+
25
+ ## Internal lifecycle stages
26
+
27
+ The healing path composes these internal functions in order. Each is idempotent
28
+ and reports the cheapest next action, so the composed path converges rather than
29
+ rebuilding from scratch.
30
+
31
+ ### Extension-id resolution
32
+
33
+ Resolves the deterministic unpacked extension id from
34
+ `dist/chrome/manifest.json`, and can verify it against a live CDP target. Every
35
+ later stage keys off this id, so it runs first.
36
+
37
+ ### Readiness decision
38
+
39
+ Inspects the checkout + live browser and returns the cheapest next action:
40
+ `install`, `build`, `relaunch`, or `ready`, plus the concrete `actions[]` to
41
+ execute. The healing path branches on this decision instead of always launching.
42
+
43
+ ### Runtime launch
44
+
45
+ Launches Chrome with the installed harness helper, seeds the wallet fixture, and
46
+ runs live smoke verification. Two tiers: reuse the existing dist (quick relaunch),
47
+ or the clean-build path — clear the webpack cache, start the harness-owned
48
+ watcher, wait for a clean compile, then launch and verify.
49
+
50
+ ### Convergence
51
+
52
+ Converges the live browser to exactly one healthy `home.html` tab (closing
53
+ strays, reopening if needed) and confirms it with the health probe. This is the
54
+ step that makes "launch" deterministic across a messy pre-existing browser state.
55
+
56
+ ### Health probe
57
+
58
+ Read-only liveness probe for the running extension over CDP. Used both as the
59
+ final gate of the convergence step and standalone by `verify` to answer "is the
60
+ runtime healthy right now?" without mutating anything.
@@ -0,0 +1,43 @@
1
+ # `mm-harness` — Command Reference
2
+
3
+ > This page is a pointer. The full, authoritative command contract lives in
4
+ > [CLI-SPEC.md](CLI-SPEC.md); the before→after overview is in
5
+ > [MENTAL-MODEL.md](MENTAL-MODEL.md). This file exists only so links to
6
+ > `harness-cli.md` keep resolving.
7
+
8
+ `mm-harness` is the MetaMask recipe harness — the single front door for launching
9
+ the app, proving behavior, and managing the per-checkout **runtime overlay**. It
10
+ is the only bin: platform (`mobile | extension | core`) is auto-detected from the
11
+ checkout, a positional target forces it (`mm-harness launch ios`), and depth lives
12
+ in flags (`--json`, ports, build tiers), never in extra command names.
13
+
14
+ Naming: **"harness" is the tool** (`mm-harness`) and the generic engine package
15
+ (`@farmslot/recipe-harness`). The files the tool installs into a target checkout
16
+ are the **runtime overlay** — never "the harness".
17
+
18
+ ## Command surface (all shipped)
19
+
20
+ ```
21
+ DAILY LOOP launch · logs · debug · fixtures what a teammate runs many times a day
22
+ DISCOVER actions · call · flows compose recipes from the vocabulary + flow library
23
+ PROVE run · doctor run recipes, check readiness
24
+ RUNTIME OVERLAY install · verify · cleanup install/verify/clean the per-checkout overlay
25
+ ```
26
+
27
+ Grammar: `mm-harness <command> [target] [flags]`. `--json` is the agent contract on
28
+ every command; the bare command is the human happy path.
29
+
30
+ - `run` validates first, then executes; `run --plan` validates and prints the plan
31
+ without touching a device.
32
+ - `call <action>` runs one action from the adapter vocabulary through the same
33
+ engine path `run` uses.
34
+ - `doctor` is read-only; `doctor --fix` runs the self-healing steps (overlay
35
+ auto-ensure; never a fixture reseed) without launching the app and reports
36
+ `fixed[]` / `failed[]`.
37
+ - `flows` lists the recipe library (bare = `flows list`); `flows promote` keeps a
38
+ proven flow.
39
+ - `install` / `verify` / `cleanup` manage the runtime overlay; the DAILY LOOP
40
+ commands auto-ensure it.
41
+
42
+ For flags, exit-code taxonomy, `--json` shapes, and per-command detail, see
43
+ [CLI-SPEC.md](CLI-SPEC.md).
@@ -0,0 +1,188 @@
1
+ # Live Adapter Contract
2
+
3
+ The MetaMask runner implements Recipe Protocol v1 through the shared recipe
4
+ harness package. Project-specific live behavior is supplied by
5
+ manifest-declared action adapters.
6
+
7
+ Official `ui.*` semantics are implemented by
8
+ `createStandardUiAdapters({ transport })` plus shared CDP/React Native base
9
+ transports. The MetaMask runner supplies only tiny runtime bindings that point
10
+ those base transports at the Extension CDP page or Mobile React Native bridge.
11
+ MetaMask-specific actions remain separate manifest-declared live adapters.
12
+
13
+ For actions that must prove real product behavior, especially `metamask.perps.*`, static placeholders are refused by default. A live adapter script must exist in one of these locations. Domain-grouped paths are preferred so code ownership stays obvious:
14
+
15
+ ```text
16
+ $METAMASK_RECIPE_LIVE_ADAPTER_DIR/<platform>/<domain>/<action-local-name>.mjs
17
+ $METAMASK_RECIPE_LIVE_ADAPTER_DIR/shared/<domain>/<action-local-name>.mjs
18
+ <runner>/library/actions/<platform>/<domain>/<action-local-name>.mjs
19
+ <runner>/library/actions/shared/<domain>/<action-local-name>.mjs
20
+ ```
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:
23
+
24
+ ```text
25
+ library/actions/extension/perps/ensure_positions.mjs
26
+ library/actions/extension/perps/close_positions.mjs
27
+ library/actions/extension/perps/close_orders.mjs
28
+ library/actions/mobile/perps/place_order.mjs
29
+ library/actions/mobile/wallet/ensure_unlocked.mjs
30
+ ```
31
+
32
+ The runner invokes the script with one argument: a JSON input file path. The same path is also available as `METAMASK_RECIPE_ADAPTER_INPUT`. The script must write JSON to `outputPath` from the input document, or print JSON to stdout.
33
+
34
+ Input shape:
35
+
36
+ ```json
37
+ {
38
+ "schemaVersion": 1,
39
+ "platform": "mobile|extension",
40
+ "action": "metamask.perps.ensure_positions",
41
+ "node": {},
42
+ "context": {
43
+ "nodeId": "ensure-clean",
44
+ "projectRoot": "/path/to/product",
45
+ "artifactsDir": "/path/to/artifacts"
46
+ },
47
+ "outputPath": "/tmp/.../output.json"
48
+ }
49
+ ```
50
+
51
+ Output shape is action-specific, but must be redacted and suitable for `trace.json`. If the adapter captures evidence files, write them under `context.artifactsDir` and return relative artifact paths for the runner adapter to index in a follow-up implementation.
52
+
53
+ Proof rule: live adapters must drive real supported app/API paths. They must not write directly into UI state, DOM state, React/Redux/MobX state, local storage, controller internals, or any mid-recipe state that fabricates the proof condition.
54
+
55
+ ## Controller/API calls vs visible UI proof
56
+
57
+ Recipe authors should choose the layer based on what must be proven:
58
+
59
+ | Need | Use | Rule |
60
+ |---|---|---|
61
+ | Fast reproducible setup/teardown before or after the proof window | `metamask.*` domain actions such as `start_state`, `ensure_positions`, `close_orders` | May use supported product/controller APIs, but must read/assert the final state. |
62
+ | Read-only state proof | `metamask.*.read_*` or `metamask.*.assert_*` | Must return redacted live state in trace output. |
63
+ | Human-visible acceptance criterion | official `ui.*` actions | Drive the actual visible path: press/tap, input/keypad, scroll into view, screenshot. Do not replace it with a controller call. |
64
+ | Ticket-specific visual detail | task-local recipe assertions/evidence | Do not add a reusable action only for one ticket. |
65
+
66
+ ## Shared action-surface boundaries
67
+
68
+ The manifest is a durable capability contract, not a place to encode one
69
+ ticket's acceptance criteria. Do not add shared `metamask.*` actions for ticket
70
+ IDs, POCs, exact test IDs, exact copy, styling, placement, or other one-off UI
71
+ proof needs.
72
+
73
+ Use:
74
+
75
+ - official `ui.*` actions for reusable presence, input, scroll, and screenshot
76
+ behavior;
77
+ - screenshot `claims` for visual, copy, and layout proof;
78
+ - task-local composed flows under the task artifact directory when a ticket
79
+ needs a reusable helper for that ticket only;
80
+ - safe direct CDP/controller calls for read/assert or supported setup paths,
81
+ never state fabrication.
82
+
83
+ Add or keep a shared action only when it represents a durable parameterized
84
+ domain capability useful across many tasks, such as `metamask.perps.start_state`,
85
+ `metamask.perps.place_order`, `metamask.perps.close_positions`, or
86
+ `metamask.perps.assert_positions`.
87
+
88
+ `ui.scroll` is part of the current executable contract and action-validation must
89
+ prove both normal scrolling and `scroll_into_view` before screenshot capture.
90
+ `ui.gesture` is intentionally not advertised yet; drag/swipe proof must wait
91
+ until the shared runtime and this runner expose and validate that action on both
92
+ platforms.
93
+
94
+
95
+
96
+ ## Flow catalog follow-up
97
+
98
+ Action adapters fulfill one manifest-declared operation. Production recipes should also be able to call domain flow catalogs that compose these operations into idempotent `ensure_*` start states. These flows are owned by this runner/domain layer, not by shared runtime packages or wrapper glue.
99
+
100
+ For Perps, recipes should use the runner-provided `metamask.perps.start_state({ network, provider, page, market, positions, orders })` and `metamask.perps.teardown_state(...)` actions as the default reproducibility boundary. These actions compose primitive bulk operations such as `close_positions({ mode: "all" })` and `close_orders({ mode: "all" })` instead of multiplying one-off cleanup actions.
101
+
102
+ ## Built-in Extension live adapters
103
+
104
+ The first Extension adapter set is bundled under `library/actions/extension/` and talks directly to the Chrome extension page target over CDP. It does not execute a second recipe graph. The v1 runner still owns workflow traversal; each script only fulfills one manifest-declared action.
105
+
106
+ Runtime input:
107
+
108
+ ```text
109
+ node.cdp_port | CDP_PORT | RECIPE_CDP_PORT
110
+ ```
111
+
112
+ Optional autolaunch input for existing Extension builds:
113
+
114
+ ```text
115
+ METAMASK_RECIPE_EXTENSION_AUTOLAUNCH=1
116
+ METAMASK_RECIPE_EXTENSION_LAUNCH_EXISTING_DIST=1
117
+ node.launch_existing_dist=true
118
+ ```
119
+
120
+ When enabled, the adapter reuses the requested CDP port if a compatible extension target exists; otherwise it launches Chrome from the target checkout's existing `dist/chrome` without rebuilding the product. The runtime copy, profile, logs, and `runtime.json` are written under `context.artifactsDir/extension-runtime/`.
121
+
122
+ CLI equivalent:
123
+
124
+ ```bash
125
+ mm-harness run <recipe.json> --adapter extension --cdp-port 6664 --launch-existing-dist --artifacts-dir <dir>
126
+ ```
127
+
128
+ The CLI maps `--cdp-port` to `CDP_PORT`/`RECIPE_CDP_PORT` and maps `--launch-existing-dist` to `METAMASK_RECIPE_EXTENSION_AUTOLAUNCH=1` for the duration of the recipe run.
129
+
130
+ Currently implemented Extension actions:
131
+
132
+ ```text
133
+ ui.navigate # extension: page alias or hash/path/url
134
+ metamask.perps.read_positions
135
+ metamask.perps.read_orders
136
+ metamask.perps.close_positions # primitive bulk close selected positions
137
+ metamask.perps.close_orders # primitive bulk cancel selected orders
138
+ metamask.perps.place_order
139
+ metamask.perps.assert_positions
140
+ metamask.perps.assert_orders
141
+ metamask.perps.ensure_positions # high-level read/close/place/assert wrapper
142
+ metamask.perps.ensure_orders # high-level read/cancel/assert wrapper
143
+ ui.press
144
+ ui.scroll
145
+ ui.wait_for
146
+ ui.screenshot
147
+ ```
148
+
149
+ Navigation supports a small discoverable `page` alias set (`home`, `perps`, `perps-market`) plus raw extension hash/url fallback. Check the action manifest before hardcoding routes.
150
+
151
+ Task-specific UI styling checks, such as one ticket proving a banner color or placement, must not be implemented as reusable `metamask.perps.*` actions. Use official `ui.wait_for` for reusable presence/absence checks plus screenshot/task-local validation evidence for that ticket.
152
+
153
+ Read-only position checks use `stateHooks.submitRequestToBackground('perpsGetPositions', [{ skipCache: true }])`. State-changing actions prefer UI interaction through CDP mouse/keyboard events. Bulk cleanup primitives may call product background APIs such as `perpsClosePositions` or `perpsCancelOrders` when the UI control is unavailable; this is a supported app/API path, not direct state mutation. Adapters must not mutate Redux/React state, DOM state, local storage, or controller internals to fabricate proof.
154
+
155
+ ## Built-in Mobile live adapters
156
+
157
+ The Mobile adapter is bundled under `library/actions/mobile/`. Its CDP bridge, wallet setup, target discovery, and screenshot helpers run from the runner runtime; the Mobile checkout must not own harness control scripts. The app still needs a development-only in-app bridge/HUD overlay when a historical checkout does not expose the required `globalThis.__AGENTIC__` hooks.
158
+
159
+ Runtime input:
160
+
161
+ ```text
162
+ node.watcher_port | node.metro_port | node.cdp_port | WATCHER_PORT | CDP_PORT | RECIPE_CDP_PORT
163
+ node.simulator | node.ios_simulator | IOS_SIMULATOR
164
+ node.android_device | ANDROID_DEVICE
165
+ ```
166
+
167
+ Currently implemented Mobile actions:
168
+
169
+ ```text
170
+ ui.navigate # mobile: page alias or route/screen + params
171
+ metamask.perps.read_positions
172
+ metamask.perps.read_orders
173
+ metamask.perps.close_positions # primitive bulk close selected positions
174
+ metamask.perps.close_orders # primitive bulk cancel selected orders
175
+ metamask.perps.place_order
176
+ metamask.perps.assert_positions
177
+ metamask.perps.assert_orders
178
+ metamask.perps.ensure_positions # high-level read/close/place/assert wrapper
179
+ metamask.perps.ensure_orders # high-level read/cancel/assert wrapper
180
+ ui.press
181
+ ui.scroll
182
+ ui.wait_for
183
+ ui.screenshot
184
+ ```
185
+
186
+ Navigation supports a small discoverable `page` alias set (`home`, `perps`, `perps-market`) plus raw React Navigation route/params fallback. Check the action manifest before hardcoding routes.
187
+
188
+ Read-only position checks use `Engine.context.PerpsController.getPositions()` through Hermes CDP. State-changing Perps actions use supported controller APIs (`placeOrder`, `closePositions`) through the same app bridge rather than mutating Redux/React/local storage. UI actions delegate to existing bridge capabilities such as `press-test-id` and `scroll-view`. Screenshot capture uses `xcrun simctl io <simulator> screenshot` for iOS simulator proof.