@deeeed/metamask-harness 0.19.1 → 0.21.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 (42) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/adapters/core/inject.sh +1 -3
  3. package/adapters/extension/ensure-browser.sh +6 -4
  4. package/adapters/extension/inject.mjs +1 -1
  5. package/adapters/extension/launch-browser.cjs +6 -1
  6. package/adapters/extension/lib/chrome-args.cjs +11 -1
  7. package/adapters/extension/start-watch.sh +1 -1
  8. package/adapters/extension/verify.sh +17 -0
  9. package/adapters/mobile/inject.sh +1 -1
  10. package/adapters/mobile/open-device.sh +12 -1
  11. package/adapters/mobile/wait-for-bridge.sh +1 -1
  12. package/dist/adapters/extension/ensure-ready.js +7 -2
  13. package/dist/adapters/extension/runtime-decision.js +4 -6
  14. package/dist/adapters/extension/runtime.js +114 -17
  15. package/dist/adapters/mobile/prepare.js +40 -6
  16. package/dist/command-contract.js +58 -5
  17. package/dist/commands/call.js +7 -4
  18. package/dist/commands/check.js +9 -2
  19. package/dist/commands/checklist.js +117 -14
  20. package/dist/commands/launch/extension.js +8 -3
  21. package/dist/commands/launch/index.js +38 -25
  22. package/dist/commands/launch/mobile.js +2 -2
  23. package/dist/commands/manifest.js +86 -25
  24. package/dist/commands/run-engine.js +7 -2
  25. package/dist/heal-bounds.js +1 -1
  26. package/dist/manifest.js +85 -101
  27. package/dist/metamask-action-validation.js +45 -0
  28. package/dist/mm-harness-cli.js +7 -4
  29. package/dist/runner.js +9 -4
  30. package/docs/CONTRIBUTING.md +8 -0
  31. package/docs/RECIPES.md +2 -11
  32. package/library/actions/extension/platform/cdp.mjs +8 -3
  33. package/library/actions/extension/ui/navigate.mjs +239 -16
  34. package/library/actions/mobile/ui/navigate.mjs +1 -1
  35. package/library/manifests/core.action-manifest.json +357 -489
  36. package/library/manifests/extension.action-manifest.json +616 -885
  37. package/library/manifests/mobile.action-manifest.json +647 -931
  38. package/library/recipes/perps/lifecycle.recipe.json +3 -9
  39. package/library/recipes/runner/action-validation.extension.recipe.json +5 -4
  40. package/package.json +5 -4
  41. package/scripts/completions.sh +2 -2
  42. package/library/library.json +0 -7
@@ -1,39 +1,14 @@
1
1
  {
2
- "runner_protocol_version": 1,
3
- "action_registry_version": 1,
4
- "supported_official_actions": [
5
- "command",
6
- "wait",
7
- "assert_file",
8
- "assert_json",
9
- "assert_exit_code",
10
- "assert_output",
11
- "watch_logs",
12
- "index_artifacts",
13
- "end",
14
- "call",
15
- "switch",
16
- "ui.navigate",
17
- "ui.press",
18
- "ui.key_press",
19
- "ui.set_input",
20
- "ui.scroll",
21
- "ui.wait_for",
22
- "ui.screenshot",
23
- "app.status",
24
- "app.hud",
25
- "cdp.target"
26
- ],
27
- "action_metadata": {
2
+ "$schema": "https://farmslot.io/schemas/action-manifest-v1.schema.json",
3
+ "actions": {
28
4
  "command": {
29
5
  "description": "Run a shell command from the project root.",
30
6
  "examples": [
31
7
  {
32
- "node": {
33
- "action": "command",
34
- "cmd": "pwd",
35
- "intent": "Run a shell command from the project root"
36
- }
8
+ "action": "command",
9
+ "cmd": "pwd",
10
+ "intent": "Prepare the project state needed for this proof.",
11
+ "next": "done"
37
12
  }
38
13
  ],
39
14
  "schema": {
@@ -62,11 +37,10 @@
62
37
  "description": "Base wait action.",
63
38
  "examples": [
64
39
  {
65
- "node": {
66
- "action": "wait",
67
- "duration_ms": 1,
68
- "intent": "Base wait action"
69
- }
40
+ "action": "wait",
41
+ "duration_ms": 1,
42
+ "intent": "Allow the requested asynchronous state to settle.",
43
+ "next": "done"
70
44
  }
71
45
  ],
72
46
  "schema": {
@@ -82,40 +56,229 @@
82
56
  "additionalProperties": false
83
57
  }
84
58
  },
85
- "switch": {
86
- "description": "Choose a graph branch when one resolved string equals another.",
59
+ "assert_file": {
60
+ "description": "Assert that a project file exists and optionally contains text.",
61
+ "examples": [
62
+ {
63
+ "action": "assert_file",
64
+ "path": "reports/result.txt",
65
+ "contains": "ok",
66
+ "intent": "Confirm the requested project file state.",
67
+ "next": "done"
68
+ }
69
+ ],
70
+ "schema": {
71
+ "type": "object",
72
+ "properties": {
73
+ "path": {
74
+ "type": "string"
75
+ },
76
+ "contains": {
77
+ "type": "string"
78
+ }
79
+ },
80
+ "required": [
81
+ "path"
82
+ ],
83
+ "additionalProperties": false
84
+ }
85
+ },
86
+ "assert_json": {
87
+ "description": "Assert a JSON file value with a JSONPath-style selector.",
88
+ "examples": [
89
+ {
90
+ "action": "assert_json",
91
+ "path": "reports/result.json",
92
+ "assert": {
93
+ "path": "$.status",
94
+ "operator": "eq",
95
+ "value": "ok"
96
+ },
97
+ "intent": "Confirm the recorded data satisfies the requested condition.",
98
+ "next": "done"
99
+ }
100
+ ],
101
+ "schema": {
102
+ "type": "object",
103
+ "properties": {
104
+ "path": {
105
+ "type": "string"
106
+ },
107
+ "assert": {
108
+ "type": "object",
109
+ "properties": {
110
+ "path": {
111
+ "type": "string"
112
+ },
113
+ "operator": {
114
+ "type": "string"
115
+ },
116
+ "value": {
117
+ "type": [
118
+ "string",
119
+ "number",
120
+ "integer",
121
+ "boolean",
122
+ "object",
123
+ "array"
124
+ ],
125
+ "items": {
126
+ "type": [
127
+ "string",
128
+ "number",
129
+ "integer",
130
+ "boolean",
131
+ "object"
132
+ ]
133
+ }
134
+ },
135
+ "all": {
136
+ "type": "array",
137
+ "items": {
138
+ "type": "object"
139
+ }
140
+ },
141
+ "any": {
142
+ "type": "array",
143
+ "items": {
144
+ "type": "object"
145
+ }
146
+ },
147
+ "none": {
148
+ "type": "array",
149
+ "items": {
150
+ "type": "object"
151
+ }
152
+ }
153
+ },
154
+ "additionalProperties": false
155
+ }
156
+ },
157
+ "required": [
158
+ "path",
159
+ "assert"
160
+ ],
161
+ "additionalProperties": false
162
+ }
163
+ },
164
+ "assert_exit_code": {
165
+ "description": "Assert the exit code captured from a command node output.",
87
166
  "examples": [
88
167
  {
168
+ "action": "assert_exit_code",
169
+ "source": "run-tests",
170
+ "expected": 0,
171
+ "intent": "Confirm the project command completed with the expected result.",
172
+ "next": "done"
173
+ }
174
+ ],
175
+ "schema": {
176
+ "type": "object",
177
+ "properties": {
178
+ "source": {
179
+ "type": "string"
180
+ },
89
181
  "node": {
90
- "action": "switch",
91
- "value": "{{params.mode}}",
92
- "equals": "background_resume",
93
- "cases": { "match": "background" },
94
- "default": "warm",
95
- "intent": "Choose the requested lifecycle preparation path"
182
+ "type": "string"
183
+ },
184
+ "expected": {
185
+ "type": "number"
96
186
  }
187
+ },
188
+ "additionalProperties": false
189
+ }
190
+ },
191
+ "assert_output": {
192
+ "description": "Assert stdout or stderr captured from a command node output.",
193
+ "examples": [
194
+ {
195
+ "action": "assert_output",
196
+ "source": "run-tests",
197
+ "stream": "stdout",
198
+ "contains": "PASS",
199
+ "intent": "Confirm the recorded output satisfies the requested condition.",
200
+ "next": "done"
97
201
  }
98
202
  ],
99
203
  "schema": {
100
204
  "type": "object",
101
205
  "properties": {
102
- "value": { "type": "string" },
103
- "equals": { "type": "string" }
206
+ "source": {
207
+ "type": "string"
208
+ },
209
+ "node": {
210
+ "type": "string"
211
+ },
212
+ "stream": {
213
+ "type": "string"
214
+ },
215
+ "contains": {
216
+ "type": "string"
217
+ },
218
+ "match": {
219
+ "type": "string"
220
+ },
221
+ "assert": {
222
+ "type": "object",
223
+ "properties": {
224
+ "path": {
225
+ "type": "string"
226
+ },
227
+ "operator": {
228
+ "type": "string"
229
+ },
230
+ "value": {
231
+ "type": [
232
+ "string",
233
+ "number",
234
+ "integer",
235
+ "boolean",
236
+ "object",
237
+ "array"
238
+ ],
239
+ "items": {
240
+ "type": [
241
+ "string",
242
+ "number",
243
+ "integer",
244
+ "boolean",
245
+ "object"
246
+ ]
247
+ }
248
+ },
249
+ "all": {
250
+ "type": "array",
251
+ "items": {
252
+ "type": "object"
253
+ }
254
+ },
255
+ "any": {
256
+ "type": "array",
257
+ "items": {
258
+ "type": "object"
259
+ }
260
+ },
261
+ "none": {
262
+ "type": "array",
263
+ "items": {
264
+ "type": "object"
265
+ }
266
+ }
267
+ },
268
+ "additionalProperties": false
269
+ }
104
270
  },
105
- "required": ["value", "equals"],
106
271
  "additionalProperties": false
107
- },
108
- "result_cases": ["match"]
272
+ }
109
273
  },
110
274
  "watch_logs": {
111
275
  "description": "Base log assertion/capture action.",
112
276
  "examples": [
113
277
  {
114
- "node": {
115
- "action": "watch_logs",
116
- "path": "logs/app.log",
117
- "intent": "Base log assertion/capture action"
118
- }
278
+ "action": "watch_logs",
279
+ "path": "logs/app.log",
280
+ "intent": "Observe the runtime behavior needed for this proof.",
281
+ "next": "done"
119
282
  }
120
283
  ],
121
284
  "schema": {
@@ -145,18 +308,17 @@
145
308
  "description": "Index extra recipe-authored artifacts that already exist on disk. Do not use after ui.screenshot; screenshots are registered by the runner.",
146
309
  "examples": [
147
310
  {
148
- "node": {
149
- "action": "index_artifacts",
150
- "artifacts": [
151
- {
152
- "path": "reports/result.json",
153
- "type": "report",
154
- "category": "validation",
155
- "label": "Validation report"
156
- }
157
- ],
158
- "intent": "Register a pre-existing report artifact"
159
- }
311
+ "action": "index_artifacts",
312
+ "artifacts": [
313
+ {
314
+ "path": "reports/result.json",
315
+ "type": "report",
316
+ "category": "validation",
317
+ "label": "Validation report"
318
+ }
319
+ ],
320
+ "intent": "Preserve generated evidence in the review package.",
321
+ "next": "done"
160
322
  }
161
323
  ],
162
324
  "schema": {
@@ -212,15 +374,58 @@
212
374
  "description": "Base terminal action.",
213
375
  "examples": [
214
376
  {
215
- "node": {
216
- "action": "end",
217
- "status": "pass"
218
- }
377
+ "action": "end",
378
+ "status": "pass"
379
+ }
380
+ ]
381
+ },
382
+ "call": {
383
+ "description": "Invoke a named recipe by ref and run its steps inline.",
384
+ "examples": [
385
+ {
386
+ "action": "call",
387
+ "ref": "mydev.open_perps_setup",
388
+ "intent": "Run a personal Perps setup recipe",
389
+ "next": "done"
390
+ }
391
+ ]
392
+ },
393
+ "switch": {
394
+ "description": "Choose a graph branch when one resolved string equals another.",
395
+ "examples": [
396
+ {
397
+ "action": "switch",
398
+ "value": "{{params.mode}}",
399
+ "equals": "background_resume",
400
+ "cases": {
401
+ "match": "background"
402
+ },
403
+ "default": "warm",
404
+ "intent": "Choose the graph path matching the observed state."
219
405
  }
406
+ ],
407
+ "schema": {
408
+ "type": "object",
409
+ "properties": {
410
+ "value": {
411
+ "type": "string"
412
+ },
413
+ "equals": {
414
+ "type": "string"
415
+ }
416
+ },
417
+ "required": [
418
+ "value",
419
+ "equals"
420
+ ],
421
+ "additionalProperties": false
422
+ },
423
+ "result_cases": [
424
+ "match"
220
425
  ]
221
426
  },
222
427
  "ui.navigate": {
223
- "description": "Navigate with either raw extension url/hash/path or a small stable page alias. Discover supported aliases here before hardcoding routes.",
428
+ "description": "Navigate by semantic page intent when available; the adapter uses the current visible UI or a verified route. Raw url/hash/path values must be verified against the current app first.",
224
429
  "schema": {
225
430
  "type": "object",
226
431
  "properties": {
@@ -257,36 +462,32 @@
257
462
  "type": "object"
258
463
  },
259
464
  "timeout_ms": {
260
- "type": "number"
465
+ "type": "number",
466
+ "default": 20000,
467
+ "description": "Maximum time to discover and verify the requested page intent."
261
468
  }
262
469
  },
263
470
  "additionalProperties": false
264
471
  },
265
472
  "examples": [
266
473
  {
267
- "description": "Open Perps through the stable alias",
268
- "node": {
269
- "action": "ui.navigate",
270
- "page": "perps",
271
- "intent": "Open Perps"
272
- }
474
+ "action": "ui.navigate",
475
+ "page": "perps",
476
+ "intent": "Open the requested application surface.",
477
+ "next": "done"
273
478
  },
274
479
  {
275
- "description": "Open a Perps market through the stable alias",
276
- "node": {
277
- "action": "ui.navigate",
278
- "page": "perps-market",
279
- "market": "ETH",
280
- "intent": "Open ETH Perps market"
281
- }
480
+ "action": "ui.navigate",
481
+ "page": "perps-market",
482
+ "market": "ETH",
483
+ "intent": "Open the requested application surface.",
484
+ "next": "done"
282
485
  },
283
486
  {
284
- "description": "Fallback to a raw extension hash when no alias exists",
285
- "node": {
286
- "action": "ui.navigate",
287
- "hash": "#/perps-home",
288
- "intent": "Open a raw extension route"
289
- }
487
+ "action": "ui.navigate",
488
+ "hash": "#/perps-home",
489
+ "intent": "Open the requested application surface.",
490
+ "next": "done"
290
491
  }
291
492
  ]
292
493
  },
@@ -294,11 +495,10 @@
294
495
  "description": "Press/tap/click a UI target through the platform adapter.",
295
496
  "examples": [
296
497
  {
297
- "node": {
298
- "action": "ui.press",
299
- "intent": "Press/tap/click a UI target through the platform adapter",
300
- "text": "Perps"
301
- }
498
+ "action": "ui.press",
499
+ "intent": "Activate the requested visible control.",
500
+ "text": "Perps",
501
+ "next": "done"
302
502
  }
303
503
  ],
304
504
  "schema": {
@@ -329,27 +529,95 @@
329
529
  "additionalProperties": false
330
530
  }
331
531
  },
532
+ "ui.key_press": {
533
+ "description": "Send a trusted keyboard press to the currently focused UI element through the UI transport.",
534
+ "schema": {
535
+ "type": "object",
536
+ "properties": {
537
+ "key": {
538
+ "type": "string"
539
+ },
540
+ "timeout_ms": {
541
+ "type": "number"
542
+ },
543
+ "settle": {
544
+ "type": "boolean"
545
+ }
546
+ },
547
+ "required": [
548
+ "key"
549
+ ],
550
+ "additionalProperties": false
551
+ },
552
+ "examples": [
553
+ {
554
+ "action": "ui.key_press",
555
+ "key": "Enter",
556
+ "intent": "Continue the requested keyboard interaction.",
557
+ "next": "done"
558
+ }
559
+ ]
560
+ },
561
+ "ui.set_input": {
562
+ "description": "Set text on an extension DOM input through the CDP UI transport.",
563
+ "schema": {
564
+ "type": "object",
565
+ "properties": {
566
+ "selector": {
567
+ "type": "string"
568
+ },
569
+ "test_id": {
570
+ "type": "string"
571
+ },
572
+ "testID": {
573
+ "type": "string"
574
+ },
575
+ "value": {
576
+ "type": [
577
+ "string",
578
+ "number"
579
+ ],
580
+ "description": "Text or a typed numeric recipe parameter; numbers are entered as their decimal text."
581
+ },
582
+ "text": {
583
+ "type": "string"
584
+ },
585
+ "timeout_ms": {
586
+ "type": "number"
587
+ },
588
+ "settle": {
589
+ "type": "boolean"
590
+ }
591
+ },
592
+ "additionalProperties": false
593
+ },
594
+ "examples": [
595
+ {
596
+ "action": "ui.set_input",
597
+ "selector": "[data-testid=multichain-account-list-search] input",
598
+ "value": "Account",
599
+ "intent": "Provide the requested value in the visible form.",
600
+ "next": "done"
601
+ }
602
+ ]
603
+ },
332
604
  "ui.scroll": {
333
605
  "description": "Scroll through the UI through the Farmslot base platform adapter. Supports deltas and scroll_into_view for selector/test-id proof positioning.",
334
606
  "examples": [
335
607
  {
336
- "node": {
337
- "action": "ui.scroll",
338
- "delta_y": 300,
339
- "intent": "Scroll through the UI through the Farmslot base platform adapter. Supports deltas and scroll_into_view for selector/test-id proof positioning"
340
- }
608
+ "action": "ui.scroll",
609
+ "delta_y": 300,
610
+ "intent": "Expose the requested content for visible review.",
611
+ "next": "done"
341
612
  },
342
613
  {
343
- "description": "Bring a web selector into view before screenshot proof",
344
- "node": {
345
- "action": "ui.scroll",
346
- "selector": "[data-testid=\"example\"]",
347
- "scroll_into_view": true,
348
- "intent": "Bring a web selector into view before screenshot proof"
349
- }
614
+ "action": "ui.scroll",
615
+ "selector": "[data-testid=\"example\"]",
616
+ "scroll_into_view": true,
617
+ "intent": "Expose the requested content for visible review.",
618
+ "next": "done"
350
619
  }
351
620
  ],
352
- "proof_effect": "E2E validation must include a passing ui-scroll-into-view trace event with output.intoView=true.",
353
621
  "schema": {
354
622
  "type": "object",
355
623
  "properties": {
@@ -433,20 +701,18 @@
433
701
  },
434
702
  "examples": [
435
703
  {
436
- "node": {
437
- "action": "ui.wait_for",
438
- "text": "Perps",
439
- "intent": "Wait for visible Extension text"
440
- }
704
+ "action": "ui.wait_for",
705
+ "text": "Perps",
706
+ "intent": "Confirm the requested application state is visible.",
707
+ "next": "done"
441
708
  },
442
709
  {
443
- "node": {
444
- "action": "ui.wait_for",
445
- "selector": "[data-testid=example]",
446
- "visible": false,
447
- "timeout_ms": 2000,
448
- "intent": "Prove an Extension element is absent"
449
- }
710
+ "action": "ui.wait_for",
711
+ "selector": "[data-testid=example]",
712
+ "visible": false,
713
+ "timeout_ms": 2000,
714
+ "intent": "Confirm the requested application state is visible.",
715
+ "next": "done"
450
716
  }
451
717
  ]
452
718
  },
@@ -454,11 +720,10 @@
454
720
  "description": "Capture registered visual evidence through capture-helper, bounded Chrome-native capture, or an explicitly labeled computed-style DOM raster fallback. Always read the resulting PNG; artifact metadata records the provider.",
455
721
  "examples": [
456
722
  {
457
- "node": {
458
- "action": "ui.screenshot",
459
- "path": "screenshots/example.png",
460
- "intent": "Capture visual evidence after a wait/assert step"
461
- }
723
+ "action": "ui.screenshot",
724
+ "path": "screenshots/example.png",
725
+ "intent": "Preserve the visible application state as review evidence.",
726
+ "next": "done"
462
727
  }
463
728
  ],
464
729
  "schema": {
@@ -471,251 +736,44 @@
471
736
  "type": "string"
472
737
  },
473
738
  "category": {
474
- "type": "string"
475
- },
476
- "timeout_ms": {
477
- "type": "number"
478
- }
479
- },
480
- "additionalProperties": false
481
- }
482
- },
483
- "assert_file": {
484
- "description": "Assert that a project file exists and optionally contains text.",
485
- "examples": [
486
- {
487
- "node": {
488
- "action": "assert_file",
489
- "path": "reports/result.txt",
490
- "contains": "ok",
491
- "intent": "Assert that a project file exists and optionally contains text"
492
- }
493
- }
494
- ],
495
- "schema": {
496
- "type": "object",
497
- "properties": {
498
- "path": {
499
- "type": "string"
500
- },
501
- "contains": {
502
- "type": "string"
503
- }
504
- },
505
- "required": [
506
- "path"
507
- ],
508
- "additionalProperties": false
509
- }
510
- },
511
- "assert_json": {
512
- "description": "Assert a JSON file value with a JSONPath-style selector.",
513
- "examples": [
514
- {
515
- "node": {
516
- "action": "assert_json",
517
- "path": "reports/result.json",
518
- "assert": {
519
- "path": "$.status",
520
- "operator": "eq",
521
- "value": "ok"
522
- },
523
- "intent": "Assert a JSON file value with a JSONPath-style selector"
524
- }
525
- }
526
- ],
527
- "schema": {
528
- "type": "object",
529
- "properties": {
530
- "path": {
531
- "type": "string"
532
- },
533
- "assert": {
534
- "type": "object",
535
- "properties": {
536
- "path": {
537
- "type": "string"
538
- },
539
- "operator": {
540
- "type": "string"
541
- },
542
- "value": {
543
- "type": [
544
- "string",
545
- "number",
546
- "integer",
547
- "boolean",
548
- "object",
549
- "array"
550
- ],
551
- "items": {
552
- "type": [
553
- "string",
554
- "number",
555
- "integer",
556
- "boolean",
557
- "object"
558
- ]
559
- }
560
- },
561
- "all": {
562
- "type": "array",
563
- "items": {
564
- "type": "object"
565
- }
566
- },
567
- "any": {
568
- "type": "array",
569
- "items": {
570
- "type": "object"
571
- }
572
- },
573
- "none": {
574
- "type": "array",
575
- "items": {
576
- "type": "object"
577
- }
578
- }
579
- },
580
- "additionalProperties": false
581
- }
582
- },
583
- "required": [
584
- "path",
585
- "assert"
586
- ],
587
- "additionalProperties": false
588
- }
589
- },
590
- "assert_exit_code": {
591
- "description": "Assert the exit code captured from a command node output.",
592
- "examples": [
593
- {
594
- "node": {
595
- "action": "assert_exit_code",
596
- "source": "run-tests",
597
- "expected": 0,
598
- "intent": "Assert the exit code captured from a command node output"
599
- }
600
- }
601
- ],
602
- "schema": {
603
- "type": "object",
604
- "properties": {
605
- "source": {
606
- "type": "string"
607
- },
608
- "node": {
609
- "type": "string"
610
- },
611
- "expected": {
612
- "type": "number"
613
- }
614
- },
615
- "additionalProperties": false
616
- }
617
- },
618
- "assert_output": {
619
- "description": "Assert stdout or stderr captured from a command node output.",
620
- "examples": [
621
- {
622
- "node": {
623
- "action": "assert_output",
624
- "source": "run-tests",
625
- "stream": "stdout",
626
- "contains": "PASS",
627
- "intent": "Assert stdout or stderr captured from a command node output"
628
- }
629
- }
630
- ],
631
- "schema": {
632
- "type": "object",
633
- "properties": {
634
- "source": {
635
- "type": "string"
636
- },
637
- "node": {
638
- "type": "string"
639
- },
640
- "stream": {
641
- "type": "string"
642
- },
643
- "contains": {
644
- "type": "string"
645
- },
646
- "match": {
647
- "type": "string"
648
- },
649
- "assert": {
650
- "type": "object",
651
- "properties": {
652
- "path": {
653
- "type": "string"
654
- },
655
- "operator": {
656
- "type": "string"
657
- },
658
- "value": {
659
- "type": [
660
- "string",
661
- "number",
662
- "integer",
663
- "boolean",
664
- "object",
665
- "array"
666
- ],
667
- "items": {
668
- "type": [
669
- "string",
670
- "number",
671
- "integer",
672
- "boolean",
673
- "object"
674
- ]
675
- }
676
- },
677
- "all": {
678
- "type": "array",
679
- "items": {
680
- "type": "object"
681
- }
682
- },
683
- "any": {
684
- "type": "array",
685
- "items": {
686
- "type": "object"
687
- }
688
- },
689
- "none": {
690
- "type": "array",
691
- "items": {
692
- "type": "object"
693
- }
694
- }
695
- },
696
- "additionalProperties": false
739
+ "type": "string"
740
+ },
741
+ "timeout_ms": {
742
+ "type": "number"
697
743
  }
698
744
  },
699
745
  "additionalProperties": false
700
746
  }
701
747
  },
748
+ "app.status": {
749
+ "description": "Report the adapter's static status — platform, project root, resolved checkout shape, and headless compatibility mode (no live route or account).",
750
+ "examples": [
751
+ {
752
+ "action": "app.status",
753
+ "intent": "Record the resolved application state before continuing.",
754
+ "next": "done"
755
+ }
756
+ ],
757
+ "schema": {
758
+ "type": "object",
759
+ "properties": {},
760
+ "additionalProperties": false
761
+ }
762
+ },
702
763
  "app.hud": {
703
764
  "description": "Display minimal human-readable recipe progress. The intent must explain the current agent goal; debug metadata and grouping labels are hidden unless explicitly opted in.",
704
765
  "examples": [
705
766
  {
706
- "description": "Default one-intent progress HUD",
707
- "node": {
708
- "action": "app.hud",
709
- "status": "running",
710
- "intent": "Open a small ETH long position",
711
- "progress": {
712
- "current": 12,
713
- "total": 19
714
- }
715
- }
767
+ "action": "app.hud",
768
+ "status": "running",
769
+ "intent": "Show the current goal to the supervising human.",
770
+ "progress": {
771
+ "current": 12,
772
+ "total": 19
773
+ },
774
+ "next": "done"
716
775
  }
717
776
  ],
718
- "safety_notes": "HUD is reviewer feedback only. It must not mutate product state or carry task assertions; trace/artifacts remain authoritative.",
719
777
  "schema": {
720
778
  "type": "object",
721
779
  "properties": {
@@ -767,106 +825,15 @@
767
825
  "additionalProperties": false
768
826
  }
769
827
  },
770
- "ui.set_input": {
771
- "description": "Set text on an extension DOM input through the CDP UI transport.",
772
- "schema": {
773
- "type": "object",
774
- "properties": {
775
- "selector": {
776
- "type": "string"
777
- },
778
- "test_id": {
779
- "type": "string"
780
- },
781
- "testID": {
782
- "type": "string"
783
- },
784
- "value": {
785
- "type": [
786
- "string",
787
- "number"
788
- ],
789
- "description": "Text or a typed numeric recipe parameter; numbers are entered as their decimal text."
790
- },
791
- "text": {
792
- "type": "string"
793
- },
794
- "timeout_ms": {
795
- "type": "number"
796
- },
797
- "settle": {
798
- "type": "boolean"
799
- }
800
- },
801
- "additionalProperties": false
802
- },
803
- "examples": [
804
- {
805
- "node": {
806
- "action": "ui.set_input",
807
- "selector": "[data-testid=multichain-account-list-search] input",
808
- "value": "Account",
809
- "intent": "Set text on an extension DOM input through the CDP UI transport"
810
- }
811
- }
812
- ]
813
- },
814
- "ui.key_press": {
815
- "description": "Send a trusted keyboard press to the currently focused UI element through the UI transport.",
816
- "schema": {
817
- "type": "object",
818
- "properties": {
819
- "key": {
820
- "type": "string"
821
- },
822
- "timeout_ms": {
823
- "type": "number"
824
- },
825
- "settle": {
826
- "type": "boolean"
827
- }
828
- },
829
- "required": [
830
- "key"
831
- ],
832
- "additionalProperties": false
833
- },
834
- "examples": [
835
- {
836
- "node": {
837
- "action": "ui.key_press",
838
- "key": "Enter",
839
- "intent": "Send a trusted Enter key press to the focused UI element"
840
- }
841
- }
842
- ]
843
- },
844
- "app.status": {
845
- "description": "Report the adapter's static status — platform, project root, resolved checkout shape, and headless compatibility mode (no live route or account).",
846
- "examples": [
847
- {
848
- "node": {
849
- "action": "app.status",
850
- "intent": "Report the resolved checkout status"
851
- }
852
- }
853
- ],
854
- "schema": {
855
- "type": "object",
856
- "properties": {},
857
- "additionalProperties": false
858
- }
859
- },
860
828
  "cdp.target": {
861
829
  "description": "Probe the platform debug transport — Chrome CDP for extension, the React Native debug bridge for mobile — and report whether it is reachable.",
862
830
  "examples": [
863
831
  {
864
- "node": {
865
- "action": "cdp.target",
866
- "cdp_port": 9323,
867
- "required": true,
868
- "intent": "Confirm the extension CDP endpoint is reachable"
869
- }
832
+ "action": "cdp.target",
833
+ "cdp_port": 9323,
834
+ "required": true,
835
+ "intent": "Confirm the requested application target is available.",
836
+ "next": "done"
870
837
  }
871
838
  ],
872
839
  "schema": {
@@ -900,23 +867,7 @@
900
867
  "additionalProperties": false
901
868
  }
902
869
  },
903
- "call": {
904
- "description": "Invoke a named recipe by ref and run its steps inline.",
905
- "examples": [
906
- {
907
- "node": {
908
- "action": "call",
909
- "ref": "mydev.open_perps_setup",
910
- "intent": "Run a personal Perps setup recipe"
911
- }
912
- }
913
- ]
914
- }
915
- },
916
- "custom_actions": [
917
- {
918
- "name": "metamask.wallet.fixture_status",
919
- "owner": "metamask",
870
+ "metamask.wallet.fixture_status": {
920
871
  "description": "Report the on-disk wallet fixture status (accounts and password presence) without launching or mutating the app.",
921
872
  "schema": {
922
873
  "type": "object",
@@ -925,20 +876,14 @@
925
876
  },
926
877
  "examples": [
927
878
  {
928
- "description": "Minimal node",
929
- "node": {
930
- "action": "metamask.wallet.fixture_status",
931
- "intent": "Check wallet fixture status"
932
- }
879
+ "action": "metamask.wallet.fixture_status",
880
+ "intent": "Check wallet fixture status",
881
+ "next": "done"
933
882
  }
934
883
  ],
935
- "proof_effect": "E2E validation must record this action in trace.json; live-proof actions include liveAdapter/proofPath or target runtime output.",
936
- "safety_notes": "Must not inject mid-recipe UI/app state to fabricate proof.",
937
884
  "execution_capabilities": []
938
885
  },
939
- {
940
- "name": "metamask.wallet.setup",
941
- "owner": "metamask",
886
+ "metamask.wallet.setup": {
942
887
  "description": "Import the wallet fixture accounts and password so the app starts from a known signed-in state.",
943
888
  "schema": {
944
889
  "type": "object",
@@ -947,22 +892,16 @@
947
892
  },
948
893
  "examples": [
949
894
  {
950
- "description": "Minimal node",
951
- "node": {
952
- "action": "metamask.wallet.setup",
953
- "intent": "Prepare the wallet fixture for recipe execution"
954
- }
895
+ "action": "metamask.wallet.setup",
896
+ "intent": "Prepare the wallet fixture for recipe execution",
897
+ "next": "done"
955
898
  }
956
899
  ],
957
- "proof_effect": "E2E validation must record this action in trace.json; live-proof actions include liveAdapter/proofPath or target runtime output.",
958
- "safety_notes": "Must not inject mid-recipe UI/app state to fabricate proof.",
959
900
  "execution_capabilities": [
960
901
  "app-mutation"
961
902
  ]
962
903
  },
963
- {
964
- "name": "metamask.wallet.ensure_unlocked",
965
- "owner": "metamask",
904
+ "metamask.wallet.ensure_unlocked": {
966
905
  "description": "Unlock the wallet with the fixture password when it is currently locked.",
967
906
  "schema": {
968
907
  "type": "object",
@@ -975,21 +914,16 @@
975
914
  },
976
915
  "examples": [
977
916
  {
978
- "description": "Minimal node",
979
- "node": {
980
- "action": "metamask.wallet.ensure_unlocked",
981
- "intent": "Ensure the wallet is unlocked before proof steps"
982
- }
917
+ "action": "metamask.wallet.ensure_unlocked",
918
+ "intent": "Ensure the wallet is unlocked before proof steps",
919
+ "next": "done"
983
920
  }
984
921
  ],
985
- "proof_effect": "E2E validation must record this action in trace.json; live-proof actions include liveAdapter/proofPath or target runtime output.",
986
- "safety_notes": "Must not inject mid-recipe UI/app state to fabricate proof.",
987
922
  "execution_capabilities": [
988
923
  "app-mutation"
989
924
  ]
990
925
  },
991
- {
992
- "name": "metamask.wallet.select_account",
926
+ "metamask.wallet.select_account": {
993
927
  "description": "extension action to select a wallet account by address, id, or display name.",
994
928
  "schema": {
995
929
  "type": "object",
@@ -1011,30 +945,23 @@
1011
945
  },
1012
946
  "examples": [
1013
947
  {
1014
- "description": "Select by deterministic fixture address",
1015
- "node": {
1016
- "action": "metamask.wallet.select_account",
1017
- "address": "0x8dc623e964475d4d669da601fd15ea9125469003",
1018
- "intent": "Select the requested wallet account"
1019
- }
948
+ "action": "metamask.wallet.select_account",
949
+ "address": "0x8dc623e964475d4d669da601fd15ea9125469003",
950
+ "intent": "Select the requested wallet account",
951
+ "next": "done"
1020
952
  },
1021
953
  {
1022
- "description": "Select by fixture display name",
1023
- "node": {
1024
- "action": "metamask.wallet.select_account",
1025
- "name": "dev1",
1026
- "intent": "Select the requested wallet account"
1027
- }
954
+ "action": "metamask.wallet.select_account",
955
+ "name": "dev1",
956
+ "intent": "Select the requested wallet account",
957
+ "next": "done"
1028
958
  }
1029
959
  ],
1030
- "owner": "metamask",
1031
960
  "execution_capabilities": [
1032
961
  "app-mutation"
1033
962
  ]
1034
963
  },
1035
- {
1036
- "name": "metamask.wallet.list_accounts",
1037
- "owner": "metamask",
964
+ "metamask.wallet.list_accounts": {
1038
965
  "description": "List redacted wallet accounts and identify the selected account without mutating the app.",
1039
966
  "schema": {
1040
967
  "type": "object",
@@ -1055,20 +982,14 @@
1055
982
  },
1056
983
  "examples": [
1057
984
  {
1058
- "description": "List wallet accounts",
1059
- "node": {
1060
- "action": "metamask.wallet.list_accounts",
1061
- "intent": "Discover the available wallet accounts"
1062
- }
985
+ "action": "metamask.wallet.list_accounts",
986
+ "intent": "Discover the available wallet accounts",
987
+ "next": "done"
1063
988
  }
1064
989
  ],
1065
- "proof_effect": "E2E validation must record this action in trace.json; live-proof actions include liveAdapter/proofPath or target runtime output.",
1066
- "safety_notes": "Read-only account metadata; must never expose keys, mnemonics, vault data, or credentials.",
1067
990
  "execution_capabilities": []
1068
991
  },
1069
- {
1070
- "name": "metamask.wallet.read_state",
1071
- "owner": "metamask",
992
+ "metamask.wallet.read_state": {
1072
993
  "description": "Read the redacted wallet state (selected account, route, and device) from the running app.",
1073
994
  "schema": {
1074
995
  "type": "object",
@@ -1077,20 +998,14 @@
1077
998
  },
1078
999
  "examples": [
1079
1000
  {
1080
- "description": "Minimal node",
1081
- "node": {
1082
- "action": "metamask.wallet.read_state",
1083
- "intent": "Read wallet state needed for the proof"
1084
- }
1001
+ "action": "metamask.wallet.read_state",
1002
+ "intent": "Read wallet state needed for the proof",
1003
+ "next": "done"
1085
1004
  }
1086
1005
  ],
1087
- "proof_effect": "E2E validation must record this action in trace.json; live-proof actions include liveAdapter/proofPath or target runtime output.",
1088
- "safety_notes": "Must not inject mid-recipe UI/app state to fabricate proof.",
1089
1006
  "execution_capabilities": []
1090
1007
  },
1091
- {
1092
- "name": "metamask.perps.read_positions",
1093
- "owner": "metamask",
1008
+ "metamask.perps.read_positions": {
1094
1009
  "description": "extension Read live Perps positions; without a selector, return all live positions.",
1095
1010
  "schema": {
1096
1011
  "type": "object",
@@ -1179,21 +1094,15 @@
1179
1094
  },
1180
1095
  "examples": [
1181
1096
  {
1182
- "description": "Read selected live state",
1183
- "node": {
1184
- "action": "metamask.perps.read_positions",
1185
- "market": "BTC",
1186
- "intent": "Read Perps positions needed for the proof"
1187
- }
1097
+ "action": "metamask.perps.read_positions",
1098
+ "market": "BTC",
1099
+ "intent": "Read Perps positions needed for the proof",
1100
+ "next": "done"
1188
1101
  }
1189
1102
  ],
1190
- "proof_effect": "E2E validation must record this action in trace.json; live-proof actions include liveAdapter/proofPath or target runtime output.",
1191
- "safety_notes": "Must not inject mid-recipe UI/app state to fabricate proof.",
1192
1103
  "execution_capabilities": []
1193
1104
  },
1194
- {
1195
- "name": "metamask.perps.read_orders",
1196
- "owner": "metamask",
1105
+ "metamask.perps.read_orders": {
1197
1106
  "description": "extension Read live Perps open orders; without a selector, return all live orders.",
1198
1107
  "schema": {
1199
1108
  "type": "object",
@@ -1282,21 +1191,15 @@
1282
1191
  },
1283
1192
  "examples": [
1284
1193
  {
1285
- "description": "Read selected live state",
1286
- "node": {
1287
- "action": "metamask.perps.read_orders",
1288
- "market": "BTC",
1289
- "intent": "Read Perps open orders needed for the proof"
1290
- }
1194
+ "action": "metamask.perps.read_orders",
1195
+ "market": "BTC",
1196
+ "intent": "Read Perps open orders needed for the proof",
1197
+ "next": "done"
1291
1198
  }
1292
1199
  ],
1293
- "proof_effect": "E2E validation must record this action in trace.json; live-proof actions include liveAdapter/proofPath or target runtime output.",
1294
- "safety_notes": "Must not inject mid-recipe UI/app state to fabricate proof.",
1295
1200
  "execution_capabilities": []
1296
1201
  },
1297
- {
1298
- "name": "metamask.perps.close_positions",
1299
- "owner": "metamask",
1202
+ "metamask.perps.close_positions": {
1300
1203
  "description": "extension Default to testnet for Perps mutations; mainnet is read-only unless explicitly requested. Close selected live Perps positions. Use selector/mode params to close matching symbols or all positions.",
1301
1204
  "schema": {
1302
1205
  "type": "object",
@@ -1385,35 +1288,27 @@
1385
1288
  },
1386
1289
  "examples": [
1387
1290
  {
1388
- "description": "Close every open position",
1389
- "node": {
1390
- "action": "metamask.perps.close_positions",
1391
- "mode": "all",
1392
- "intent": "Close selected Perps positions before continuing"
1393
- }
1291
+ "action": "metamask.perps.close_positions",
1292
+ "mode": "all",
1293
+ "intent": "Close selected Perps positions before continuing",
1294
+ "next": "done"
1394
1295
  },
1395
1296
  {
1396
- "description": "Close selected BTC/ETH positions",
1397
- "node": {
1398
- "action": "metamask.perps.close_positions",
1399
- "markets": [
1400
- "BTC",
1401
- "ETH"
1402
- ],
1403
- "intent": "Close selected Perps positions before continuing"
1404
- }
1297
+ "action": "metamask.perps.close_positions",
1298
+ "markets": [
1299
+ "BTC",
1300
+ "ETH"
1301
+ ],
1302
+ "intent": "Close selected Perps positions before continuing",
1303
+ "next": "done"
1405
1304
  }
1406
1305
  ],
1407
- "proof_effect": "E2E validation must record this action in trace.json; live-proof actions include liveAdapter/proofPath or target runtime output.",
1408
- "safety_notes": "Must not inject mid-recipe UI/app state to fabricate proof.",
1409
1306
  "execution_capabilities": [
1410
1307
  "app-mutation",
1411
1308
  "external-mutation"
1412
1309
  ]
1413
1310
  },
1414
- {
1415
- "name": "metamask.perps.close_orders",
1416
- "owner": "metamask",
1311
+ "metamask.perps.close_orders": {
1417
1312
  "description": "extension Default to testnet for Perps mutations; mainnet is read-only unless explicitly requested. Cancel selected live Perps open orders. Use selector/mode params to cancel matching symbols or all orders.",
1418
1313
  "schema": {
1419
1314
  "type": "object",
@@ -1502,32 +1397,24 @@
1502
1397
  },
1503
1398
  "examples": [
1504
1399
  {
1505
- "description": "Cancel every open order",
1506
- "node": {
1507
- "action": "metamask.perps.close_orders",
1508
- "mode": "all",
1509
- "intent": "Cancel selected Perps orders before continuing"
1510
- }
1400
+ "action": "metamask.perps.close_orders",
1401
+ "mode": "all",
1402
+ "intent": "Cancel selected Perps orders before continuing",
1403
+ "next": "done"
1511
1404
  },
1512
1405
  {
1513
- "description": "Cancel BTC orders only",
1514
- "node": {
1515
- "action": "metamask.perps.close_orders",
1516
- "market": "BTC",
1517
- "intent": "Cancel selected Perps orders before continuing"
1518
- }
1406
+ "action": "metamask.perps.close_orders",
1407
+ "market": "BTC",
1408
+ "intent": "Cancel selected Perps orders before continuing",
1409
+ "next": "done"
1519
1410
  }
1520
1411
  ],
1521
- "proof_effect": "E2E validation must record this action in trace.json; live-proof actions include liveAdapter/proofPath or target runtime output.",
1522
- "safety_notes": "Must not inject mid-recipe UI/app state to fabricate proof.",
1523
1412
  "execution_capabilities": [
1524
1413
  "app-mutation",
1525
1414
  "external-mutation"
1526
1415
  ]
1527
1416
  },
1528
- {
1529
- "name": "metamask.perps.place_order",
1530
- "owner": "metamask",
1417
+ "metamask.perps.place_order": {
1531
1418
  "description": "extension Place a Perps order through a supported app/API path. Default to testnet; mainnet order placement requires an explicit user request.",
1532
1419
  "schema": {
1533
1420
  "type": "object",
@@ -1606,17 +1493,23 @@
1606
1493
  "long",
1607
1494
  "short"
1608
1495
  ],
1609
- "description": "Optional position/order side filter."
1496
+ "description": "Required order direction."
1610
1497
  },
1611
1498
  "timeout_ms": {
1612
1499
  "type": "number"
1613
1500
  },
1614
1501
  "amount": {
1615
- "type": "string",
1502
+ "type": [
1503
+ "string",
1504
+ "number"
1505
+ ],
1616
1506
  "description": "USD amount/notional alias."
1617
1507
  },
1618
1508
  "notional": {
1619
- "type": "string",
1509
+ "type": [
1510
+ "string",
1511
+ "number"
1512
+ ],
1620
1513
  "description": "USD notional alias."
1621
1514
  },
1622
1515
  "size": {
@@ -1632,27 +1525,21 @@
1632
1525
  },
1633
1526
  "examples": [
1634
1527
  {
1635
- "description": "Place BTC market order",
1636
- "node": {
1637
- "action": "metamask.perps.place_order",
1638
- "market": "BTC",
1639
- "side": "long",
1640
- "notional": "10",
1641
- "leverage": 2,
1642
- "intent": "Place the requested Perps order"
1643
- }
1528
+ "action": "metamask.perps.place_order",
1529
+ "market": "BTC",
1530
+ "side": "long",
1531
+ "notional": "10",
1532
+ "leverage": 2,
1533
+ "intent": "Place the requested Perps order",
1534
+ "next": "done"
1644
1535
  }
1645
1536
  ],
1646
- "proof_effect": "E2E validation must record this action in trace.json; live-proof actions include liveAdapter/proofPath or target runtime output.",
1647
- "safety_notes": "Must not inject mid-recipe UI/app state to fabricate proof.",
1648
1537
  "execution_capabilities": [
1649
1538
  "app-mutation",
1650
1539
  "external-mutation"
1651
1540
  ]
1652
1541
  },
1653
- {
1654
- "name": "metamask.perps.assert_positions",
1655
- "owner": "metamask",
1542
+ "metamask.perps.assert_positions": {
1656
1543
  "description": "extension Assert live Perps positions are present or absent for an explicit market selection or mode=all.",
1657
1544
  "schema": {
1658
1545
  "type": "object",
@@ -1755,22 +1642,16 @@
1755
1642
  },
1756
1643
  "examples": [
1757
1644
  {
1758
- "description": "Assert BTC position is open",
1759
- "node": {
1760
- "action": "metamask.perps.assert_positions",
1761
- "state": "open",
1762
- "market": "BTC",
1763
- "intent": "Assert the expected Perps position state"
1764
- }
1645
+ "action": "metamask.perps.assert_positions",
1646
+ "state": "open",
1647
+ "market": "BTC",
1648
+ "intent": "Assert the expected Perps position state",
1649
+ "next": "done"
1765
1650
  }
1766
1651
  ],
1767
- "proof_effect": "E2E validation must record this action in trace.json; live-proof actions include liveAdapter/proofPath or target runtime output.",
1768
- "safety_notes": "Must not inject mid-recipe UI/app state to fabricate proof.",
1769
1652
  "execution_capabilities": []
1770
1653
  },
1771
- {
1772
- "name": "metamask.perps.assert_orders",
1773
- "owner": "metamask",
1654
+ "metamask.perps.assert_orders": {
1774
1655
  "description": "extension Assert live Perps open orders are present or absent for an explicit market selection or mode=all.",
1775
1656
  "schema": {
1776
1657
  "type": "object",
@@ -1873,22 +1754,16 @@
1873
1754
  },
1874
1755
  "examples": [
1875
1756
  {
1876
- "description": "Assert no BTC open orders",
1877
- "node": {
1878
- "action": "metamask.perps.assert_orders",
1879
- "state": "none",
1880
- "market": "BTC",
1881
- "intent": "Assert the expected Perps open order state"
1882
- }
1757
+ "action": "metamask.perps.assert_orders",
1758
+ "state": "none",
1759
+ "market": "BTC",
1760
+ "intent": "Assert the expected Perps open order state",
1761
+ "next": "done"
1883
1762
  }
1884
1763
  ],
1885
- "proof_effect": "E2E validation must record this action in trace.json; live-proof actions include liveAdapter/proofPath or target runtime output.",
1886
- "safety_notes": "Must not inject mid-recipe UI/app state to fabricate proof.",
1887
1764
  "execution_capabilities": []
1888
1765
  },
1889
- {
1890
- "name": "metamask.perps.ensure_positions",
1891
- "owner": "metamask",
1766
+ "metamask.perps.ensure_positions": {
1892
1767
  "description": "extension Default to testnet for Perps mutations; mainnet is read-only unless explicitly requested. Higher-level wrapper that reads selected positions, closes or places when needed, then asserts final position state.",
1893
1768
  "schema": {
1894
1769
  "type": "object",
@@ -1967,11 +1842,25 @@
1967
1842
  "long",
1968
1843
  "short"
1969
1844
  ],
1970
- "description": "Optional position/order side filter."
1845
+ "description": "Required when state=open/present; otherwise filters selected positions."
1971
1846
  },
1972
1847
  "timeout_ms": {
1973
1848
  "type": "number"
1974
1849
  },
1850
+ "amount": {
1851
+ "type": [
1852
+ "string",
1853
+ "number"
1854
+ ],
1855
+ "description": "Explicit USD notional used only when creating a missing open position. Alias: notional."
1856
+ },
1857
+ "notional": {
1858
+ "type": [
1859
+ "string",
1860
+ "number"
1861
+ ],
1862
+ "description": "Alias for amount."
1863
+ },
1975
1864
  "state": {
1976
1865
  "type": "string",
1977
1866
  "enum": [
@@ -1991,34 +1880,35 @@
1991
1880
  },
1992
1881
  "examples": [
1993
1882
  {
1994
- "description": "Ensure no BTC position before proof window",
1995
- "node": {
1996
- "action": "metamask.perps.ensure_positions",
1997
- "state": "none",
1998
- "market": "BTC",
1999
- "intent": "Converge Perps positions to the requested state"
2000
- }
1883
+ "action": "metamask.perps.ensure_positions",
1884
+ "state": "none",
1885
+ "market": "BTC",
1886
+ "intent": "Converge Perps positions to the requested state",
1887
+ "next": "done"
2001
1888
  },
2002
1889
  {
2003
- "description": "Ensure any existing positions are closed",
2004
- "node": {
2005
- "action": "metamask.perps.ensure_positions",
2006
- "state": "none",
2007
- "mode": "all",
2008
- "intent": "Converge Perps positions to the requested state"
2009
- }
1890
+ "action": "metamask.perps.ensure_positions",
1891
+ "state": "none",
1892
+ "mode": "all",
1893
+ "intent": "Converge Perps positions to the requested state",
1894
+ "next": "done"
1895
+ },
1896
+ {
1897
+ "action": "metamask.perps.ensure_positions",
1898
+ "market": "ETH",
1899
+ "side": "long",
1900
+ "state": "open",
1901
+ "notional": "10",
1902
+ "intent": "Converge ETH positions to one explicit testnet precondition",
1903
+ "next": "done"
2010
1904
  }
2011
1905
  ],
2012
- "proof_effect": "E2E validation must record this action in trace.json; live-proof actions include liveAdapter/proofPath or target runtime output.",
2013
- "safety_notes": "Must not inject mid-recipe UI/app state to fabricate proof.",
2014
1906
  "execution_capabilities": [
2015
1907
  "app-mutation",
2016
1908
  "external-mutation"
2017
1909
  ]
2018
1910
  },
2019
- {
2020
- "name": "metamask.perps.ensure_orders",
2021
- "owner": "metamask",
1911
+ "metamask.perps.ensure_orders": {
2022
1912
  "description": "extension Converge selected testnet orders to open or absent, then independently assert the final state. Creating an open state uses a resting limit order.",
2023
1913
  "schema": {
2024
1914
  "type": "object",
@@ -2097,15 +1987,24 @@
2097
1987
  "long",
2098
1988
  "short"
2099
1989
  ],
2100
- "description": "Optional position/order side filter."
1990
+ "description": "Required when state=open/present; otherwise filters selected orders."
2101
1991
  },
2102
1992
  "timeout_ms": {
2103
1993
  "type": "number"
2104
1994
  },
1995
+ "amount": {
1996
+ "type": [
1997
+ "string",
1998
+ "number"
1999
+ ],
2000
+ "description": "Explicit USD notional used only when creating a missing open order. Alias: notional."
2001
+ },
2105
2002
  "notional": {
2106
- "type": "number",
2107
- "default": 10,
2108
- "description": "Small testnet USD notional used only when creating a missing open order."
2003
+ "type": [
2004
+ "string",
2005
+ "number"
2006
+ ],
2007
+ "description": "Alias for amount."
2109
2008
  },
2110
2009
  "leverage": {
2111
2010
  "type": "number",
@@ -2131,37 +2030,29 @@
2131
2030
  },
2132
2031
  "examples": [
2133
2032
  {
2134
- "description": "Ensure no open orders before proof window",
2135
- "node": {
2136
- "action": "metamask.perps.ensure_orders",
2137
- "state": "none",
2138
- "mode": "all",
2139
- "intent": "Converge Perps orders to the requested state"
2140
- }
2033
+ "action": "metamask.perps.ensure_orders",
2034
+ "state": "none",
2035
+ "mode": "all",
2036
+ "intent": "Converge Perps orders to the requested state",
2037
+ "next": "done"
2141
2038
  },
2142
2039
  {
2143
- "description": "Ensure one small resting ETH short order exists",
2144
- "node": {
2145
- "action": "metamask.perps.ensure_orders",
2146
- "market": "ETH",
2147
- "side": "short",
2148
- "state": "open",
2149
- "notional": 10,
2150
- "leverage": 2,
2151
- "intent": "Converge ETH open orders to one deterministic testnet precondition"
2152
- }
2040
+ "action": "metamask.perps.ensure_orders",
2041
+ "market": "ETH",
2042
+ "side": "short",
2043
+ "state": "open",
2044
+ "notional": 10,
2045
+ "leverage": 2,
2046
+ "intent": "Converge ETH open orders to one deterministic testnet precondition",
2047
+ "next": "done"
2153
2048
  }
2154
2049
  ],
2155
- "proof_effect": "E2E validation must record this action in trace.json; live-proof actions include liveAdapter/proofPath or target runtime output.",
2156
- "safety_notes": "Must not inject mid-recipe UI/app state to fabricate proof.",
2157
2050
  "execution_capabilities": [
2158
2051
  "app-mutation",
2159
2052
  "external-mutation"
2160
2053
  ]
2161
2054
  },
2162
- {
2163
- "name": "metamask.perps.start_state",
2164
- "owner": "metamask",
2055
+ "metamask.perps.start_state": {
2165
2056
  "description": "extension Default to testnet for Perps mutations; mainnet is read-only unless explicitly requested. Recommended configurable Perps start state that composes reusable domain operations before the proof window.",
2166
2057
  "schema": {
2167
2058
  "type": "object",
@@ -2485,48 +2376,40 @@
2485
2376
  },
2486
2377
  "examples": [
2487
2378
  {
2488
- "description": "Clean BTC testnet market state",
2489
- "node": {
2490
- "action": "metamask.perps.start_state",
2491
- "profile": "clean_market_testnet",
2492
- "market": "BTC",
2493
- "positions": {
2494
- "state": "none"
2495
- },
2496
- "orders": {
2497
- "state": "none"
2498
- },
2499
- "intent": "Prepare the Perps start state for the proof",
2500
- "network": "testnet"
2501
- }
2379
+ "action": "metamask.perps.start_state",
2380
+ "profile": "clean_market_testnet",
2381
+ "market": "BTC",
2382
+ "positions": {
2383
+ "state": "none"
2384
+ },
2385
+ "orders": {
2386
+ "state": "none"
2387
+ },
2388
+ "intent": "Prepare the Perps start state for the proof",
2389
+ "network": "testnet",
2390
+ "next": "done"
2502
2391
  },
2503
2392
  {
2504
- "description": "Start with an open ETH long position",
2505
- "node": {
2506
- "action": "metamask.perps.start_state",
2507
- "profile": "open_position_testnet",
2508
- "market": "ETH",
2509
- "side": "long",
2510
- "positions": {
2511
- "state": "open",
2512
- "notional": "10",
2513
- "leverage": 2
2514
- },
2515
- "intent": "Prepare the Perps start state for the proof",
2516
- "network": "testnet"
2517
- }
2393
+ "action": "metamask.perps.start_state",
2394
+ "profile": "open_position_testnet",
2395
+ "market": "ETH",
2396
+ "side": "long",
2397
+ "positions": {
2398
+ "state": "open",
2399
+ "notional": "10",
2400
+ "leverage": 2
2401
+ },
2402
+ "intent": "Prepare the Perps start state for the proof",
2403
+ "network": "testnet",
2404
+ "next": "done"
2518
2405
  }
2519
2406
  ],
2520
- "proof_effect": "Runs in setup/teardown phase and must be visible in trace.json; proof videos can keep this as trace-only setup.",
2521
- "safety_notes": "Composes supported app/API paths and must not mutate React/Redux/MobX/DOM/local storage to fabricate proof.",
2522
2407
  "execution_capabilities": [
2523
2408
  "app-mutation",
2524
2409
  "external-mutation"
2525
2410
  ]
2526
2411
  },
2527
- {
2528
- "name": "metamask.perps.teardown_state",
2529
- "owner": "metamask",
2412
+ "metamask.perps.teardown_state": {
2530
2413
  "description": "extension Recommended configurable Perps teardown state that restores the selected environment after proof.",
2531
2414
  "schema": {
2532
2415
  "type": "object",
@@ -2854,181 +2737,29 @@
2854
2737
  },
2855
2738
  "examples": [
2856
2739
  {
2857
- "description": "Return BTC test account to clean state",
2858
- "node": {
2859
- "action": "metamask.perps.teardown_state",
2860
- "market": "BTC",
2861
- "positions": {
2862
- "state": "none"
2863
- },
2864
- "orders": {
2865
- "state": "none"
2866
- },
2867
- "page": "home",
2868
- "intent": "Restore Perps state after the proof"
2869
- }
2740
+ "action": "metamask.perps.teardown_state",
2741
+ "market": "BTC",
2742
+ "positions": {
2743
+ "state": "none"
2744
+ },
2745
+ "orders": {
2746
+ "state": "none"
2747
+ },
2748
+ "page": "home",
2749
+ "intent": "Restore Perps state after the proof",
2750
+ "next": "done"
2870
2751
  },
2871
2752
  {
2872
- "description": "Teardown only state created by selected market",
2873
- "node": {
2874
- "action": "metamask.perps.teardown_state",
2875
- "market": "BTC",
2876
- "intent": "Restore Perps state after the proof"
2877
- }
2753
+ "action": "metamask.perps.teardown_state",
2754
+ "market": "BTC",
2755
+ "intent": "Restore Perps state after the proof",
2756
+ "next": "done"
2878
2757
  }
2879
2758
  ],
2880
- "proof_effect": "Runs in setup/teardown phase and must be visible in trace.json; proof videos can keep this as trace-only setup.",
2881
- "safety_notes": "Composes supported app/API paths and must not mutate React/Redux/MobX/DOM/local storage to fabricate proof.",
2882
2759
  "execution_capabilities": [
2883
2760
  "app-mutation",
2884
2761
  "external-mutation"
2885
2762
  ]
2886
2763
  }
2887
- ],
2888
- "native_bindings": [
2889
- {
2890
- "action": "command",
2891
- "implementation": "@farmslot/recipe-harness/adapters/core"
2892
- },
2893
- {
2894
- "action": "wait",
2895
- "implementation": "@farmslot/recipe-harness/adapters/core"
2896
- },
2897
- {
2898
- "action": "assert_file",
2899
- "implementation": "@farmslot/recipe-harness/adapters/core"
2900
- },
2901
- {
2902
- "action": "assert_json",
2903
- "implementation": "@farmslot/recipe-harness/adapters/core"
2904
- },
2905
- {
2906
- "action": "assert_exit_code",
2907
- "implementation": "@farmslot/recipe-harness/adapters/core"
2908
- },
2909
- {
2910
- "action": "assert_output",
2911
- "implementation": "@farmslot/recipe-harness/adapters/core"
2912
- },
2913
- {
2914
- "action": "watch_logs",
2915
- "implementation": "@farmslot/recipe-harness/adapters/core"
2916
- },
2917
- {
2918
- "action": "index_artifacts",
2919
- "implementation": "@farmslot/recipe-harness/adapters/core"
2920
- },
2921
- {
2922
- "action": "end",
2923
- "implementation": "@farmslot/recipe-harness/adapters/core"
2924
- },
2925
- {
2926
- "action": "ui.navigate",
2927
- "implementation": "@farmslot/recipe-harness/runtime/cdp"
2928
- },
2929
- {
2930
- "action": "ui.press",
2931
- "implementation": "@farmslot/recipe-harness/runtime/cdp"
2932
- },
2933
- {
2934
- "action": "ui.key_press",
2935
- "implementation": "@farmslot/recipe-harness/runtime/cdp"
2936
- },
2937
- {
2938
- "action": "ui.set_input",
2939
- "implementation": "@metamask/recipe-runner/extension/cdp-set-input"
2940
- },
2941
- {
2942
- "action": "ui.scroll",
2943
- "implementation": "@farmslot/recipe-harness/runtime/cdp"
2944
- },
2945
- {
2946
- "action": "ui.wait_for",
2947
- "implementation": "@farmslot/recipe-harness/runtime/cdp"
2948
- },
2949
- {
2950
- "action": "ui.screenshot",
2951
- "implementation": "@farmslot/recipe-harness/runtime/cdp"
2952
- },
2953
- {
2954
- "action": "app.status",
2955
- "implementation": "@metamask/recipe-runner/platform-status"
2956
- },
2957
- {
2958
- "action": "app.hud",
2959
- "implementation": "@farmslot/recipe-harness/runtime/cdp"
2960
- },
2961
- {
2962
- "action": "cdp.target",
2963
- "implementation": "@metamask/recipe-runner/platform-cdp-target"
2964
- },
2965
- {
2966
- "action": "metamask.wallet.fixture_status",
2967
- "implementation": "@metamask/recipe-runner/extension/wallet"
2968
- },
2969
- {
2970
- "action": "metamask.wallet.setup",
2971
- "implementation": "@metamask/recipe-runner/extension/wallet"
2972
- },
2973
- {
2974
- "action": "metamask.wallet.ensure_unlocked",
2975
- "implementation": "@metamask/recipe-runner/extension/wallet"
2976
- },
2977
- {
2978
- "action": "metamask.wallet.select_account",
2979
- "implementation": "@metamask/recipe-runner/extension/wallet"
2980
- },
2981
- {
2982
- "action": "metamask.wallet.list_accounts",
2983
- "implementation": "@metamask/recipe-runner/extension/wallet"
2984
- },
2985
- {
2986
- "action": "metamask.wallet.read_state",
2987
- "implementation": "@metamask/recipe-runner/extension/wallet"
2988
- },
2989
- {
2990
- "action": "metamask.perps.read_positions",
2991
- "implementation": "@metamask/recipe-runner/extension/perps"
2992
- },
2993
- {
2994
- "action": "metamask.perps.read_orders",
2995
- "implementation": "@metamask/recipe-runner/extension/perps"
2996
- },
2997
- {
2998
- "action": "metamask.perps.close_positions",
2999
- "implementation": "@metamask/recipe-runner/extension/perps"
3000
- },
3001
- {
3002
- "action": "metamask.perps.close_orders",
3003
- "implementation": "@metamask/recipe-runner/extension/perps"
3004
- },
3005
- {
3006
- "action": "metamask.perps.place_order",
3007
- "implementation": "@metamask/recipe-runner/extension/perps"
3008
- },
3009
- {
3010
- "action": "metamask.perps.assert_positions",
3011
- "implementation": "@metamask/recipe-runner/extension/perps"
3012
- },
3013
- {
3014
- "action": "metamask.perps.assert_orders",
3015
- "implementation": "@metamask/recipe-runner/extension/perps"
3016
- },
3017
- {
3018
- "action": "metamask.perps.ensure_positions",
3019
- "implementation": "@metamask/recipe-runner/extension/perps"
3020
- },
3021
- {
3022
- "action": "metamask.perps.ensure_orders",
3023
- "implementation": "@metamask/recipe-runner/extension/perps"
3024
- },
3025
- {
3026
- "action": "metamask.perps.start_state",
3027
- "implementation": "@metamask/recipe-runner/extension/perps-domain-dispatcher"
3028
- },
3029
- {
3030
- "action": "metamask.perps.teardown_state",
3031
- "implementation": "@metamask/recipe-runner/extension/perps-domain-dispatcher"
3032
- }
3033
- ]
2764
+ }
3034
2765
  }