@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,422 @@
1
+ {
2
+ "schema_version": 1,
3
+ "title": "MetaMask Mobile action E2E validation",
4
+ "description": "Exercises every Mobile manifest-declared action against mm-4 and leaves trace output for false-positive checks.",
5
+ "validate": {
6
+ "workflow": {
7
+ "entry": "prepare-files",
8
+ "nodes": {
9
+ "prepare-files": {
10
+ "action": "command",
11
+ "cmd": "mkdir -p temp/recipe-action-validation && printf 'E2E_ACTION_VALIDATION mobile\\n' > temp/recipe-action-validation/probe.txt && printf '{\"platform\":\"mobile\",\"ok\":true}\n' > temp/recipe-action-validation/probe.json && printf 'mobile log E2E_ACTION_VALIDATION\\n' > temp/recipe-action-validation/validation.log && echo E2E_ACTION_VALIDATION mobile",
12
+ "next": "assert-command-exit",
13
+ "intent": "Prepare action-validation probe files",
14
+ "detail": "Create local files used to prove command/assert/log/artifact actions.",
15
+ "flow": "setup"
16
+ },
17
+ "assert-command-exit": {
18
+ "action": "assert_exit_code",
19
+ "source": "prepare-files",
20
+ "expected": 0,
21
+ "next": "assert-command-output",
22
+ "intent": "Verify the probe command exited successfully",
23
+ "detail": "Check the previous command exit code.",
24
+ "flow": "setup"
25
+ },
26
+ "assert-command-output": {
27
+ "action": "assert_output",
28
+ "source": "prepare-files",
29
+ "stream": "stdout",
30
+ "contains": "E2E_ACTION_VALIDATION mobile",
31
+ "next": "assert-file",
32
+ "intent": "Verify the probe command produced expected output",
33
+ "detail": "Check stdout from the previous command.",
34
+ "flow": "setup"
35
+ },
36
+ "assert-file": {
37
+ "action": "assert_file",
38
+ "path": "temp/recipe-action-validation/probe.txt",
39
+ "contains": "E2E_ACTION_VALIDATION mobile",
40
+ "next": "assert-json",
41
+ "intent": "Verify file assertions work",
42
+ "detail": "Read the probe text file from the project workspace.",
43
+ "flow": "setup"
44
+ },
45
+ "assert-json": {
46
+ "action": "assert_json",
47
+ "path": "temp/recipe-action-validation/probe.json",
48
+ "assert": {
49
+ "path": "$.platform",
50
+ "operator": "eq",
51
+ "value": "mobile"
52
+ },
53
+ "next": "watch-logs",
54
+ "intent": "Verify JSON assertions work",
55
+ "detail": "Read the probe JSON file and compare $.platform.",
56
+ "flow": "setup"
57
+ },
58
+ "watch-logs": {
59
+ "action": "watch_logs",
60
+ "path": "temp/recipe-action-validation/validation.log",
61
+ "contains": "E2E_ACTION_VALIDATION",
62
+ "next": "index-artifacts",
63
+ "intent": "Verify log watching works",
64
+ "detail": "Scan the probe log for the validation marker.",
65
+ "flow": "setup"
66
+ },
67
+ "index-artifacts": {
68
+ "action": "index_artifacts",
69
+ "artifacts": [
70
+ {
71
+ "path": "temp/recipe-action-validation/probe.txt",
72
+ "type": "log",
73
+ "category": "debug",
74
+ "label": "Mobile action validation probe"
75
+ }
76
+ ],
77
+ "next": "wait",
78
+ "intent": "Register the probe file as an artifact",
79
+ "detail": "Add the validation probe to the run artifact manifest.",
80
+ "flow": "setup"
81
+ },
82
+ "wait": {
83
+ "action": "wait",
84
+ "duration_ms": 1,
85
+ "next": "status",
86
+ "intent": "Verify the base wait action works",
87
+ "detail": "Pause briefly before app/runtime checks.",
88
+ "flow": "setup"
89
+ },
90
+ "status": {
91
+ "action": "app.status",
92
+ "next": "app-hud",
93
+ "intent": "Read project runner compatibility status",
94
+ "detail": "Confirm the app adapter reports its platform and shape.",
95
+ "flow": "setup"
96
+ },
97
+ "cdp-probe": {
98
+ "action": "cdp.target",
99
+ "next": "fixture",
100
+ "required": true,
101
+ "intent": "Verify the automation channel is reachable",
102
+ "detail": "Probe CDP/bridge connectivity before UI actions.",
103
+ "flow": "setup"
104
+ },
105
+ "fixture": {
106
+ "action": "metamask.wallet.fixture_status",
107
+ "next": "wallet-setup",
108
+ "intent": "Verify wallet fixture availability",
109
+ "detail": "Check the preseeded wallet fixture required by validation.",
110
+ "flow": "wallet"
111
+ },
112
+ "wallet-setup": {
113
+ "action": "metamask.wallet.setup",
114
+ "next": "wallet-unlock",
115
+ "intent": "Verify wallet setup is ready",
116
+ "detail": "Confirm onboarding/fixture state before driving the app.",
117
+ "flow": "wallet"
118
+ },
119
+ "wallet-unlock": {
120
+ "action": "metamask.wallet.ensure_unlocked",
121
+ "next": "wallet-read-state",
122
+ "intent": "Ensure the wallet is unlocked",
123
+ "detail": "Unlock only if the app is currently locked.",
124
+ "flow": "wallet"
125
+ },
126
+ "wallet-read-state": {
127
+ "action": "metamask.wallet.read_state",
128
+ "next": "select-account",
129
+ "intent": "Read wallet state for proof",
130
+ "detail": "Capture selected account and network state without mutating UI.",
131
+ "flow": "wallet"
132
+ },
133
+ "select-account": {
134
+ "action": "metamask.wallet.select_account",
135
+ "address": "0x8dc623e964475d4d669da601fd15ea9125469003",
136
+ "next": "app-navigate-wallet",
137
+ "intent": "Select the validation account",
138
+ "detail": "Ensure the run uses the configured deterministic account.",
139
+ "flow": "wallet"
140
+ },
141
+ "app-navigate-wallet": {
142
+ "action": "ui.navigate",
143
+ "next": "ui-wait-wallet",
144
+ "intent": "Navigate to wallet home",
145
+ "detail": "Use the raw React Navigation WalletView route.",
146
+ "flow": "wallet",
147
+ "route": "WalletView"
148
+ },
149
+ "ui-wait-wallet": {
150
+ "action": "ui.wait_for",
151
+ "test_id": "wallet-screen",
152
+ "expected": "present",
153
+ "next": "ui-press",
154
+ "intent": "Wait for wallet UI to be visible",
155
+ "detail": "Use the generic UI wait action.",
156
+ "flow": "ui"
157
+ },
158
+ "ui-press": {
159
+ "action": "ui.press",
160
+ "test_id": "wallet-account-copy-button",
161
+ "next": "ui-scroll",
162
+ "intent": "Verify generic press/tap works",
163
+ "detail": "Drive a harmless UI press through the platform adapter.",
164
+ "flow": "ui"
165
+ },
166
+ "ui-scroll": {
167
+ "action": "ui.scroll",
168
+ "test_id": "wallet-screen",
169
+ "offset": 1000,
170
+ "next": "ui-scroll-into-view",
171
+ "intent": "Verify generic scroll works",
172
+ "detail": "Scroll the visible app surface through the platform adapter.",
173
+ "flow": "ui"
174
+ },
175
+ "ui-screenshot-wallet": {
176
+ "action": "ui.screenshot",
177
+ "path": "screenshots/mobile-wallet-action-validation.png",
178
+ "next": "wallet-navigate-perps",
179
+ "intent": "Capture wallet visual proof",
180
+ "detail": "Save a screenshot artifact after generic UI actions.",
181
+ "flow": "ui"
182
+ },
183
+ "wallet-navigate-perps": {
184
+ "action": "ui.navigate",
185
+ "next": "perps-navigate-home",
186
+ "intent": "Navigate to Perps with a raw app route",
187
+ "detail": "Use the raw React Navigation PerpsMarketListView route.",
188
+ "flow": "ui",
189
+ "route": "PerpsMarketListView"
190
+ },
191
+ "perps-navigate-home": {
192
+ "action": "ui.navigate",
193
+ "next": "perps-navigate-market",
194
+ "intent": "Open Perps home with a raw app route",
195
+ "detail": "Use the raw React Navigation PerpsMarketListView route.",
196
+ "flow": "perps",
197
+ "route": "PerpsMarketListView"
198
+ },
199
+ "perps-navigate-market": {
200
+ "action": "ui.navigate",
201
+ "next": "start-state",
202
+ "intent": "Open the ETH Perps market with raw route params",
203
+ "detail": "Use the raw React Navigation PerpsMarketDetails route and market params.",
204
+ "flow": "perps",
205
+ "route": "PerpsMarketDetails",
206
+ "params": {
207
+ "market": {
208
+ "symbol": "ETH"
209
+ }
210
+ }
211
+ },
212
+ "ensure-clean": {
213
+ "action": "metamask.perps.ensure_positions",
214
+ "market": "ETH",
215
+ "next": "ensure-orders-clean",
216
+ "state": "none",
217
+ "intent": "Ensure no positions are open",
218
+ "detail": "Use the reusable Perps position precondition in selected-market cleanup mode.",
219
+ "flow": "perps setup",
220
+ "mode": "matching"
221
+ },
222
+ "assert-no-position": {
223
+ "action": "metamask.perps.assert_positions",
224
+ "market": "ETH",
225
+ "next": "read-orders-empty",
226
+ "state": "none",
227
+ "intent": "Confirm selected ETH position state is clean",
228
+ "detail": "Assert no position exists before placing a validation order.",
229
+ "flow": "perps setup",
230
+ "mode": "matching"
231
+ },
232
+ "read-orders-empty": {
233
+ "action": "metamask.perps.read_orders",
234
+ "market": "ETH",
235
+ "next": "assert-no-orders",
236
+ "intent": "Read order state after cleanup",
237
+ "detail": "Collect current ETH order state for proof.",
238
+ "flow": "perps setup"
239
+ },
240
+ "place-order": {
241
+ "action": "metamask.perps.place_order",
242
+ "market": "ETH",
243
+ "side": "long",
244
+ "amount": "11",
245
+ "leverage": 3,
246
+ "next": "read-positions",
247
+ "intent": "Place a small ETH 3x long validation order",
248
+ "detail": "Create an approximately $11 real Perps position through the runner action.",
249
+ "flow": "perps validation"
250
+ },
251
+ "read-positions": {
252
+ "action": "metamask.perps.read_positions",
253
+ "market": "ETH",
254
+ "next": "assert-position",
255
+ "intent": "Read ETH position after order placement",
256
+ "detail": "Collect current ETH position state for proof.",
257
+ "flow": "perps validation"
258
+ },
259
+ "assert-position": {
260
+ "action": "metamask.perps.assert_positions",
261
+ "market": "ETH",
262
+ "next": "read-orders-open",
263
+ "state": "open",
264
+ "intent": "Confirm the ETH validation position opened",
265
+ "detail": "Assert the order produced an open ETH position.",
266
+ "flow": "perps validation"
267
+ },
268
+ "read-orders-open": {
269
+ "action": "metamask.perps.read_orders",
270
+ "market": "ETH",
271
+ "next": "close-orders",
272
+ "intent": "Read ETH orders before cleanup",
273
+ "detail": "Collect order state before closing validation artifacts.",
274
+ "flow": "perps validation"
275
+ },
276
+ "assert-closed": {
277
+ "action": "metamask.perps.assert_positions",
278
+ "market": "ETH",
279
+ "next": "teardown-state",
280
+ "state": "none",
281
+ "intent": "Confirm global position cleanup completed",
282
+ "detail": "Assert no selected ETH position remains after cleanup.",
283
+ "flow": "perps teardown",
284
+ "mode": "matching"
285
+ },
286
+ "done": {
287
+ "action": "end",
288
+ "status": "pass",
289
+ "intent": "Finish action validation successfully",
290
+ "detail": "Mark the recipe run as passed after all checks complete.",
291
+ "flow": "teardown"
292
+ },
293
+ "ensure-orders-clean": {
294
+ "action": "metamask.perps.ensure_orders",
295
+ "market": "ETH",
296
+ "state": "none",
297
+ "next": "assert-no-position",
298
+ "intent": "Ensure no orders are open",
299
+ "detail": "Use the reusable Perps order precondition in selected-market cleanup mode.",
300
+ "flow": "perps setup",
301
+ "mode": "matching"
302
+ },
303
+ "assert-no-orders": {
304
+ "action": "metamask.perps.assert_orders",
305
+ "market": "ETH",
306
+ "state": "none",
307
+ "next": "place-order",
308
+ "intent": "Confirm selected ETH order state is clean",
309
+ "detail": "Assert no orders exist before placing a validation order.",
310
+ "flow": "perps setup",
311
+ "mode": "matching"
312
+ },
313
+ "close-orders": {
314
+ "action": "metamask.perps.close_orders",
315
+ "market": "ETH",
316
+ "next": "close-positions",
317
+ "intent": "Close ETH orders created by validation",
318
+ "detail": "Clean up any open ETH orders selected by the recipe parameters.",
319
+ "flow": "perps teardown"
320
+ },
321
+ "close-positions": {
322
+ "action": "metamask.perps.close_positions",
323
+ "market": "ETH",
324
+ "next": "assert-closed",
325
+ "intent": "Close the ETH validation position",
326
+ "detail": "Clean up the ETH position selected by the recipe parameters.",
327
+ "flow": "perps teardown"
328
+ },
329
+ "start-state": {
330
+ "action": "metamask.perps.start_state",
331
+ "profile": "clean_market_testnet",
332
+ "market": "ETH",
333
+ "positions": {
334
+ "state": "none",
335
+ "mode": "matching"
336
+ },
337
+ "orders": {
338
+ "state": "none",
339
+ "mode": "matching"
340
+ },
341
+ "page": "market",
342
+ "next": "ensure-clean",
343
+ "intent": "Prepare selected-market clean testnet Perps validation state",
344
+ "detail": "Close existing ETH testnet positions and orders before validation so unrelated leftovers cannot produce false confidence.",
345
+ "flow": "perps setup",
346
+ "mode": "matching"
347
+ },
348
+ "teardown-state": {
349
+ "action": "metamask.perps.teardown_state",
350
+ "market": "ETH",
351
+ "positions": {
352
+ "state": "none",
353
+ "mode": "matching"
354
+ },
355
+ "orders": {
356
+ "state": "none",
357
+ "mode": "matching"
358
+ },
359
+ "page": "home",
360
+ "next": "done",
361
+ "intent": "Return Perps to a selected-market clean home state",
362
+ "detail": "Close remaining ETH positions and orders so the account ends selected-market clean.",
363
+ "flow": "perps teardown",
364
+ "mode": "matching"
365
+ },
366
+ "app-hud": {
367
+ "action": "app.hud",
368
+ "text": "mobile action parity validation",
369
+ "status": "running",
370
+ "next": "cdp-probe",
371
+ "intent": "Start visible recipe progress HUD",
372
+ "detail": "Show the human reviewer what this validation is doing.",
373
+ "flow": "setup"
374
+ },
375
+ "ui-scroll-into-view": {
376
+ "action": "ui.scroll",
377
+ "test_id": "wallet-screen",
378
+ "scroll_into_view": true,
379
+ "offset": 40,
380
+ "next": "app-navigate-network-selector",
381
+ "intent": "Bring the wallet screen into view for proof",
382
+ "detail": "Exercise the Recipe v1 scroll_into_view contract on React Native.",
383
+ "flow": "ui"
384
+ },
385
+ "app-navigate-network-selector": {
386
+ "action": "ui.press",
387
+ "test_id": "import-token-button",
388
+ "next": "wait-token-search-input",
389
+ "intent": "Open token search input",
390
+ "detail": "Press the wallet Import token button so Recipe v1 can validate generic text input through a real React Native TextInput.",
391
+ "flow": "ui"
392
+ },
393
+ "ui-set-input-network": {
394
+ "action": "ui.set_input",
395
+ "test_id": "input-search-asset",
396
+ "value": "ETH",
397
+ "next": "return-wallet-after-input",
398
+ "intent": "Set a visible token search value",
399
+ "detail": "Exercise ui.set_input on a harmless token search field without changing wallet or Perps state.",
400
+ "flow": "ui"
401
+ },
402
+ "return-wallet-after-input": {
403
+ "action": "ui.navigate",
404
+ "next": "ui-screenshot-wallet",
405
+ "intent": "Return to wallet after input validation",
406
+ "detail": "Use the raw React Navigation WalletView route before continuing generic wallet UI proof.",
407
+ "flow": "ui",
408
+ "route": "WalletView"
409
+ },
410
+ "wait-token-search-input": {
411
+ "action": "ui.wait_for",
412
+ "test_id": "input-search-asset",
413
+ "expected": "present",
414
+ "next": "ui-set-input-network",
415
+ "intent": "Wait for token search input",
416
+ "detail": "Wait until the Import Token search field is mounted before setting text.",
417
+ "flow": "ui"
418
+ }
419
+ }
420
+ }
421
+ }
422
+ }
@@ -0,0 +1,78 @@
1
+ {
2
+ "schema_version": 1,
3
+ "title": "MetaMask core headless Perps order lifecycle",
4
+ "description": "Proves the headless `core` adapter places and cancels a REAL resting Perps LIMIT order on HyperLiquid testnet through the full signing path: a default viem signer backs the perps controller's wallet adapter, the active HyperLiquid testnet provider is initialized, a BTC limit BUY 30% below mid is placed (does not fill — rests on the book), asserted open, then canceled and asserted gone. Leaves the account FLAT — no open orders and no open positions. No CDP, bridge, or UI.",
5
+ "validate": {
6
+ "workflow": {
7
+ "entry": "status",
8
+ "nodes": {
9
+ "status": {
10
+ "action": "app.status",
11
+ "next": "start_flat",
12
+ "intent": "Resolve the core checkout and report headless compatibility mode"
13
+ },
14
+ "start_flat": {
15
+ "action": "metamask.perps.start_state",
16
+ "market": "BTC",
17
+ "positions": { "state": "none" },
18
+ "orders": { "state": "none" },
19
+ "network": "testnet",
20
+ "timeout_ms": 45000,
21
+ "next": "place_order",
22
+ "intent": "Converge BTC testnet baseline to flat (no positions, no orders) before the proof window"
23
+ },
24
+ "place_order": {
25
+ "action": "metamask.perps.place_order",
26
+ "market": "BTC",
27
+ "side": "long",
28
+ "order_type": "limit",
29
+ "offset_pct": -30,
30
+ "notional": "11",
31
+ "leverage": 2,
32
+ "network": "testnet",
33
+ "timeout_ms": 45000,
34
+ "next": "assert_open",
35
+ "intent": "Place a resting BTC limit BUY 30% below mid via the headless controller (real signed limit order to HL testnet; does not fill)"
36
+ },
37
+ "assert_open": {
38
+ "action": "metamask.perps.assert_orders",
39
+ "market": "BTC",
40
+ "state": "open",
41
+ "timeout_ms": 30000,
42
+ "next": "cancel_order",
43
+ "intent": "Assert the BTC resting limit order opened"
44
+ },
45
+ "cancel_order": {
46
+ "action": "metamask.perps.close_orders",
47
+ "market": "BTC",
48
+ "network": "testnet",
49
+ "timeout_ms": 45000,
50
+ "next": "assert_none",
51
+ "intent": "Cancel the BTC resting limit order via the headless controller (real signed cancel to HL testnet)"
52
+ },
53
+ "assert_none": {
54
+ "action": "metamask.perps.assert_orders",
55
+ "market": "BTC",
56
+ "state": "none",
57
+ "timeout_ms": 30000,
58
+ "next": "teardown_flat",
59
+ "intent": "Assert the BTC order canceled — no open orders remain"
60
+ },
61
+ "teardown_flat": {
62
+ "action": "metamask.perps.teardown_state",
63
+ "market": "BTC",
64
+ "positions": { "state": "none" },
65
+ "orders": { "state": "none" },
66
+ "network": "testnet",
67
+ "timeout_ms": 45000,
68
+ "next": "done",
69
+ "intent": "Restore BTC testnet baseline to flat after the proof (leave the shared account clean)"
70
+ },
71
+ "done": {
72
+ "action": "end",
73
+ "status": "pass"
74
+ }
75
+ }
76
+ }
77
+ }
78
+ }