@deeeed/metamask-harness 0.18.0 → 0.19.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 (79) hide show
  1. package/CHANGELOG.md +187 -0
  2. package/README.md +4 -7
  3. package/adapters/core/inject.sh +1 -6
  4. package/adapters/extension/inject.mjs +1 -4
  5. package/adapters/extension/verify.sh +1 -1
  6. package/adapters/mobile/inject.sh +5 -4
  7. package/adapters/mobile/verify.sh +15 -19
  8. package/adapters/shared/harness-source-fingerprint.mjs +14 -12
  9. package/dist/adapters/harness-freshness.js +49 -0
  10. package/dist/cli.js +0 -4
  11. package/dist/command-contract.js +51 -40
  12. package/dist/command-journal.js +50 -12
  13. package/dist/commands/call.js +95 -20
  14. package/dist/commands/check.js +1 -1
  15. package/dist/commands/completion-candidates.js +7 -19
  16. package/dist/commands/fixtures.js +50 -1
  17. package/dist/commands/last.js +9 -1
  18. package/dist/commands/launch/extension.js +2 -2
  19. package/dist/commands/launch/mobile.js +2 -0
  20. package/dist/commands/list-executables.js +56 -20
  21. package/dist/commands/manifest.js +28 -12
  22. package/dist/commands/parse-args.js +55 -3
  23. package/dist/commands/provision.js +0 -1
  24. package/dist/commands/run-engine.js +514 -336
  25. package/dist/commands/run.js +25 -37
  26. package/dist/commands/shared.js +1 -1
  27. package/dist/heal-bounds.js +5 -0
  28. package/dist/mm-harness-cli.js +22 -30
  29. package/dist/run-diagnostics.js +1 -1
  30. package/dist/run-recording.js +1 -1
  31. package/dist/runner.js +24 -2
  32. package/docs/CONTRIBUTING.md +2 -3
  33. package/docs/QA.md +0 -1
  34. package/docs/RECIPES.md +52 -91
  35. package/library/README.md +5 -5
  36. package/library/actions/core/perps/_controller.mjs +33 -1
  37. package/library/actions/core/perps/assert_orders.mjs +6 -7
  38. package/library/actions/core/perps/assert_positions.mjs +6 -7
  39. package/library/actions/core/perps/close_orders.mjs +2 -0
  40. package/library/actions/core/perps/close_positions.mjs +2 -0
  41. package/library/actions/core/perps/ensure_orders.mjs +4 -2
  42. package/library/actions/core/perps/ensure_positions.mjs +4 -2
  43. package/library/actions/core/perps/place_order.mjs +7 -3
  44. package/library/actions/extension/perps/assert_orders.mjs +2 -1
  45. package/library/actions/extension/perps/assert_positions.mjs +2 -1
  46. package/library/actions/extension/perps/perps.mjs +43 -14
  47. package/library/actions/mobile/perps/assert_orders.mjs +2 -1
  48. package/library/actions/mobile/perps/assert_positions.mjs +2 -1
  49. package/library/actions/mobile/perps/perps.mjs +40 -12
  50. package/library/library.json +1 -1
  51. package/library/manifests/core.action-manifest.json +1170 -413
  52. package/library/manifests/extension.action-manifest.json +1495 -641
  53. package/library/manifests/mobile.action-manifest.json +1704 -769
  54. package/library/recipes/app/lifecycle.android-smoke.mobile.recipe.json +63 -81
  55. package/library/recipes/perps/clean-market-testnet.core.recipe.json +44 -0
  56. package/library/recipes/perps/clean-market-testnet.recipe.json +49 -0
  57. package/library/recipes/perps/lifecycle.recipe.json +136 -180
  58. package/library/recipes/perps/order-lifecycle.core.recipe.json +71 -67
  59. package/library/recipes/perps/performance.background-resume.mobile.recipe.json +51 -67
  60. package/library/recipes/perps/performance.cold-start.mobile.recipe.json +51 -67
  61. package/library/recipes/perps/performance.mobile.recipe.json +37 -51
  62. package/library/recipes/perps/performance.warm-start.mobile.recipe.json +44 -59
  63. package/library/recipes/perps/read-markets.core.recipe.json +29 -31
  64. package/library/recipes/perps/smoke.core.recipe.json +29 -32
  65. package/library/recipes/perps/smoke.extension.recipe.json +41 -44
  66. package/library/recipes/perps/smoke.mobile.recipe.json +42 -44
  67. package/library/recipes/perps/trading-lifecycle.core.recipe.json +69 -65
  68. package/library/recipes/runner/action-validation.extension.recipe.json +312 -405
  69. package/library/recipes/runner/action-validation.mobile.recipe.json +316 -409
  70. package/library/recipes/runner/smoke.core.recipe.json +18 -20
  71. package/library/recipes/runner/smoke.extension.recipe.json +23 -24
  72. package/library/recipes/runner/smoke.mobile.recipe.json +23 -24
  73. package/library/recipes/wallet/smoke.extension.recipe.json +33 -35
  74. package/library/recipes/wallet/smoke.mobile.recipe.json +33 -35
  75. package/package.json +3 -3
  76. package/scripts/completions.sh +1 -4
  77. package/dist/adapters/extension/harness-freshness.js +0 -39
  78. package/dist/commands/flows.js +0 -91
  79. package/library/flows/perps.flows.json +0 -64
@@ -1,86 +1,68 @@
1
1
  {
2
- "schema_version": 1,
2
+ "$schema": "https://farmslot.io/schemas/recipe-v1.schema.json",
3
3
  "title": "MetaMask Mobile Android lifecycle smoke",
4
- "description": "Isolated on-device proof for the standard outer app.lifecycle action on Android. Setup cold-relaunches the installed app through the Expo dev-client deep link without rebuilding and proves the React Native bridge is reachable. The main workflow then backgrounds the installed app, foregrounds it again, proves the bridge is reachable after resume, and captures a screenshot.",
5
- "startState": {
6
- "action": "app.status",
7
- "intent": "Record the stable run start status after setup relaunch",
8
- "detail": "Read-only start-state marker after setup has made the Android bridge reachable."
9
- },
10
- "validate": {
11
- "workflow": {
12
- "setup": [
13
- {
14
- "id": "terminate-before",
15
- "action": "app.lifecycle",
16
- "platform": "android",
17
- "command": "terminate",
18
- "settle_ms": 3000,
19
- "intent": "Terminate the installed Android app before lifecycle smoke",
20
- "detail": "Force-stops stale foreground instances that may have no React Native debug target."
21
- },
22
- {
23
- "id": "launch-before",
24
- "action": "app.lifecycle",
25
- "platform": "android",
26
- "command": "launch",
27
- "settle_ms": 15000,
28
- "intent": "Launch the installed Android app through the Expo dev-client deep link from a cold process",
29
- "detail": "Uses the selected adb serial, Metro port, and installed package; it does not rebuild."
30
- },
31
- {
32
- "id": "bridge-before",
33
- "action": "cdp.target",
34
- "require_reachable": true,
35
- "intent": "Confirm the mobile React Native debug bridge is reachable after setup relaunch",
36
- "detail": "This proves setup can recover a missing Android bridge target before the background-resume proof."
37
- }
38
- ],
39
- "entry": "background-app",
40
- "nodes": {
41
- "background-app": {
42
- "action": "app.lifecycle",
43
- "platform": "android",
44
- "command": "background",
45
- "settle_ms": 1000,
46
- "intent": "Put the selected Android device on the launcher",
47
- "detail": "Uses adb HOME through the shared Farmslot lifecycle adapter.",
48
- "flow": "lifecycle",
49
- "next": "foreground-app"
50
- },
51
- "foreground-app": {
52
- "action": "app.lifecycle",
53
- "platform": "android",
54
- "command": "foreground",
55
- "settle_ms": 15000,
56
- "intent": "Foreground the installed Android app through the Expo dev-client deep link",
57
- "detail": "Uses the selected adb serial, Metro port, and installed package; it does not rebuild.",
58
- "flow": "lifecycle",
59
- "next": "bridge-after"
60
- },
61
- "bridge-after": {
62
- "action": "cdp.target",
63
- "require_reachable": true,
64
- "intent": "Confirm the mobile React Native debug bridge recovered after foregrounding",
65
- "detail": "This is the capability gate before running Perps recipes that depend on CDP/bridge control.",
66
- "flow": "lifecycle",
67
- "next": "screenshot-after"
68
- },
69
- "screenshot-after": {
70
- "action": "ui.screenshot",
71
- "path": "screenshots/app.lifecycle.android-smoke.png",
72
- "intent": "Capture the app after Android lifecycle foreground",
73
- "detail": "Visual proof that the selected device is displaying the relaunched app.",
74
- "flow": "evidence",
75
- "next": "end"
76
- },
77
- "end": {
78
- "action": "end",
79
- "status": "pass",
80
- "intent": "Finish the isolated Android lifecycle smoke",
81
- "detail": "If this passes, lifecycle is proven independently of wallet and Perps actions.",
82
- "flow": "complete"
83
- }
4
+ "description": "Isolated on-device proof for the standard app.lifecycle action on Android. The graph cold-relaunches the installed app without rebuilding, proves the React Native bridge is reachable, backgrounds and foregrounds the app, proves bridge recovery, and captures a screenshot.",
5
+ "workflow": {
6
+ "entry": "terminate-before",
7
+ "nodes": {
8
+ "background-app": {
9
+ "action": "app.lifecycle",
10
+ "platform": "android",
11
+ "command": "background",
12
+ "settle_ms": 1000,
13
+ "intent": "Put the selected Android device on the launcher",
14
+ "next": "foreground-app"
15
+ },
16
+ "foreground-app": {
17
+ "action": "app.lifecycle",
18
+ "platform": "android",
19
+ "command": "foreground",
20
+ "settle_ms": 15000,
21
+ "intent": "Foreground the installed Android app through the Expo dev-client deep link",
22
+ "next": "bridge-after"
23
+ },
24
+ "bridge-after": {
25
+ "action": "cdp.target",
26
+ "require_reachable": true,
27
+ "intent": "Confirm the mobile React Native debug bridge recovered after foregrounding",
28
+ "next": "screenshot-after"
29
+ },
30
+ "screenshot-after": {
31
+ "action": "ui.screenshot",
32
+ "path": "screenshots/app.lifecycle.android-smoke.png",
33
+ "intent": "Record the foreground app state as evidence",
34
+ "next": "end"
35
+ },
36
+ "end": {
37
+ "action": "end",
38
+ "status": "pass"
39
+ },
40
+ "start-state": {
41
+ "action": "app.status",
42
+ "intent": "Record the stable run start status after setup relaunch",
43
+ "next": "background-app"
44
+ },
45
+ "bridge-before": {
46
+ "action": "cdp.target",
47
+ "require_reachable": true,
48
+ "intent": "Confirm the mobile React Native debug bridge is reachable after setup relaunch",
49
+ "next": "start-state"
50
+ },
51
+ "launch-before": {
52
+ "action": "app.lifecycle",
53
+ "platform": "android",
54
+ "command": "launch",
55
+ "settle_ms": 15000,
56
+ "intent": "Launch the installed Android app through the Expo dev-client deep link from a cold process",
57
+ "next": "bridge-before"
58
+ },
59
+ "terminate-before": {
60
+ "action": "app.lifecycle",
61
+ "platform": "android",
62
+ "command": "terminate",
63
+ "settle_ms": 3000,
64
+ "intent": "Terminate the installed Android app before lifecycle smoke",
65
+ "next": "launch-before"
84
66
  }
85
67
  }
86
68
  }
@@ -0,0 +1,44 @@
1
+ {
2
+ "$schema": "https://farmslot.io/schemas/recipe-v1.schema.json",
3
+ "title": "Clean Perps testnet market",
4
+ "description": "Converge one Core Perps testnet market to no matching open position or order.",
5
+ "paramsSchema": {
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": [
9
+ "market"
10
+ ],
11
+ "properties": {
12
+ "market": {
13
+ "type": "string",
14
+ "default": "ETH",
15
+ "description": "Perps market symbol to clean."
16
+ }
17
+ }
18
+ },
19
+ "workflow": {
20
+ "entry": "clean-market",
21
+ "nodes": {
22
+ "clean-market": {
23
+ "action": "metamask.perps.start_state",
24
+ "intent": "Converge Core Perps to a clean testnet market",
25
+ "profile": "clean_market_testnet",
26
+ "network": "testnet",
27
+ "market": "{{params.market}}",
28
+ "positions": {
29
+ "state": "none",
30
+ "mode": "matching"
31
+ },
32
+ "orders": {
33
+ "state": "none",
34
+ "mode": "matching"
35
+ },
36
+ "next": "done"
37
+ },
38
+ "done": {
39
+ "action": "end",
40
+ "status": "pass"
41
+ }
42
+ }
43
+ }
44
+ }
@@ -0,0 +1,49 @@
1
+ {
2
+ "$schema": "https://farmslot.io/schemas/recipe-v1.schema.json",
3
+ "title": "Clean Perps testnet market",
4
+ "description": "Converge one Perps testnet market to an unlocked wallet with no matching open position or order.",
5
+ "paramsSchema": {
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": [
9
+ "market"
10
+ ],
11
+ "properties": {
12
+ "market": {
13
+ "type": "string",
14
+ "default": "ETH",
15
+ "description": "Perps market symbol to clean."
16
+ }
17
+ }
18
+ },
19
+ "workflow": {
20
+ "entry": "ensure-wallet",
21
+ "nodes": {
22
+ "ensure-wallet": {
23
+ "action": "metamask.wallet.ensure_unlocked",
24
+ "intent": "Ensure the wallet is unlocked before cleaning the Perps market",
25
+ "next": "clean-market"
26
+ },
27
+ "clean-market": {
28
+ "action": "metamask.perps.start_state",
29
+ "intent": "Converge Perps to a clean testnet market",
30
+ "profile": "clean_market_testnet",
31
+ "network": "testnet",
32
+ "market": "{{params.market}}",
33
+ "positions": {
34
+ "state": "none",
35
+ "mode": "matching"
36
+ },
37
+ "orders": {
38
+ "state": "none",
39
+ "mode": "matching"
40
+ },
41
+ "next": "done"
42
+ },
43
+ "done": {
44
+ "action": "end",
45
+ "status": "pass"
46
+ }
47
+ }
48
+ }
49
+ }
@@ -1,193 +1,149 @@
1
1
  {
2
- "schema_version": 1,
2
+ "$schema": "https://farmslot.io/schemas/recipe-v1.schema.json",
3
3
  "title": "MetaMask Perps lifecycle smoke",
4
4
  "description": "Reusable Recipe v1 smoke that proves a deterministic Perps prestate, opens a small ETH testnet position, verifies it, closes it, and tears down to a clean state.",
5
- "validate": {
6
- "workflow": {
7
- "entry": "status",
8
- "nodes": {
9
- "status": {
10
- "action": "app.status",
11
- "intent": "Read runner compatibility status",
12
- "detail": "Confirm the selected MetaMask adapter is connected before trading.",
13
- "flow": "setup",
14
- "next": "fixture"
15
- },
16
- "fixture": {
17
- "action": "metamask.wallet.fixture_status",
18
- "intent": "Verify wallet fixture availability",
19
- "detail": "Check the preseeded wallet fixture required for deterministic Perps validation.",
20
- "flow": "setup",
21
- "next": "wallet-setup"
22
- },
23
- "wallet-setup": {
24
- "action": "metamask.wallet.setup",
25
- "intent": "Ensure wallet fixture setup is ready",
26
- "detail": "Confirm onboarding and fixture state before controlling the app.",
27
- "flow": "wallet",
28
- "next": "wallet-unlock"
29
- },
30
- "wallet-unlock": {
31
- "action": "metamask.wallet.ensure_unlocked",
32
- "intent": "Ensure the wallet is unlocked",
33
- "detail": "Unlock only if the app is currently locked.",
34
- "flow": "wallet",
35
- "next": "select-account"
36
- },
37
- "select-account": {
38
- "action": "metamask.wallet.select_account",
39
- "address": "0x8dc623e964475d4d669da601fd15ea9125469003",
40
- "intent": "Select the validation account",
41
- "detail": "Use the deterministic testnet account shared by mobile and extension.",
42
- "flow": "wallet",
43
- "next": "wallet-read-state"
44
- },
45
- "wallet-read-state": {
46
- "action": "metamask.wallet.read_state",
47
- "intent": "Capture wallet state proof",
48
- "detail": "Record selected account/network state before the Perps lifecycle.",
49
- "flow": "wallet",
50
- "next": "navigate-perps"
51
- },
52
- "navigate-perps": {
53
- "action": "ui.navigate",
54
- "intent": "Navigate to Perps with raw platform routing",
55
- "detail": "Mobile consumes route=PerpsMarketListView; extension consumes hash=#/?tab=perps.",
56
- "flow": "perps setup",
57
- "next": "navigate-market",
58
- "route": "PerpsMarketListView",
59
- "hash": "#/?tab=perps"
60
- },
61
- "navigate-market": {
62
- "action": "ui.navigate",
63
- "intent": "Open the ETH Perps market with raw platform routing",
64
- "detail": "Mobile consumes route=params; extension consumes hash=#/perps/market/ETH.",
65
- "flow": "perps setup",
66
- "next": "start-state",
67
- "route": "PerpsMarketDetails",
68
- "params": {
69
- "market": {
70
- "symbol": "ETH"
71
- }
72
- },
73
- "hash": "#/perps/market/ETH"
74
- },
75
- "start-state": {
76
- "action": "metamask.perps.start_state",
77
- "profile": "clean_market_testnet",
78
- "market": "ETH",
79
- "positions": {
80
- "state": "none",
81
- "mode": "matching"
82
- },
83
- "orders": {
84
- "state": "none",
85
- "mode": "matching"
86
- },
87
- "page": "market",
88
- "intent": "Prepare clean ETH testnet Perps state",
89
- "detail": "Close existing ETH testnet positions and orders before the smoke lifecycle starts.",
90
- "flow": "perps setup",
91
- "next": "assert-clean-position",
92
- "tutorial": {
93
- "completed": true
5
+ "workflow": {
6
+ "entry": "status",
7
+ "nodes": {
8
+ "status": {
9
+ "action": "app.status",
10
+ "intent": "Read runner compatibility status",
11
+ "next": "fixture"
12
+ },
13
+ "fixture": {
14
+ "action": "metamask.wallet.fixture_status",
15
+ "intent": "Verify wallet fixture availability",
16
+ "next": "wallet-setup"
17
+ },
18
+ "wallet-setup": {
19
+ "action": "metamask.wallet.setup",
20
+ "intent": "Ensure wallet fixture setup is ready",
21
+ "next": "wallet-unlock"
22
+ },
23
+ "wallet-unlock": {
24
+ "action": "metamask.wallet.ensure_unlocked",
25
+ "intent": "Ensure the wallet is unlocked",
26
+ "next": "select-account"
27
+ },
28
+ "select-account": {
29
+ "action": "metamask.wallet.select_account",
30
+ "address": "0x8dc623e964475d4d669da601fd15ea9125469003",
31
+ "intent": "Select the validation account",
32
+ "next": "wallet-read-state"
33
+ },
34
+ "wallet-read-state": {
35
+ "action": "metamask.wallet.read_state",
36
+ "intent": "Capture wallet state proof",
37
+ "next": "navigate-perps"
38
+ },
39
+ "navigate-perps": {
40
+ "action": "ui.navigate",
41
+ "intent": "Show the Perps home surface",
42
+ "next": "navigate-market",
43
+ "route": "PerpsMarketListView",
44
+ "hash": "#/?tab=perps"
45
+ },
46
+ "navigate-market": {
47
+ "action": "ui.navigate",
48
+ "intent": "Show the ETH Perps market details",
49
+ "next": "start-state",
50
+ "route": "PerpsMarketDetails",
51
+ "params": {
52
+ "market": {
53
+ "symbol": "ETH"
94
54
  }
95
55
  },
96
- "assert-clean-position": {
97
- "action": "metamask.perps.assert_positions",
98
- "market": "ETH",
56
+ "hash": "#/perps/market/ETH"
57
+ },
58
+ "start-state": {
59
+ "action": "metamask.perps.start_state",
60
+ "profile": "clean_market_testnet",
61
+ "market": "ETH",
62
+ "positions": {
99
63
  "state": "none",
100
- "intent": "Confirm ETH position state is clean",
101
- "detail": "Assert lifecycle starts with no ETH Perps position.",
102
- "flow": "perps setup",
103
- "next": "assert-clean-orders"
64
+ "mode": "matching"
104
65
  },
105
- "assert-clean-orders": {
106
- "action": "metamask.perps.assert_orders",
107
- "market": "ETH",
66
+ "orders": {
108
67
  "state": "none",
109
- "intent": "Confirm ETH order state is clean",
110
- "detail": "Assert lifecycle starts with no ETH Perps orders.",
111
- "flow": "perps setup",
112
- "next": "place-order"
113
- },
114
- "place-order": {
115
- "action": "metamask.perps.place_order",
116
- "market": "ETH",
117
- "side": "long",
118
- "amount": "11",
119
- "leverage": 3,
120
- "intent": "Open a small ETH 3x long position",
121
- "detail": "Place an approximately $11 real testnet market order through the Perps controller.",
122
- "flow": "perps lifecycle",
123
- "next": "assert-open"
124
- },
125
- "assert-open": {
126
- "action": "metamask.perps.assert_positions",
127
- "market": "ETH",
128
- "state": "open",
129
- "intent": "Confirm the ETH position opened",
130
- "detail": "Verify the lifecycle created a real position before cleanup.",
131
- "flow": "perps lifecycle",
132
- "next": "read-orders"
133
- },
134
- "read-orders": {
135
- "action": "metamask.perps.read_orders",
136
- "market": "ETH",
137
- "intent": "Read ETH orders before cleanup",
138
- "detail": "Collect order state proof before lifecycle teardown.",
139
- "flow": "perps lifecycle",
140
- "next": "close-orders"
141
- },
142
- "close-orders": {
143
- "action": "metamask.perps.close_orders",
144
- "market": "ETH",
145
- "intent": "Close ETH orders from the lifecycle",
146
- "detail": "Cancel any open ETH orders selected by the recipe parameters.",
147
- "flow": "perps teardown",
148
- "next": "close-position"
149
- },
150
- "close-position": {
151
- "action": "metamask.perps.close_positions",
152
- "market": "ETH",
153
- "intent": "Close the ETH lifecycle position",
154
- "detail": "Close the position created by this smoke and wait for clean state.",
155
- "flow": "perps teardown",
156
- "next": "assert-closed"
157
- },
158
- "assert-closed": {
159
- "action": "metamask.perps.assert_positions",
160
- "market": "ETH",
68
+ "mode": "matching"
69
+ },
70
+ "page": "market",
71
+ "intent": "Prepare clean ETH testnet Perps state",
72
+ "next": "assert-clean-position"
73
+ },
74
+ "assert-clean-position": {
75
+ "action": "metamask.perps.assert_positions",
76
+ "market": "ETH",
77
+ "state": "none",
78
+ "intent": "Confirm ETH position state is clean",
79
+ "next": "assert-clean-orders"
80
+ },
81
+ "assert-clean-orders": {
82
+ "action": "metamask.perps.assert_orders",
83
+ "market": "ETH",
84
+ "state": "none",
85
+ "intent": "Confirm ETH order state is clean",
86
+ "next": "place-order"
87
+ },
88
+ "place-order": {
89
+ "action": "metamask.perps.place_order",
90
+ "market": "ETH",
91
+ "side": "long",
92
+ "amount": "11",
93
+ "leverage": 3,
94
+ "intent": "Open a small ETH 3x long position",
95
+ "next": "assert-open"
96
+ },
97
+ "assert-open": {
98
+ "action": "metamask.perps.assert_positions",
99
+ "market": "ETH",
100
+ "state": "open",
101
+ "intent": "Confirm the ETH position opened",
102
+ "next": "read-orders"
103
+ },
104
+ "read-orders": {
105
+ "action": "metamask.perps.read_orders",
106
+ "market": "ETH",
107
+ "intent": "Read ETH orders before cleanup",
108
+ "next": "close-orders"
109
+ },
110
+ "close-orders": {
111
+ "action": "metamask.perps.close_orders",
112
+ "market": "ETH",
113
+ "intent": "Close ETH orders from the lifecycle",
114
+ "next": "close-position"
115
+ },
116
+ "close-position": {
117
+ "action": "metamask.perps.close_positions",
118
+ "market": "ETH",
119
+ "intent": "Close the ETH lifecycle position",
120
+ "next": "assert-closed"
121
+ },
122
+ "assert-closed": {
123
+ "action": "metamask.perps.assert_positions",
124
+ "market": "ETH",
125
+ "state": "none",
126
+ "intent": "Confirm ETH position cleanup completed",
127
+ "next": "teardown-state"
128
+ },
129
+ "teardown-state": {
130
+ "action": "metamask.perps.teardown_state",
131
+ "market": "ETH",
132
+ "positions": {
161
133
  "state": "none",
162
- "intent": "Confirm ETH position cleanup completed",
163
- "detail": "Assert no ETH position remains after lifecycle cleanup.",
164
- "flow": "perps teardown",
165
- "next": "teardown-state"
134
+ "mode": "matching"
166
135
  },
167
- "teardown-state": {
168
- "action": "metamask.perps.teardown_state",
169
- "market": "ETH",
170
- "positions": {
171
- "state": "none",
172
- "mode": "matching"
173
- },
174
- "orders": {
175
- "state": "none",
176
- "mode": "matching"
177
- },
178
- "page": "home",
179
- "intent": "Return Perps to globally clean home state",
180
- "detail": "Close remaining ETH positions and orders so the lifecycle leaves its selected market clean.",
181
- "flow": "perps teardown",
182
- "next": "done"
183
- },
184
- "done": {
185
- "action": "end",
186
- "status": "pass",
187
- "intent": "Finish Perps lifecycle smoke",
188
- "detail": "The recipe opened and closed a real ETH testnet position.",
189
- "flow": "complete"
190
- }
136
+ "orders": {
137
+ "state": "none",
138
+ "mode": "matching"
139
+ },
140
+ "page": "home",
141
+ "intent": "Return Perps to globally clean home state",
142
+ "next": "done"
143
+ },
144
+ "done": {
145
+ "action": "end",
146
+ "status": "pass"
191
147
  }
192
148
  }
193
149
  }